Mocha test times out when using promises and skips over code, why? - javascript

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?

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.

Related

Promises structure misunderstood

I have a problem with understanding Promises syntax.
So, what I am trying to do:
getPosts() gets some data from a DB then, I want to get some metadata for each row with another promise call, addMetadata(). Then, once all the metadata is fetched, I want to console it out.
See my attempt below:
var getPosts = function(){
return new Promise(function(resolve, reject){
postModel.find()
.exec(function(err, posts) {
resolve(posts);
);
});
};
var addMetadata = function(posts){
var options = {
host: 'localhost',
port: 3000,
path: '',
method: 'GET'
};
var postPromises = posts.map(function(post) {
return new Promise(function(resolve) {
options.path = '/api/user?id=' + post.profileID;
var req = http.get(options, function(res) {
var bodyChunks = [];
res.on('data', function(chunk) {
bodyChunks.push(chunk);
}).on('end', function() {
var body = Buffer.concat(bodyChunks);
var parsedBody = JSON.parse(body);
post.fullname = parsedBody.user.fullname;
post.profilePic = parsedBody.user.profilePic;
// resolve the promise with the updated post
resolve(post);
});
});
});
});
// Is this the right place to put Promise.all???
Promise.all(postPromises)
.then(function(posts) {
//What should I put here
});
};
getPosts()
.then(function(posts){
return addMetadata(posts);
})
.then(function(posts){//I get a little lost here
console.log();//posts is undefined
});
Of course, my understanding is wrong but I thought I was going the right way. Can someone please guide me to the right direction?
Thanks
Change
// Is this the right place to put Promise.all???
Promise.all(postPromises)
.then(function (posts) {
//What should I put here
});
into
// Is this the right place to put Promise.all???
return Promise.all(postPromises);
This way your addMetadata function will return Promise that resolve when all promises from postPromises resolves or reject if any of postPromises rejects.
The key point to understand the async concept of it and what time the content is available.
Reading this will help to put you in the right direction.
For instance:
var promise = new Promise(function(resolve, reject) {
resolve(1);
});
promise
.then(function(val) {
console.log(val); // 1
return val + 2;
})
.then(function(val) {
console.log(val); // 3
})
After as per your scenario, in order to have all the metadata Promise.all is the way to go.
Promise.all(arrayOfPromises).then(function(arrayOfResults) {
// One result per each promise of AddMetadata
})
What you wanna do here, if I am correct, is called streams, as you wanna call multiple paralel promises as your concept of looping through list of posts using map is not going to work this way
Take a look at this short video introducing streams Streams - FunFunFunction, he is using library for workin with streams called Baconjs
Here is a short example on streams
const stupidNumberStream = {
each: (callback) => {
setTimeout( () => callback(1), 3000 )
setTimeout( () => callback(2), 2000 )
setTimeout( () => callback(3), 1000 )
}
}
stupidNumberStream.each(console.log)
Your getPosts function is good in the sense that its only job is to promisfy the database find. (Though, I think if it's mongo, the exec produces a promise for you).
Your addMetadataToAPost is less good, because it mixes up processing an array of posts and "promisifying" the http.get. Use the same pattern you applied correctly in the first function and return a promise to do a single get and add metadata. (It would be even better to just wrap the get, which you can reuse, and build a simple add-metadata function that returns - rather than creates - a promise)
// renamed pedantically
var addMetadataToAPost = function(post) {
return new Promise(function(resolve) {
options.path = '/api/user?id=' + post.profileID;
var req = http.get(options, function(res) {
var bodyChunks = [];
res.on('data', function(chunk) {
bodyChunks.push(chunk);
}).on('end', function() {
var body = Buffer.concat(bodyChunks);
var parsedBody = JSON.parse(body);
post.fullname = parsedBody.user.fullname;
post.profilePic = parsedBody.user.profilePic;
// resolve the promise with the updated post
resolve(post);
});
});
});
}
Now your batching function is simple:
// also renamed pedantically
var addMetadataToAllPosts = function(posts){
var postPromises = posts.map(function(post) {
return addMetadataToAPost(post)
})
return Promise.all(postPromises)
};
Your original code should work...
getPosts().then(function(posts){
return addMetadataToAllPosts(posts);
})
.then(function(posts){
console.log(posts);//posts should be an array of posts with metadata added
});

Nested promises - Mocha - Exceeded timeout

I have got a test failing because of a timeout using Mocha.
I do call the "done()" function but it does not seem to work for some reason.
My test currently looks like this:
var express = require('express');
var expect = require('chai').expect;
var mocha = require('mocha');
var calendar = require('./../Server/calendarDatabase');
describe("Calendar", function () {
describe("Database", function () {
it("should get stuff from the database", function (done) {
return calendar.Connect()
.then(function () {
return calendar.getAll();
})
.then(function (returnValue) {
expect(returnValue.count).to.equal(5); //This should fail, my database records are 20+
done();
});
});
});
});
Where my calendar.Connect() and calendar.getAll() are both promises:
var express = require('express');
var sql = require('mssql');
var config = require('./../config');
var calendarDbConnection = {};
calendarDbConnection.Connect = function() {
return sql.connect(config.mssql);
}
calendarDbConnection.getAll = function () {
var promise = new sql.Request()
.query('select * from CalendarTable')
.catch(function (err) {
console.log(err.message);
});
return promise;
}
module.exports = calendarDbConnection;
However while running my test, I get following output:
When I call the done() after my last then(), the function gets resolved but the outcome of my test does not. The number of lines I get from the database are over 20 and I check if they are equal to 5. So, my test should fail, but it does not.
//previous code
.then(function (returnValue) {
expect(returnValue.count).to.equal(5); //This should fail, my database records are 20+
});
done();
//...
So this last outcome passes my test, while it should not.
What am I missing here? I am calling the callback function but then my expected outcome is not correct.
Since You are returning a Promise from the test, you should not pass done as an argument:
Alternately, instead of using the done() callback, you may return a Promise. This is useful if the APIs you are testing return promises instead of taking callbacks.
Although you can pass done to catch call as stated above, it seems more convenient to get rid of done and just return a promise.
it("should get stuff from the database", function () {
return calendar.Connect() // returns Promise, so no `done`
.then(function () {
return calendar.getAll();
})
.then(function (returnValue) {
expect(returnValue.count).to.equal(5);
});
});
Only pass done in the catch.
then(....) { ... done(); }.catch(done);

How to use async mocha before() initialization without setTimeout?

I am using Mocha to test out some database queries I created. I need my before block to create the appropriate foreign keys so the unit tests can focus on testing out the raw create/delete functionality of my Node ORM.
My problem is that I am inserting db entries in my before block and the unit tests are running before the before block is finished executing.
I read that promises should be used to deal with this kind of thing, so I refactored my codebase to use promises but I still can't get around the fact that I need a setTimeout.
How can I perform async before block without needing to wrap my first it block in a setTimeout?
var chai = require('chai');
var expect = chai.expect;
var db = require('../server/db/config');
var models = require('../server/db/models');
describe('scoring', function() {
var testMessage = {
x: 37.783599,
y: -122.408974,
z: 69,
message: 'Brooks was here'
};
var messageId = 1;
before(function() {
var token = '' + Math.random();
models.createUser(token).then(function() {
testMessage.userToken = token;
models.insert(testMessage)
});
});
it('should have votes created in db when createVote is called', function(done) {
setTimeout(function(done) {
models.createVote(messageId, token, function(err, res) {
expect(res.insertId).to.be.a('number');
done();
});
}.bind(this, done), 1000);
});
});
You could do it like joews suggests. However, Mocha supports using promises for synchronization. You have to return your promise:
before(function() {
var token = '' + Math.random();
return models.createUser(token).then(function() {
testMessage.userToken = token;
models.insert(testMessage)
});
});
Mocha won't continue to the test until it is able to execute .then on the promise returned by your code.
The function you pass to before, like the other Mocha API methods, can accept a done callback. You should call this when your before actions have completed:
before(function(done) {
var token = '' + Math.random();
models.createUser(token).then(function() {
testMessage.userToken = token;
return models.insert(testMessage)
}).then(function() {
done();
})
});
Mocha docs - asynchronous code

synchronous hashing function for files

I have a function which generates a checksum for a given path
function getHash(path) {
var fs = require('fs');
var crypto = require('crypto');
var fd = fs.createReadStream(path);
var hash = crypto.createHash('sha1');
hash.setEncoding('hex');
fd.on('end', function () {
hash.end();
// *** Here is my problem ***
console.log(hash.read());
});
fd.pipe(hash);
};
I want to call the calcNewHash function so that it returns the hash, the problem is, that I haven't found an asynchronous version of this, maybe some one can help.
Just add a return statement doesn't work, because the function is in a Listener
Later I want to add the checksum to an object, so I could give the reference to it as parameter, but this still does not change that this is ansynchronous ...
You basically have 2 solutions:
#1 Work with promises (i.e. q - npm install q --save)
function getHash(path) {
var Q = require('q');
var fs = require('fs');
var crypto = require('crypto');
var deferred = Q.defer();
var fd = fs.createReadStream(path);
var hash = crypto.createHash('sha1');
hash.setEncoding('hex');
fd.on('end', function () {
hash.end();
// *** Here is my problem ***
console.log(hash.read());
deferred.resolve(hash.read());
});
fd.pipe(hash);
return deferred.promise;
};
getHash('c:\\')
.then(function(result) {
// do something with the hash result
});
#2 Or use a callback function
function getHash(path, cb) {
var fs = require('fs');
var crypto = require('crypto');
var fd = fs.createReadStream(path);
var hash = crypto.createHash('sha1');
hash.setEncoding('hex');
fd.on('end', function () {
hash.end();
// *** Here is my problem ***
console.log(hash.read());
if (cb) {
cb(null, hash.read());
}
});
fd.pipe(hash);
};
getHash('C:\\', function (error, data) {
if (!error) {
// do something with data
}
});
If you don't have deep nesting in callback functions I would go for option #2.
(Note: behind the scenes promises are also using callbacks, it's just a 'cleaner' solution if you have deep nesting)
I know this is old but it is in the top results when searching for something along these lines. So for those that land here looking for a solution to this, here you go: (note that this only works well if you know the file is small. Otherwise for larger files refer to the answer provided by Dieterg)
const fs = require('fs');
const crypto = require('crypto');
function fileHashSync(filePath){
var fileData;
try{ fileData = fs.readFileSync(filePath, 'utf8'); }
catch(err){
if(err.code === 'ENOENT') return console.error('File does not exist. Error: ', err);
return console.error('Error: ', err);
}
return crypto.createHash('sha1').update(fileData, 'utf8').digest('hex');
}

How does one stub promise with sinon?

I have a data service with following function
function getInsureds(searchCriteria) {
var deferred = $q.defer();
insuredsSearch.get(searchCriteria,
function (insureds) {
deferred.resolve(insureds);
},
function (response) {
deferred.reject(response);
});
return deferred.promise;
}
I want to test following function:
function search ()
{
dataService
.getInsureds(vm.searchCriteria)
.then(function (response) {
vm.searchCompleted = true;
if (response.insureds.length > 100) {
vm.searchResults = response.insureds.slice(0, 99);
} else {
vm.searchResults = response.insureds;
}
});
}
How would I stub the promise so that when I call getInsureds it would resolve the promise and return me the results immediately. I started like this (jasmine test), but I am stuck, as I don't know how to resolve the promise and pass in arguments needed.
it("search returns over 100 results searchResults should contain only 100 records ", function () {
var results103 = new Array();
for (var i = 0; i < 103; i++) {
results103.push(i);
}
var fakeSearchForm = { $valid: true };
var isSearchValidStub = sinon.stub(sut, "isSearchCriteriaValid").returns(true);
var deferred = $q.defer();
var promise = deferred.promise;
var dsStub = sinon.stub(inSearchDataSvc, "getInsureds").returns(promise);
var resolveStub = sinon.stub(deferred, "resolve");
//how do i call resolve and pass in results103
sut.performSearch(fakeSearchForm);
sinon.assert.calledOnce(isSearchValidStub);
sinon.assert.calledOnce(dsStub);
sinon.assert.called(resolveStub);
expect(sut.searchResults.length).toBe(100);
});
At current sinon version v2.3.1, you can use stub.resolves(value) and stub.rejects(value) function
For example, you can stub myClass.myFunction with following code
sinon.stub(myClass, 'myFunction').resolves('the value you want to return');
or
sinon.stub(myClass, 'myFunction').rejects('the error information you want to return');
You just have to resolve the promise before you call the search function. This way your stub will return a resolved promise and then will be called immediately. So instead of
var resolveStub = sinon.stub(deferred, "resolve");
you will resolve the deferred with your fake response data
deferred.resolve({insureds: results103})
Also you can do something like this:
import sinon from 'sinon';
const sandbox = sinon.sandbox.create();
const promiseResolved = () => sandbox.stub().returns(Promise.resolve('resolved'));
const promiseRejected = () => sandbox.stub().returns(Promise.reject('rejected'));
const x = (promise) => {
return promise()
.then((result) => console.log('result', result))
.catch((error) => console.log('error', error))
}
x(promiseResolved); // resolved
x(promiseRejected); // rejected
I had a similar situation and the accepted answer and comments were not working, but along with this question they helped me solve this in the following way. I hope it is helpful for somebody.
var Promise = require('bluebird');
var deferred = Promise.defer();
stub = sinon.stub(deferred, 'resolve').returns(deferred.promise);
deferred.resolve({data: data});
// or
deferred.reject(new Error('fake error'));
There's one more alternative I found. Much pain free than other methods.
You can use this npm package: sinon-stub-promise.
It abstracts much of the details, so that you don't have to invent the wheel again. Helped my write my tests after struggling to simulate a promise for long.
Hope it helps!

Categories

Resources