Callbacks in Laika tests are not called - javascript

Meteor.collection.insert() accepts callback as an argument. As an example, one can create a brand new Meteor project and run the following code in the browser's console.
my_collection = new Meteor.Collection("myCollection");
my_collection.insert(
{some: "object"},
function() {
console.log("finished insertion");
})
When I take this same code and put it in a Laika test, the callback argument never gets called. Here is my test code:
suite('testing Laika out', function() {
test('inserting into collection', function(done, server, client) {
client.eval(function() {
my_collection = new Meteor.Collection("myCollection");
my_collection.insert(
{some: "object"},
function() {
console.log("finished insertion");
done();
})
})
})
})
Anyone know why the callback function isn't called in this Laika test? This seems to be an issue for more than just Meteor.collection.insert().
(I'm running Ubuntu 13.04, Meteor 0.7.0.1, Laika 0.3.1, PhantomJS 1.9.2-6)

The problem is that you're trying to call done(); inside of your insert callback, when it doesn't exist in that function scope. You actually need to listen for the insertion into my_collection and emit a signal which is picked up by either the client or server (the client in your case). Also, you obviously won't be initializing your collection in your test; that should be done in your production code.
Try this instead:
var assert = require("assert");
suite('testing Laika out', function() {
test('inserting into collection', function(done, server, client) {
client.eval(function() {
addedNew = function(newItem) {
console.log("finished insertion");
emit("done", newItem)
};
my_collection = new Meteor.Collection("myCollection");
my_collection.find().observe({
added: addedNew
});
my_collection.insert(
{some: "object"}
)
}).once("done", function(item) {
assert.equal(item.some, "object");
done();
});
});
})
Check out https://github.com/arunoda/hello-laika for the basic examples for testing.

Well, Mr. jonS90, if you were to run Laika with the --verbose flag, you would notice that an exception is quietly being thrown:
[client log] Exception in delivering result of invoking '/myCollection/insert': ReferenceError: Can't find variable: done
You see, you don't have access to done() in that context. Here's how you should revise your code:
test('inserting into collection', function(done, server, client) {
client.eval(function() {
my_collection = new Meteor.Collection("myCollection");
finishedInsertion = function () {
console.log("finished insertion");
emit('done')
}
my_collection.insert(
{some: "object"},
finishedInsertion)
})
client.once('done', function() {
done();
})
})

Related

Async Issue using NPM Package and Meteor.wrapAsync

When using a function client.call from an npm module (node-celery), it appears that the callback function of client.call is not executed.
Meteor.methods({
'estimates.request'(data) {
var celery = require('node-celery')
var client = celery.createClient({...})
client.on('connect', function() {
console.log('connected'); // this is executed
client.call('proj.tasks.register', [name],
function(err, result) {
console.log('result: ', result); // this is not executed
client.end();
return result;
}
);
});
}
});
Tried wrapping client.call in Meteor.wrapAsync:
callFunc = client.on('connect', function() {
console.log('connected'); // this is executed
client.call('proj.tasks.register', [name],
function(err, result) {
console.log('result: ', result); // this is not executed
client.end();
return result;
}
);
});
callFuncSync = Meteor.wrapAsync(callFunc)
callFuncSync()
but this throws an error in the Meteor server console:
err: [Error: Meteor code must always run within a Fiber. Try wrapping callbacks that you pass to non-Meteor libraries with Meteor.bindEnvironment.]
err: { [Error: read ECONNRESET] code: 'ECONNRESET', errno: 'ECONNRESET', syscall: 'read' }
Question: How should we use Meteor.bindEnvironment to fix this issue?
From the docs,
Wrap a function that takes a callback function as its final parameter. The signature of the callback of the wrapped function should be function(error, result){}
Your code simply wraps the return value of the event attachment call.
You could wrap the whole thing (connection + task calls), but in your case, I would suggest a different approach:
Right now, you are connecting to Celery every time someone is calling the method. I would advise having a persistent connection to Celery, if possible.
The functions that you are trying to wrap do not adhere to the wrapAsync requirements, so you would have to wrap them in a function that does.
In the following code, both the connection and call functions are taken care of. Note that those functions take a cb argument, which will be provided to them by Meteor, and call it with an error and/or result, as appropriate.
those functions are then passed to wrapAsync.
The sync version of them throws if an error is passed to the callback and simulate a synchronous run using a fiber if they are called in a synchronous manner (i.e, no callback is passed). That's the reason for the try..catch block.
import { Meteor } from 'meteor/meteor';
const celery = require('node-celery'); // or `import` it
function createAndConnectAsync(details, cb) {
const client = celery.createClient(details);
const errHandler = function(e) {
cb(e, client);
};
client.once('connect', function() {
client.off('error', errHandler);
cb(null, client); // success
});
client.once('error', errHandler);
}
function callAsync(client, task, args, cb) {
client.call(task, args, function(result) {
cb(null, result);
});
}
const createAndConnect = Meteor.wrapAsync(createAndConnectAsync);
const call = Meteor.wrapAsync(callAsync);
let client;
try {
client = createAndConnect({...}); // returns after the client is connected
} catch(e) {
// connection error
}
Meteor.methods({
'estimates.request'(data) {
// generate `name`
const result = call(client, 'proj.tasks.register', [name]);
return result;
}
});
Combinding async libraries can be tricky, usually they have some helper methods like Meteor's bindEnvironment.
The function returned from Meteor.bindEnvironment also automatically gets run in a Fiber.
Meteor.methods({
'estimates.request'(data) {
var celery = require('node-celery')
var client = celery.createClient({...})
var otherCallback = Meteor.bindEnvironment(function(err, result) {
console.log('result: ', result); // this is not executed
client.end();
return result;
});
var connectCallback = Meteor.bindEnvironment(function() {
console.log('connected'); // this is executed
client.call('proj.tasks.register', [name], otherCallback);
});
client.on('connect', connectCallback);
}
});
FYI: didn't test this because your example isn't 100% complete :)

