1

I have been trying to run this test for 2 days straight now and I can't figure what is wrong with it:

/*eslint-env mocha */
// var expect = require('chai').expect;
var chai = require('chai');
var chaiAsPromised = require("chai-as-promised");
var expect = chai.expect;
var Promise = require('bluebird');
var Archive = require('../lib/archive');
var path = require('path');
var fs = Promise.promisifyAll(require('fs-extra'));
var globAsync = Promise.promisify(require('glob'));
var tar = require('tar-fs');
var zlib = Promise.promisifyAll(require('zlib'));

chai.use(chaiAsPromised);

describe('Archive', function() {
  var pkg;
  var archive_location;
  var subject;

  beforeEach(function() {
    pkg = {
      name: 'test_0790feebb1',
      recipient_name: 'Test',
      files: {
        letter: '../documents/letters/test/letter.tex',
        resume: '../documents/cover/cover.tex'
      },
      compiled_files: {
        package: '../documents/letters/test/test.pdf'
      }
    };
    archive_location = path.resolve('archives/test_0790feebb1.tar.gz'); 

    subject = new Archive(pkg);
  });

  after(function() {
    return globAsync('archives/test*')
      .each(function(filename) {
        return fs.removeAsync(filename);
      });
  });

  describe('#make', function() {
    it('has the correct directory structure', function() {
      // debugger;
      var tmp_extract_path = path.resolve('test/.tmp');
      var tarPromise = function(data) {
        console.log('tarP'); // never run
        return new Promise(function(reject, resolve) {
          data
            .pipe(zlib.Unzip())
            .pipe(tar.extract(tmp_extract_path))
            .on('error', reject)
            .on('end', resolve);
        });
      };

      var verifyDir = function() {
        console.log('verD'); // never run
        return Promise.all([
            'code',
            'pdf',
            'code/repo',
            'code/documents',
            'code/documents/letters',
            'code/documents/letters/test',
            'code/documents/letters/shared',
            'code/documents/cover',
            'code/documents/letters'
        ].map(function(subpath) {
          return fs.statAsync(path.resolve(tmp_extract_path, subpath));
        }));
      };

      return fs.createReadStreamAsync(archive_location)
        .then(function(data) { return tarPromise(data); })
        .then(function() { return verifyDir(); })
        .then(function(files) {
          console.log(files); // never run
          return expect(true).to.be.true;
        })
        .catch(function(e) { console.log(e); });
    });
  });
});

The various console.log never even get executed and eventually the test times out without any error or stack trace.

I have no idea what I am doing wrong and promises hurt my brain now. When I run the code with node inspector and uncomment the breakpoint, I can see that he value of this._runnable._trace is "done() called multiple times". I have no idea if this is an actual error nor why it doesn't throw an exception if that's an error. I can't explain either why this is even happening since I am not using any done() callbacks anymore with promises and that my test starts with function() and not function(done) like an async test would

Any ideas?

springloaded
  • 1,079
  • 2
  • 13
  • 23
  • two issues I see are: the function `tarPromise` returns undefined, not promise, and `verifyDir` is not called in the chain, just passed on. Also, adding a catch block to the promise chain might help identify the issue... – mido Oct 01 '15 at 01:55
  • My bad, I made typos when I formatted the code for SO. I edited the question to correct the mistakes. I also added a catch block but it is never printing anything when I run the code. – springloaded Oct 01 '15 at 02:09
  • 1
    proabably [this issue](https://github.com/petkaantonov/bluebird/issues/225)? – mido Oct 01 '15 at 03:11
  • @mido22 Yes it was this issue. I wasn't aware of it. Thanks – springloaded Oct 02 '15 at 13:46

1 Answers1

2

Your problem is that fs.createReadStream is synchronous and returns a ReadableStream that reads data into memory in chunks. PromisifyAll converts callbacks to asynchronous functions into promises, not synchronous functions into asynchronous.

Probably what you want is to use fs.readFileAsync:

var Promise = require('bluebird');
var fs = Promise.promisifyAll(require('fs'));
var path = require('path')
var archive_location = path.resolve('example.zip');
var assert = require('assert');

describe('This', function(){
    it('should work', function(){
       return fs.readFileAsync(archive_location).
           then(function(data) {
                assert.notEqual(data.length, data.length);
                return data;
           }).
           catch(console.log);
    });
});

I have the assertion set to a failing assertion to demonstrate that this actually hits the promise chain.

Joseph Yaduvanshi
  • 20,241
  • 5
  • 61
  • 69
  • Thanks, based on your answer, I ended up moving the fs.createReadStream within tarPromise and that solved the issue. I expected createReadStreamAsync to pass a stream on to the next then() but that wasn't the case. Your solution works if one uses buffers, I wanted to stick to streams because my tar.extract works with streams. – springloaded Oct 02 '15 at 13:43
  • In case you ever encounter that problem again, you can wrap a buffer with a stream. see http://stackoverflow.com/a/16044400/151445 This doesn't avoid the issue of reading the entire file into an in-memory buffer, but it does decorate buffers to be used by your tar.extract. If your library is public, it may even be nice to expose this for users. – Joseph Yaduvanshi Oct 02 '15 at 16:38