Meteor/mocha testing promise/fiber failure

Hi guys. I’m having a problem with a mocha test. It’s recreatable very easily. Create a new meteor project (“meteor create crash2”.)
Add this describe() to main.js in the test directory:

describe("this test", function(){
  it("should work", function(done){
    let x = 0;

    function check(xc) {
        return new Promise((resolve, reject) => {
            if(x !== xc)
                throw "fail!";
                x++;
                resolve();
        });
    }

    check(0)
    .then(check(1))
        .then(check(2))
        .then(check(3))
        .then(check(4))
        .then(check(5))
        .then(check(6))
        .then(() => {console.log("We made it, x=" + x); done(); });
  });
});

Run the puppy with meteor test --driver-package meteortesting:mocha
That’s it. It crashes in the fiber code, and I can’t seem to debug into it to see why it’s crashing, and if I write a plain node/chai test project (without meteor), it runs fine.

Any ideas? Tech stuff below

Thank you,
David Logan

DAVIDs-MacBook-Pro-7:crash2 davidlogan$ meteor test --verbose --driver-package meteortesting:mocha
[[[[[ Tests ]]]]]                             

=> Started proxy.                             
Local package version is up-to-date: allow-deny@1.1.0
Local package version is up-to-date: autopublish@1.0.7
Local package version is up-to-date: autoupdate@1.6.0
Local package version is up-to-date: babel-compiler@7.4.1
Local package version is up-to-date: babel-runtime@1.4.0
Local package version is up-to-date: base64@1.0.12
Local package version is up-to-date: binary-heap@1.0.11
Local package version is up-to-date: blaze@2.3.3
Local package version is up-to-date: blaze-html-templates@1.1.2
Local package version is up-to-date: blaze-tools@1.0.10
Local package version is up-to-date: boilerplate-generator@1.6.0
Local package version is up-to-date: caching-compiler@1.2.1
Local package version is up-to-date: caching-html-compiler@1.1.3
Local package version is up-to-date: callback-hook@1.2.0
Local package version is up-to-date: check@1.3.1
Local package version is up-to-date: ddp@1.4.0
Local package version is up-to-date: ddp-client@2.3.3
Local package version is up-to-date: ddp-common@1.4.0
Local package version is up-to-date: ddp-server@2.3.0
Local package version is up-to-date: deps@1.0.12
Local package version is up-to-date: diff-sequence@1.1.1
Local package version is up-to-date: dynamic-import@0.5.1
Local package version is up-to-date: ecmascript@0.13.1
Local package version is up-to-date: ecmascript-runtime@0.7.0
Local package version is up-to-date: ecmascript-runtime-client@0.9.0
Local package version is up-to-date: ecmascript-runtime-server@0.8.0
Local package version is up-to-date: ejson@1.1.0
Local package version is up-to-date: es5-shim@4.8.0
Local package version is up-to-date: fetch@0.1.1
Local package version is up-to-date: geojson-utils@1.0.10
Local package version is up-to-date: hot-code-push@1.0.4
Local package version is up-to-date: html-tools@1.0.11
Local package version is up-to-date: htmljs@1.0.11
Local package version is up-to-date: http@1.4.2
Local package version is up-to-date: id-map@1.1.0
Local package version is up-to-date: insecure@1.0.7
Local package version is up-to-date: inter-process-messaging@0.1.0
Local package version is up-to-date: jquery@1.11.11
Local package version is up-to-date: launch-screen@1.1.1
Local package version is up-to-date: livedata@1.0.18
Local package version is up-to-date: lmieulet:meteor-coverage@1.1.4
Local package version is up-to-date: logging@1.1.20
Local package version is up-to-date: meteor@1.9.3
Local package version is up-to-date: meteor-base@1.4.0
Local package version is up-to-date: meteorhacks:picker@1.0.3
Local package version is up-to-date: meteortesting:browser-tests@1.0.0
Local package version is up-to-date: meteortesting:mocha@1.1.3
Local package version is up-to-date: meteortesting:mocha-core@1.0.1
Local package version is up-to-date: minifier-css@1.4.3
Local package version is up-to-date: minifier-js@2.5.1
Local package version is up-to-date: minimongo@1.4.5
Local package version is up-to-date: mobile-experience@1.0.5
Local package version is up-to-date: mobile-status-bar@1.0.14
Local package version is up-to-date: modern-browsers@0.1.4
Local package version is up-to-date: modules@0.14.0
Local package version is up-to-date: modules-runtime@0.11.0
Local package version is up-to-date: mongo@1.7.0
Local package version is up-to-date: mongo-decimal@0.1.1
Local package version is up-to-date: mongo-dev-server@1.1.0
Local package version is up-to-date: mongo-id@1.0.7
Local package version is up-to-date: npm-mongo@3.2.0
Local package version is up-to-date: observe-sequence@1.0.16
Local package version is up-to-date: ordered-dict@1.1.0
Local package version is up-to-date: promise@0.11.2
Local package version is up-to-date: random@1.1.0
Local package version is up-to-date: reactive-var@1.0.11
Local package version is up-to-date: reload@1.3.0
Local package version is up-to-date: retry@1.1.0
Local package version is up-to-date: routepolicy@1.1.0
Local package version is up-to-date: shell-server@0.4.0
Local package version is up-to-date: socket-stream-client@0.2.2
Local package version is up-to-date: spacebars@1.0.15
Local package version is up-to-date: spacebars-compiler@1.1.3
Local package version is up-to-date: standard-minifier-css@1.5.4
Local package version is up-to-date: standard-minifier-js@2.5.2
Local package version is up-to-date: templating@1.3.2
Local package version is up-to-date: templating-compiler@1.3.3
Local package version is up-to-date: templating-runtime@1.3.2
Local package version is up-to-date: templating-tools@1.1.2
Local package version is up-to-date: tracker@1.2.0
Local package version is up-to-date: typescript@3.7.0
Local package version is up-to-date: ui@1.0.13
Local package version is up-to-date: underscore@1.0.10
Local package version is up-to-date: url@1.2.0
Local package version is up-to-date: webapp@1.7.5
Local package version is up-to-date: webapp-hashing@1.0.9
=> Started MongoDB.                           
I20191130-12:53:30.385(-7)?                   
I20191130-12:53:30.424(-7)? --------------------------------
I20191130-12:53:30.424(-7)? ----- RUNNING SERVER TESTS -----
I20191130-12:53:30.424(-7)? --------------------------------
I20191130-12:53:30.424(-7)? 
I20191130-12:53:30.424(-7)? 
I20191130-12:53:30.424(-7)? 
I20191130-12:53:30.425(-7)?   crash2
=> Started your app.

