How can you generate a promise in functional teardown if this.async isn't available? - intern

What's the equivalent of calling this.async() in teardown of a function test suite? I'm using this.async() in a test to create a promise that I later resolve and want to move this code into the teardown but it seems that it's not available.
The documentation suggests that the test suite will pause whilst it waits for a promise to be resolved, but I'm not sure how to generate a promise in a teardown that I can call the .resolve() and .reject() functions on.
This is the code I have working in a test:
var dfd = test.async(10000);
var js = "var coverageData = {" +
"name : name || ''," +
"lines : $$_l.lines," +
"runLines : $$_l.runLines," +
"code : $$_l.code," +
"allConditions : $$_l.allConditions," +
"conditions : $$_l.conditions," +
"allFunctions : $$_l.allFunctions," +
"runFunctions : $$_l.runFunctions" +
"};" +
"return JSON.stringify(coverageData);";
browser.execute(js)
.then(function(data) {
console.log("Retrieved coverage data from browser...");
try {
var post_options = {
host: "localhost",
port: "8082",
path: "/node-coverage-store",
method: "POST",
headers: {
"Content-Type": "application/json;charset=UTF-8"
}
};
console.log("Posting coverage data...");
// Set up the request
var post_req = http.request(post_options, function(res) {
res.setEncoding("utf8");
res.on("data", function (data) {
console.log("Coverage data posted successfully");
dfd.resolve();
});
});
post_req.on("error", function(e) {
console.log("Coverage data post failed", e);
dfd.reject(e);
});
post_req.write(data);
post_req.end();
}
catch (e) {
console.log("An error occurred handling coverage data", e);
}
});
How would I instantiate the promise in a teardown?
FYI... I know that Intern includes code coverage capabilities, but they don't meet our specific requirements.

You need to create a promise and return it yourself. You can use your own Promise library, native Promises (if they exist on the platforms you are testing), or you can use 'intern/dojo/Deferred' (but be aware doing this last one will require you to change your tests when the next version is released).

Related

Node function in AWS throws "missing ) after argument list" error

