/* global describe, specify, assert, Promise */

describe('Evil promises should not be able to break invariants', function () {
  'use strict';

  specify('resolving to a promise that calls onFulfilled twice', function (done) {
    // note that we have to create a trivial subclass, as otherwise the
    // Promise.resolve(evilPromise) is just the identity function.
    // (And in fact, most native Promise implementations use a private
    // [[PromiseConstructor]] field in `Promise.resolve` which can't be
    // easily patched in an ES5 engine, so instead of
    // `Promise.resolve(evilPromise)` we'll use
    // `new Promise(function(r){r(evilPromise);})` below.)
    var EvilPromise = function (executor) {
      var self = new Promise(executor);
      Object.setPrototypeOf(self, EvilPromise.prototype);
      return self;
    };
    if (!Object.setPrototypeOf) { return done(); } // skip test if on IE < 11
    Object.setPrototypeOf(EvilPromise, Promise);
    EvilPromise.prototype = Object.create(Promise.prototype, {
      constructor: { value: EvilPromise }
    });

    var evilPromise = EvilPromise.resolve();
    evilPromise.then = function (f) {
      f(1);
      f(2);
    };

    var calledAlready = false;
    new Promise(function (r) { r(evilPromise); }).then(function (value) {
      assert.strictEqual(calledAlready, false);
      calledAlready = true;
      assert.strictEqual(value, 1);
    }).then(done, done);
  });
});