=> App running at: http://localhost:3000/
    ✓ package.json has correct name
    ✓ server is not client? 
I20191130-12:53:30.426(-7)? 
I20191130-12:53:30.426(-7)?   this test
W20191130-12:53:30.426(-7)? (STDERR) (node:97801) UnhandledPromiseRejectionWarning: AssertionError [ERR_ASSERTION]: 'object' === 'function'
W20191130-12:53:30.427(-7)? (STDERR)     at FiberPool.run (/Users/davidlogan/.meteor/packages/promise/.0.11.2.1dhxp4c.8hddl++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/meteor-promise/fiber_pool.js:81:12)
W20191130-12:53:30.428(-7)? (STDERR)     at result (/Users/davidlogan/.meteor/packages/promise/.0.11.2.1dhxp4c.8hddl++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/meteor-promise/promise_server.js:157:29)
W20191130-12:53:30.428(-7)? (STDERR)     at <anonymous>
W20191130-12:53:30.428(-7)? (STDERR) (node:97801) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
W20191130-12:53:30.428(-7)? (STDERR) (node:97801) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
    1) should work.422(-7)? 
I20191130-12:53:32.422(-7)? 
I20191130-12:53:32.422(-7)? 
I20191130-12:53:32.422(-7)?   2 passing (2s)
I20191130-12:53:32.422(-7)?   1 failing
I20191130-12:53:32.423(-7)? 
I20191130-12:53:32.423(-7)?   1) this test should work:
I20191130-12:53:32.423(-7)?      Error: timeout of 2000ms exceeded. Ensure the done() callback is being called in this test.
I20191130-12:53:32.423(-7)?   
I20191130-12:53:32.423(-7)? 
I20191130-12:53:32.423(-7)? 
I20191130-12:53:32.423(-7)? 
I20191130-12:53:32.423(-7)? Load the app in a browser to run client tests, or set the TEST_BROWSER_DRIVER environment variable. See https://github.com/meteortesting/meteor-mocha/blob/master/README.md#run-app-tests
1 Like

The issue here is that .then takes a function to run when the promise resolves, not a promise. The assertion error shows that it is looking for a function, but has received an object (the Promise)

so you want to change this:

    check(0)
    .then(check(1))
        .then(check(2))
        .then(check(3))
        .then(check(4))
        .then(check(5))
        .then(check(6))
        .then(() => {console.log("We made it, x=" + x); done(); });

into this:

check(0)
  .then(() => check(1))
  .then(() => check(2))
  .then(() => check(3))
  .then(() => check(4))
  .then(() => check(5))
  .then(() => check(6))
  .then(() => {
    console.log("We made it, x=" + x);
    done();
  });

Alternatively, if check returns a function, then we can use it like the original example:

    function check(xc) {
      return function () {
        return new Promise((resolve, reject) => {
          if(x !== xc)
            throw "fail!";
          x++;
          resolve();
        });
      }
    }

except that you’ll need to execute the first function:

check(0)() // <-- extra function call
  .then(check(1))
  .then(check(2))

And as an extra note, normally you should reject instead of throw inside promise code, just to keep things consistent (and save you from scope errors if you have callbacks)

1 Like

Apologies, sometimes it’s hard to see the forest for the trees! Thank for the help.

The stack traces are much more cryptic when they’ve gone through both mocha and Meteor, so I don’t blame you!

Yes, a variety of circumstances contributed to my confusion. It did not crash in a straight node app as well. Had the straight node program crashed, I would have figured it out right away.

Thanks again. That problem is solved. Now I just have some horrible issue with PublicationCollector I have to resolve :slight_smile:

David Logan

You’ve got me curious, so I went looking and found that the ECMAScript spec says to check if the argument passed to then is callable (ie. a function) and if it’s not, it gets replaced with an identity function (input) => input. (I also found that the Promise spec is quite annoying, taking multiple steps to get to this point)

MDN explains it best:

onFulfilled Optional

A Function called if the Promise is fulfilled. This function has one argument, the fulfillment value . If it is not a function, it is internally replaced with an “Identity” function (it returns the received argument).

But because Meteor has had to create it’s own promise abstraction to co-exist with fibers, it handles input a little bit differently:


Considering that passing a non-function argument to then has no purpose, and is usually an accident, I’m okay with the trade-off of Meteor’s Promises not being fully spec compliant