I have created a Lambda function (Node.js 12.x) in AWS so SNS messages are pushed to Slack from our ETL tool Matillion.
console.log('Loading function');
const https = require('https');
const url = require('url');
const slack_url = 'https://hooks.slack.com/services/T1MJBHQ95/B01DQ60NUR2/....';
const slack_req_opts = url.parse(slack_url);
slack_req_opts.method = 'POST';
slack_req_opts.headers = { 'Content-Type': 'application/json' };
exports.handler = function (event, context) {
(event.Records || []).forEach(function (rec) {
if (rec.Sns) {
var req = https.request(slack_req_opts, function (res) {
if (res.statusCode === 200) {
context.succeed('sns posted to slack');
}
else {
context.fail('status code: ' + res.statusCode);
}
});
req.on('error', function (e) {
console.log('problem with request: ' + e.message);
context.fail(e.message);
});
req.write(JSON.stringify({ text: `${rec.Sns.Message}` }));
req.end();
}
});
};
The function will fail with a missing ) after argument list syntax error. I run it thru a linter in Sublime and it throws an error on require and exports being undefined.
My research shows several challenges:
I may need a file called eslint.rc but I am unclear why I need to put
in.
The use of "require" and "exports" appears deprecated.
Can someone please give me pointers how what to focus on to resolve this? Thank you.
You made a syntax mistake in your code.
req.write(JSON.stringify({ text: `${rec.Sns.Message}` }));
req.end();
Need to add ) before ; in your req.write() statement.
Just use Axios, it will make your life easier:
console.log('Loading function');
const axios = require('axios');
const url = require('url');
const slack_url = 'https://hooks.slack.com/services/T1MJBHQ95/B01DQ60NUR2/....';
exports.handler = async (event, context) {
(event.Records || []).forEach(function(rec) {
if (rec.Sns) {
axios.post(slack_url, {
text: rec.Sns.Message
}, {
'Content-Type': 'application/json'
}).done(r => {
context.succeed('sns posted to slack');
}).catch(e => {
context.fail('status code: ' + e.statusCode);
});
});
Notice that in your code, after 1 sns event he end the invocation, not running all, to run all of them you need to wait for all requests to be done (can use Promise.all for example)
Also, be aware that sns can send directly to HTTPS without any code
https://aws.amazon.com/sns/features/
By the way, you can use an external monitoring tool that will show you exactly the outgoing request + all payload as Lumigo (As a disclaimer I work for a company named lumigo)
I fixed my issue:
I added the following statement at the top of my file /* eslint-env node, common */
I re-deployed my function.
I believe the redeployment of the function did the trick.

Module Export callback function returns undefined

Hello: i am new to nodejs and mocha. I trying to use module.exports to return a value from a callback function. However, its returning undefined. For simple cases it works though. Please help.
Result
Module Export Example
√ Test Case 1: Module
Hello Node World!!! (*** this works - its a direct return ***)
√ Test Case 2: Module
undefined (*** this fails - its from a callback fn ***)
google.js
var requirejs = require('requirejs');
requirejs.config({ baseUrl: '.', paths: { }, nodeRequire: require });
describe('Module Export Example', function(){
var mod;
before(function(done){
requirejs(['./googleModule'],
function(_mod) {
mod = _mod;
done();
});
});
it('Test Case 1: Module', function(done){
console.log(mod.get(done));
});
it('Test Case 2: Module', function(done){
console.log(mod.google(done));
});
});
googleModule.js
var request = require('request');
module.exports = {
get: function(done){
var a = "Hello Node World!!!";
return(done(), a);
},
google: function(done){
var a = doCallback(function(){
var b = "PRINT DATA: " + data.statusCode + ' ' + data.headers['content-type'];
return(done(), b);
});
return(done(), a);
}
}
function doCallback(callback, done){
var options = {url: 'http://www.google.com', headers: {'Content-Type': 'text/html'}, encoding: null};
request.get(options, function(err, res, body){
var a = callback(res, done);
return (callback(), a); //???????
});
}
Because you say you are new to nodejs and mocha, I assume you didn't want to do anything fancy and simplified your code.
Result
Module Export Example
Hello Node World!!!
✓ Test Case 1: Module
test
PRINT DATA: 200 text/html; charset=ISO-8859-1
✓ Test Case 2: Module (194ms)
test/test.js
let mod = require('../googleModule');
describe('Module Export Example', function(){
it('Test Case 1: Module', function(){
console.log(mod.get()); // this is synchronous, no done needed
});
it('Test Case 2: Module', function(done){
mod.google(function(res) {
console.log(res);
done(); // this is asynchronous, so we need to tell mocha when we are done
});
console.log('test');
});
});
googleModule.js
let request = require('request');
module.exports = {
get: function() {
let a = "Hello Node World!!!";
return a;
},
google: function(callback) {
let options = {url: 'http://www.google.com', headers: {'Content-Type': 'text/html'}, encoding: null};
request.get(options, function(err, res, body) {
var b = "PRINT DATA: " + res.statusCode + ' ' + res.headers['content-type'];
callback(b);
});
}
}
I changed 2 major things to get it working.
I removed require.js. You don't need to use that in node.js because a module loader is already included.
In javascript there are synchronous and asynchronous functions. Synchronous functions are regular functions that you know from other programming languages like PHP or Java. However, javascript also has asynchronous functions. The difference is that the code inside an asynchronous function will be executed later and you can't expect it to return the value immediately. As an example, look at the output of your test and compare it to your code. As you can see, console.log('test'); gets printed BEFORE console.log(res), even though it is below the other one. To handle this, javascript makes use of callbacks (or Promises). A callback is like a way of telling the program where it should continue once the asynchronous function is finished. In your case, the HTTP-Request is asynchronous, so you need to tell your code to "wait" for it to finish and then print it. The done is used to tell mocha when the test is finished.
It's probably best if you read some articles about how asynchronous functions work.

Wait for JSON response data from POST request in nodejs (mocha test)

I'm aware that there are several questions related to mine, but I didn't find any of them useful:
this one doesn't apply to my case, I'm actually getting the answer, it's the contents that I can't get.
on this one, on the other hand, the problem is a wrong handling of an asynchronous call, which is not my case
there, well, I really didn't fully understand this question
And so on...
Then, I think this is a legitimate question. I'm actually performing some encryption in my server (express routing in node) through a post request:
app.post('/encrypt', encrypt);
Encrypt is doing:
function encrypt(req,res) {
if(req.body.key && req.body.message) {
var encryptedMessage = Encrypter.encrypt(req.body.key,req.body.message);
return res.status(200).json({ message: encryptedMessage });
}
res.status(409).json({ message: 'the message could not be encrypted, no key found' });
}
}
So, I tested this via console.log, and it's working. When the server receives the request, the encrypted message is being generated.
At the same time, I'm testing my thing with mocha and I'm doing it like so:
describe('# Here is where the fun starts ', function () {
/**
* Start and stop the server
*/
before(function () {
server.listen(port);
});
after(function () {
server.close();
});
it('Requesting an encrypted message', function(done) {
var postData = querystring.stringify({
key : key,
message : message
});
var options = {
hostname: hostname,
port: port,
path: '/encrypt',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': postData.length
}
};
var req = http.request(options, function(res) {
res.statusCode.should.equal(200);
var encryptedMessage = res.message;
encryptedMessage.should.not.equal(message);
done();
});
req.on('error', function(e) {
//I'm aware should.fail doesn't work like this
should.fail('problem with request: ' + e.message);
});
req.write(postData);
req.end();
});
});
So, whenever I execute the tests, it fails with Uncaught TypeError: Cannot read property 'should' of undefined because res.message does not exist.
None of the res.on (data, end, events is working, so I suppose the data should be available from there. First I had this:
var req = http.request(options, function(res) {
res.statusCode.should.equal(200);
var encryptedMessage;
res.on('data', function (chunk) {
console.log('BODY: ' + chunk);
encryptedMessage = chunk.message;
});
encryptedMessage.should.not.equal(message);
done();
});
But res.on was never accessed (the console.log didn't show anything). I'm therefore a bit stuck here. I'm surely doing some basic stuff wrong, but I don't have a clue, and the many questions I found doesn't seem to apply to my case.
Weird enough, if I launch a test server and then I curl it
curl --data "key=secret&message=veryimportantstuffiabsolutellyneedtoprotect" localhost:2409/encrypt
Curl justs waits ad aeternam.
Actually I was doing it properly at the beginning, and the problem was indeed the same than in the second question I mentionned I was actually "clearing" my context with done() before the post data arrived. The solution is:
var req = http.request(options, function(res) {
res.statusCode.should.equal(200);
res.on('data', function(data) {
encryptedMessage = JSON.parse(data).message;
encryptedMessage.should.not.equal(message);
done();
});
});
In such a way that done() is only called when the data has been threated. Otherwise, mocha will not wait for the answer.

Failing test displays "Error: timeout of 2000ms exceeded" when using Sinon-Chai

I have the following route (express) for which I'm writing an integration test.
Here's the code:
var q = require("q"),
request = require("request");
/*
Example of service wrapper that makes HTTP request.
*/
function getProducts() {
var deferred = q.defer();
request.get({uri : "http://localhost/some-service" }, function (e, r, body) {
deferred.resolve(JSON.parse(body));
});
return deferred.promise;
}
/*
The route
*/
exports.getProducts = function (request, response) {
getProducts()
.then(function (data) {
response.write(JSON.stringify(data));
response.end();
});
};
I want to test that all the components work together but with a fake HTTP response, so I am creating a stub for the request/http interactions.
I am using Chai, Sinon and Sinon-Chai and Mocha as the test runner.
Here's the test code:
var chai = require("chai"),
should = chai.should(),
sinon = require("sinon"),
sinonChai = require("sinon-chai"),
route = require("../routes"),
request = require("request");
chai.use(sinonChai);
describe("product service", function () {
before(function(done){
sinon
.stub(request, "get")
// change the text of product name to cause test failure.
.yields(null, null, JSON.stringify({ products: [{ name : "product name" }] }));
done();
});
after(function(done){
request.get.restore();
done();
});
it("should call product route and return expected resonse", function (done) {
var writeSpy = {},
response = {
write : function () {
writeSpy.should.have.been.calledWith("{\"products\":[{\"name\":\"product name\"}]}");
done();
}
};
writeSpy = sinon.spy(response, "write");
route.getProducts(null, response);
});
});
If the argument written to the response (response.write) matches the test passes ok. The issue is that when the test fails the failure message is:
"Error: timeout of 2000ms exceeded"
I've referenced this answer, however it doesn't resolve the problem.
How can I get this code to display the correct test name and the reason for failure?
NB A secondary question may be, could the way the response object is being asserted be improved upon?
The problem looks like an exception is getting swallowed somewhere. The first thing that comes to my mind is adding done at the end of your promise chain:
exports.getProducts = function (request, response) {
getProducts()
.then(function (data) {
response.write(JSON.stringify(data));
response.end();
})
.done(); /// <<< Add this!
};
It is typically the case when working with promises that you want to end your chain by calling a method like this. Some implementations call it done, some call it end.
How can I get this code to display the correct test name and the reason for failure?
If Mocha never sees the exception, there is nothing it can do to give you a nice error message. One way to diagnose a possible swallowed exception is to add a try... catch block around the offending code and dump something to the console.

Can't get one node.js server to talk to another

I have two node servers, one on port 5000 (call it "Face") and another on port 5001 (call it "Hands")
Both are started via a foreman procfile at the same time. Ports are fixed and the url I'm targeting works in the browser.
When the Hands starts up, it needs to talk to the Face (Facepalm?) and register itself. However, the below code doesn't seem to be working. (This is coffeescript generated JS)
Register gets called during server initialization, after the http server has been started. In case it was a timing issue, I kick off the register function with a setTimeout() of 2 seconds. I know the page that its hitting (/home/register) is available and working.
Right now I can see it get to the "Posting to" console log line. On the Face I have put a console.log in the register code and its never logging anything - meaning I don't think its actually getting hit. (It DOES log if hit from browser) And nothing errors out - it just calls the request and then wanders off to get a sandwich.
Both servers are "roll your own" - not using any frameworks. Let me know if you see a weird typo or need more info. Thanks!
register = function() {
var _this = this;
console.log('Registering with Face Server');
return post_face('/home/register', GLOBAL.data, function(rs) {
console.log(rs);
if (rs.registered) {
GLOBAL.data.registered = true;
return console.log("Registered with face at " + GLOBAL.config.face_host + ":" + GLOBAL.config.face_port);
} else {
throw "ERROR: Could not register with face server! " + GLOBAL.config.face_host + ":" + GLOBAL.config.face_port;
return false;
}
});
};
post_face = function(path, data, cb) {
var post_data, post_options, post_req;
post_data = querystring.stringify({
'registration': data
});
post_options = {
host: GLOBAL.config.face_host,
port: GLOBAL.config.face_port,
path: path,
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': post_data.length
}
};
console.log("Posting to " + post_options.host + ":" + post_options.port);
post_req = http.request(post_options, function(res) {
var response,
_this = this;
console.log(res);
response = "";
res.setEncoding('utf8');
res.on('data', function(chunk) {
return response += chunk;
});
return res.on('end', function() {
return cb.call(_this, response);
});
});
return true;
};
Thanks to Bill above, the answer was that I wasn't actually posting the data and ending the request! Bad copy / paste / edit from some samples I was referring to. Here's the last two lines of code I should have had:
post_req.write(post_data);
post_req.end();

Resources