Export a variable which is a result of an asynchronous function call

I'm creating an app that will use the https://github.com/vpulim/node-soap to communicate with a soap server.
I would like to create a client component which I will forward the necessary methods to the soap-client created with this module.
I'm having trouble to return an object that will use this client, since the client is created asynchronously.
var soap = require('soap');
var url = 'http://someurl?wsdl';
soap.createClient(url, function(err, client) {
if (err) {
console.log(err);
return;
}
console.log(client.describe());
// I need to publish this client so that other functions in this file will be able to use it
});
module.exports = {
doSomething: function() {
//how can I access the client here?
}
};
How would I go about doing this?
One solution to this problem is to use promises:
var soap = require('soap');
var url = 'http://someurl?wsdl';
var clientPromise = new Promise(function(resolve, reject) {
soap.createClient(url, function(err, client) {
if (err) {
// reject the promise when an error occurs
reject(err);
return;
}
// resolve the promise with the client when it's ready
resolve(client);
});
});
module.exports = {
doSomething: function() {
// promise will wait asynchronously until client is ready
// then call the .then() callback with the resolved value (client)
return clientPromise.then(function(client) {
// do something with client here
}).catch(function(err) {
// handle errors here
console.error(err);
});
}
};
A few advantages to this:
Promises are native JavaScript objects (as of Node 4.0.0, with packages such as bluebird providing support for prior versions)
Promises can be "reused": if clientPromise has already resolved once, it will immediately resolve when doSomething is later called.
Some disadvantages:
doSomething and all other exported functions are inherently asynchronous.
Is not directly compatible with Node-type callbacks.
Not sure if my response helps you, but this is how I do. I create createClient every time and then within the client, I call the actual SOAP method (here GetAccumulators). May be not a great way, but this works for me. Here is my code sample
soap.createClient(url, function (err, client) {
if (err) {
logger.error(err, 'Error creating SOAP client for %s', tranId);
reject('Could not create SOAP client');
}
client.addSoapHeader(headers);
// envelope stuff
client.wsdl.definitions.xmlns.soapenv = 'http://schemas.xmlsoap.org/soap/envelope/';
client.wsdl.definitions.xmlns.acc = 'http://exampleurl/';
client.wsdl.xmlnsInEnvelope = client.wsdl._xmlnsMap();
client.GetAccumulators(args, function (err, result) {
if (err) {
if (isNotFoundError(err)) {
logger.debug('Member not found for tranId %s', tranId);
reject({status:404, description:'No match found'});
}
reject({status:500, description:'GetAccumulators error'});
}
return resolve({data: result, tranId: tranId});
});

Understanding Scopes in Spooky JS

This implementation of SpookyJS is really spooky. While using Gulp to run Mocha + SpookyJS tests, I am unable to see most console log output. I have been following the Quickstart steps on SpookyJS's github page. Why can't I see these console log outputs?
describe('test', function () {
it('test 1', function(done){
try {
var Spooky = require('spooky');
} catch (e) {
var Spooky = require('../lib/spooky');
}
var spooky = new Spooky({
child: {
transport: 'http'
},
casper: {
logLevel: 'debug',
verbose: true
}
}, function (err) {
if (err) {
e = new Error('Failed to initialize SpookyJS');
e.details = err;
throw e;
}
spooky.start(URL);
console.log('Hello 3'); //currently this is not printing anything to the console
spooky.then(function () {
this.emit('hello', 'Hello, from ' + this.evaluate(function () {
return document.title;
}));
});
spooky.run();
console.log('Hello 1'); //currently this is not printing anything to the console
});
spooky.on('hello', function (line) {
console.log(line);
});
spooky.on('console', function (line) {
console.log(line);
});
spooky.on('error', function (e, stack) {
console.error(e);
if (stack) {
console.log(stack);
}
});
console.log('Hello 2'); //this is printing to the console
done();
});
});
Simplifying your code, it roughly looks like this:
describe('test', function () {
it('test 1', function(done){
var Spooky = require('spooky');
var spooky = create a spooky object with a lot of logic in it;
console.log('Hello 2'); //this is printing to the console
done();
});
});
The spooky object is created, and after that, the program flow simply continues. Anything that the spooky object does, it does asynchronously, later on.
After creating the spooky object, you get your Hello 2 and then done() is called, which ends this test.
So, before any hello stuff can be processed, your tests ends already.
What might help is to move the done() line here:
spooky.on('hello', function (line) {
console.log(line);
done();
});
Then your test will end once the hello event has been caught.
Since I'm not familiar with Spooky or with what exactly you want to test, I'm afraid I can't be of any more help. Hopefully this gets you a step further.

Mocha js Calling After() too Soon?

New to Mocha unit testing, I have a few Mocha examples that are running fine, but I have been trying for hours to get this one to run and no matter what I do, after() is called way earlier than I feel it should. Here's an example:
var dummyData = require('./dummyData.js')
describe('mochaTest', function() {
after(function(done) {
dummyData.cleanDb(function(){
done();
})
});
it('should hit the db to get dummy data and send', function(done) {
dummyData.createDummyData(function(data1, data2, Lookup) {
Lookup.lookup({
data1: data1,
data2: data2
}, function(err, result) {
done();
});
});
});
})
And then in dummyData.js:
exports.createDummyData = function(cb){
doSomeStuff(function (err, patient) {
// Connect to db, get some data to pass.
var Lookup = require(./Lookup.js);
cb(data1, data2, Lookup);
})
}
exports.cleanDb = function(cb) {
// Clear db connections.
cb();
}
The problem is that right after the test is run, the after() function gets called and the Lookup function can't hit the db, because the db connection has been cleared. Why is after being called so early, it shouldn't be called until the it statement calls done() right?
This is an old question, but I have experienced the same issue and could not find any explanation to solve this. Unfortunately I do not have enough reputation to answer as a comment, so I'll share how I solved my issue here.
In my controller, I had a method outlined as follows:
exports.insert = (request, response) => {
UserModel.createUser(request.body)
.then(() => {
respond.status(201).send({message: 'User created successfully
});
};
I realized the issue here, was that my test method wasn't waiting for a response from my User.insert(), because this function is void -- it does not return a value. So the following test would jump straight to calling done(); since there was no reason to wait for a response from User.insert(). This was causing my after hook to run prematurely and close the connection to my database before my UserModel could populate.
// Code is shortened, but my request and response was formed using node-mocks-http package
it('should return status 201', function (done) {
User.insert(request, response);
assert(response._getStatusCode() === 201);
done();
};
I hate to say how long this took me to realize this. But, I needed to return a promise from my controller, so that my test function would have something to wait on. By changing my controller to this:
exports.insert = async (request, response) => {
await UserModel.createUser(request.body)
.then(() => {
response.status(201).send({message: "User created successfully"});
})
.catch(() => {
response.status(400).send({message: "There is already an account with that email"});
});
return response;
And with my test looking something like this:
it('should return a status code of 201', async function () {
await User.insert(request, response);
assert(response._getStatusCode() === 201);
});
The database populates successfully, and my test will wait for my promise to fulfill, thus updating the response before calling my assert. Then the after hook works when expected.
I hope this helps someone else who stumbles across this old thread.

Grunt registerTask() Not Running Tasks in List

I have added the following registerTask calls to this Gruntfile.js
grunt.task.registerTask('runDebug', 'Run the app with debug flag', function() {
var done = this.async();
grunt.util.spawn({
cmd: 'node',
args: ['--debug', './node_modules/nodemon/nodemon.js', 'index.js'],
opts: {
stdio: 'inherit'
}
}, function (error, result, code) {
if (error) {
grunt.log.write (result);
grunt.fail.fatal(error);
}
done();
});
grunt.log.writeln ('node started');
grunt.util.spawn({
cmd: 'node-inspector',
args: ['&'],
opts: {
//cwd: current working directory
}
},
function (error, result, code) {
if (error) {
grunt.log.write (result);
grunt.fail.fatal(error);
}
done();
});
grunt.log.writeln ('inspector started');
});
grunt.task.registerTask('debug', ['runDebug', 'compile', 'watch']);
The new debug task is similar to the existing server task. However, grunt server command runs compile, watch, and runNode tasks, whereas grunt debug command only runs runDebug task.
What am I missing here? Why aren't the compile and watch tasks run with grunt debug command.
Your code is calling the done() function returned by this.async() more than once. This could be confusing Grunt. I would suggest calling a function of your own which could named spawned() instead of calling done() directly in your callbacks. The function could be something like:
var expected_spawns = 2;
function spawned() {
if (!--expected_spawns)
done();
if (expected_spawns < 0)
throw new Error("too many spawns!") // Or some of Grunt's fail functions.
}
This way done() will be called once.

Categories