Stubbing PouchDB with Sinon failing - javascript

Im trying to stub a pouchDB 'put' function but in an invoked function but it is failing.
my DB function-
var PouchDB = require('pouchDB')
var PendingDB = new PouchDB("")
module.exports.addPendingRequest = function(doc, callback){
PendingDB.put(doc, function(err, result){
if(err) {
console.log("Error in PendingDB: addPendingRequest");
console.log(err);
callback(err, null);
}
callback(null, result);
});
console.log("after put: inside addPendingRequest");
}
My Test Function:
var expect = require("chai").expect;
var PendingDB = require("../../lib/administration/PendingDB");
var PouchDB = require('pouchDB');
var sinon = require('sinon');
describe('Testing adding a request', function(){
it('should save the request with email', function(done){
var req = {
_id : "test#email.com",
first_name: "firstTest",
last_name: "test",
id: "test#email.com",
justif: "Testing Purposes",
}
var res = {};
var next = null;
console.log("after req, res, next");
var testOutput = {
success : "success"
};
console.log("after testOutput is set");
var PendingDBTest = sinon.stub(PouchDB.prototype, 'put', function(err, result){
console.log("in stub addReq");
});
console.log("after sinon.stub");
expect(function(){
PendingDB.addPendingRequest(req, function(err, response){
console.log("response");
console.log(response);
});
}).to.not.throw(Error);
expect(PendingDBTest.called).to.equal(true);
PendingDBTest.restore();
done();
})
})
Console:
after req, res, next
after testOutput is set
after sinon.stub
after put: inside addPendingRequest
Therefore PendingDB.put is never entered and my test PendingDBTest.called returns false, thus failing.

Stub on actual object, not on its prototype.
var PendingDBTest = sinon.stub(PouchDB, 'put', function(err, result){
...

Instead of stubbing you could write your tests with the in-memory adapters avaliable for PouchDB:
http://pouchdb.com/adapters.html
In NodeJS:
var PouchDB = require('pouchdb');
var testDB = new PouchDB('testDb', {
db: require('memdown')
});
Or in the Browser:
<script src="pouchdb.js"></script>
<script src="pouchdb.memory.js"></script>
<script>
// this pouch is ephemeral; it only exists in memory
var testDB = new PouchDB('testDB', {
adapter: 'memory'
});
</script>
For example of a project that tests in this way check out: https://github.com/hoodiehq/pouchdb-hoodie-api/tree/master/tests

Related

Callback is not a function -manage asynchronous call Node js

I am reading files from ftp using the code below.
var JSFtp = require("jsftp");
var config = require('./config.json');
var FtpService = function () {};
// Connect to FTP
var Ftp = new JSFtp({
host: config.ftp.host,
port: config.ftp.port,
user: config.ftp.user,
pass: config.ftp.pass
});
FtpService.prototype.getFTPDirectoryFiles = function (callback) {
Ftp.list(config.ftp.FilePath, function(err, res) {
if(err){
console.log('File Listing Failed', err);
callback(null,err);
return;
}
else{
console.log(res);
callback(null,res);
}
});
};
FtpService.prototype.closeFtp = function () {
console.log('Disconnect to FTP');
};
module.exports = new FtpService();
Now i include this ftp service js file in my index.js as
var ftp = require('./ftpservice.js');
ftpfiles = ftp.getFTPDirectoryFiles();
console.log(ftpfiles);
getFTPDirectoryFiles returns the list of file. But if i call it via index.js i get undefined ftpfiles. This is because of the asynchronous nature of node js.
so i thought of adding callback but
I am getting the error Callback is not defined in function FtpService.prototype.getFTPDirectoryFiles
In this line:
ftpfiles = ftp.getFTPDirectoryFiles()
you are not passing the callback that that function requires and are trying to use a return value that the function does not return.
You need to do something like this:
var ftp = require('./ftpservice.js');
ftp.getFTPDirectoryFiles(function(err, ftpfiles) {
if (err) {
console.log(err);
} else {
console.log(ftpfiles);
}
});
You need to pass a callbackfunction in your function getFTPDirectoryFiles();
var ftp = require('./ftpservice.js');
var ftpFiles;
function setFtpFiles(err, res) {
if (err) throw err;
ftpFiles = res; // to use "ftpFiles" variable later
console.log(res);
}
ftp.getFTPDirectoryFiles(setFtpFiles);
1 Don't change args order to call callback. (replace callback(null,err); and callback(null,res); by callback(err,res);)
2 You need define a specifc function (your callaback) an give it to ftp.getFTPDirectoryFiles().
var JSFtp = require("jsftp");
var config = require('./config.json');
var FtpService = function () {};
// Connect to FTP
var Ftp = new JSFtp({
host: config.ftp.host,
port: config.ftp.port,
user: config.ftp.user,
pass: config.ftp.pass
});
FtpService.prototype.getFTPDirectoryFiles = function (callback) {
Ftp.list(config.ftp.FilePath, function(err, res) {
if(err){
console.log('File Listing Failed', err);
callback(err, res);
return;
}
else{
console.log(res);
callback(err, res);
}
});
};
FtpService.prototype.getFTPDirectoryFilesSimplify = function (callback) {
// no console.log, but very more simple !
Ftp.list(config.ftp.FilePath, callback);
};
FtpService.prototype.closeFtp = function () {
console.log('Disconnect to FTP');
};
and then :
var ftp = require('./ftpservice.js');
ftpfiles = ftp.getFTPDirectoryFiles(function(err,res){
// do your specifc job here using err and res
});
console.log(ftpfiles);

Parsing JSON result from node oracledb

I am using oracledb with node and fetching data from it asynchronously. For the sake of ease, I have implemented it using separate files like below -
config.js -
module.exports = {
user : "user",
password : "password",
connectString : "*connstring*" ,
deliveredQuery: " SELECT COUNT (DISTINCT order_num) AS Cnt from orders where department = 'HR'
};
query2.js :
module.exports = function(callback) {//pass callback function and return with this
var oracledb = require('oracledb');
var dbConfig = require('./config.js');
this.queryDB = function(query,callback) {
oracledb.getConnection({
user: dbConfig.user,
password: dbConfig.password,
connectString: dbConfig.connectString,
deliveredQuery: dbConfig.deliveredQuery
}, function(err, connection) {
if (err) {
console.error(err.message);
return callback(err);
}
connection.execute(query, function(err, result) {
if (err) {
console.error(err.message);
doRelease(connection);
return;
}
//console.log(result.metaData);
//console.log(JSON.parse(result.rows[0][0]));
doRelease(connection);
return callback(null, JSON.parse(result.rows[0][0]))
});
});
function doRelease(connection) {
connection.release(function(err) {
if (err) {
console.error(err.message);
return callback(err);
}
});
}
};
};
serv_ontime.js :
var dbConfig = require('./config.js');
var res = require('./query2.js')();
var onTime_query = dbConfig.onTime_query;
module.exports = queryDB(onTime_query, function(err, callback){ });
index.js :
var res = require('./serv_ontime.js');
console.log("The result is= "+ res);
Now, When I am doing - node index.js from my cmd then I am getting the output as [object Object]. I suppose it is because the call is happening asynchronously. But if you see in the file query2.js , I am returning the value after parsing(using JSON.parse) but still the value I am getting in the index.js file is not the parsed one. How can I parse the value in index.js? I have already tried JSON.parse but it doesn`t work.
You are getting output as [object Object] because you are doing + with a String('The result is= '), So js engine tries to convert the Object to a String. To view it as an Object, do log it separately or log with comma separated,
console.log('The result is= ', res); (or)
console.log(res); // or console.dir(res)
Or you can get String version of it, by doing JSON.stringify(res)
console.log('The result is= ', JSON.stringify(res));
In serv_ontime.js you are exporting result of queryDB witch indeed is undefined. Ty this:
serv_ontime.js
var dbConfig = require('./config.js');
var res = require('./query2.js')();
var onTime_query = dbConfig.onTime_query;
module.exports = function (callback) {
queryDB(onTime_query, callback)
};
index.js
var serv_ontime = require('./serv_ontime.js');
serv_ontime(function(error, res) {
console.log("The error is= ", error);
console.log("The result is= ", res);
});

Multiple node.js requests

My controller is using the request package to make server-side HTTP requests to another API. My question is how can I make MULTIPLE of these requests? Here is my current code:
** UPDATED CODE **
module.exports = function (req, res) {
var context = {};
request('http://localhost:3000/api/single_project/' + req.params.id, function (err, resp1, body) {
context.first = JSON.parse(body);
request('http://localhost:3001/api/reports/' + req.params.id, function (err, resp2, body2) {
context.second = JSON.parse(body2); //this line throws 'SyntaxError: Unexpected token u' error
res.render('../views/project', context);
});
});
};
I need to make two more of those calls and send the data returned from it to my template...
Can someone help?
Thanks in advance!
function makePromise (url) {
return Promise(function(resolve, reject) {
request(url, function(err, resp, body) {
if (err) reject(err);
resolve(JSON.parse(body));
});
});
}
module.exprts = function (req, res) {
let urls = ['http://localhost:3000/api/1st',
'http://localhost:3000/api/2st',
'http://localhost:3000/api/3st'].map((url) => makePromise(url));
Promise
.all(urls)
.then(function(result) {
res.render('../views/project', {'first': result[0], 'second': result[1], 'third': result[2]});
})
.catch(function(error){
res.end(error);
});
}
You can use Promise lib in latest nodejs.
Simple solution
Nest request calls. This is how you can handle the dependency between requests. Just make sure your parameters are unique across scopes if needed.
module.exports = function (req, res) {
var context = {};
request('http://localhost:3000/api/1st', function (err, resp1, body) {
var context.first = JSON.parse(body);
request('http://localhost:3000/api/2nd', function (err, resp2, body) {
context.second = JSON.parse(body);
request('http://localhost:3000/api/3rd', function (err, resp3, body) {
context.third = JSON.parse(body);
res.render('../views/project', context);
});
});
});
};
Simplest way if you use bluebird promise library:
var Promise = require('bluebird');
var request = Promise.promisify(require('request'));
module.exports = function (req, res) {
var id = req.params.id;
var urls = [
'http://localhost:3000/api/1st/' + id,
'http://localhost:3000/api/2st/' + id,
'http://localhost:3000/api/3st/' + id
];
var allRequests = urls.map(function(url) { return request(url); });
Promise.settle(allRequests)
.map(JSON.parse)
.spread(function(json1, json2, json3) {
res.render('../views/project', { json1: json1 , json2: json2, json3: json3 });
});
});
it executes all requests even if one (or more) fails

node.js and async module error

I am getting an undefined variable in my code and not sure what the error in my code is:
I get client as undefined when I call getClient...
I have a soap client creation singleton and I have:
var mySingleton = (function() {
var soap = require('soap');
var async = require('async');
var instance;
var client;
function init() {
var url = "http://172.31.19.39/MgmtServer.wsdl";
var endPoint = "https://172.31.19.39:9088";
var options = {};
options.endpoint = endPoint;
async.series([
function(callback) {
soap.createClient(url, options, function (err, result){
console.log('Client is ready');
client = result;
client.setSecurity(new soap.BasicAuthSecurity('admin-priv', 'password'));
callback();
});
}
],
function(err) {
if (err)
return next(err);
});
return {
getClient : function() {
console.log("I will give you the client");
**return client;**
},
publicProperty : "I am also public",
};
};
return {
getInstance : function() {
if (!instance) {
instance = init();
}
return instance;
}
};
})();
module.exports = mySingleton;
so my consumer is :
var soapC = mySingleton.getInstance();
var mySoapClient = soapC.getClient();
I get mySingleton.client is undefined.
Why?
Sure there are better solutions than this one, but it shows you that it can be implemented easier (without async, without singleton):
var soap = require('soap');
var client;
var url = "http://172.31.19.39/MgmtServer.wsdl";
var options = {
endpoint: "https://172.31.19.39:9088"
};
module.exports = {
getClient: function (callback) {
if (client) {
callback(null, client);
return;
}
soap.createClient(url, options, function (err, result) {
if (err) {
callback(err);
return;
}
console.log('Client is ready');
client = result;
client.setSecurity(new soap.BasicAuthSecurity('admin-priv', 'password'));
callback(null, client);
});
},
publicProperty: "I am also public"
};
And when using the client:
// using the client
var mySoapServer = require('./path/to/above/code.js');
mySoapServer.getClient(function (err, client) {
if (err) { /* to error handling and return */ }
client.someRequestMethod(myEnvelope, function (err, response) {
// ...
});
});
There might be a problem when your Soap-Clients gets into trouble (there is no logic to reconnect in case of error). For this you could have a look at the source code of Redis-Client, MySQL-Client, MongoDB-Client, ...
Edit
Some comments on the different aproaches:
The Singleton-pattern is not needed here. Node will execute this JS file only once and further requires get only a reference to the exports. There is no need to create an IIFE scope - the variables won't be visible outside, only the exports.
Programming in Node.js is (besides some special cases) an all-async way. If not done consequently, it just doesn't work or fails/succeeds only if you have good/bad luck.
Error handling looks very much like a lot of boilerplate, but it's necessary in most cases.

Testing asynchronous function with mocha

I want to test a asynchronous javascript function that runs in node.js and makes a simple request to a http api:
const HOST = 'localhost';
const PORT = 80;
http = require('http');
var options = {
host: HOST,
port: PORT,
path: '/api/getUser/?userCookieId=26cf7a34c0b91335fbb701f35d118c4c32566bce',
method: 'GET'
};
doRequest(options, myCallback);
function doRequest(options, callback) {
var protocol = options.port == 443 ? https : http;
var req = protocol.request(options, function(res) {
var output = '';
res.setEncoding('utf8');
res.on('data', function(chunk) {
console.log(chunk);
output += chunk;
});
res.on('error', function(err) {
throw err;
});
res.on('end', function() {
var dataRes = JSON.parse(output);
if(res.statusCode != 200) {
throw new Error('error: ' + res.statusCode);
} else {
try {
callback(dataRes);
} catch(err) {
throw err;
}
}
});
});
req.on('error', function(err) {
throw err;
});
req.end();
}
function myCallback(dataRes) {
console.log(dataRes);
}
Executed this code works and the response will be displayed as expected.
If I execute this in a mocha test the request is not executed:
describe('api', function() {
it('should load a user', function() {
assert.doesNotThrow(function() {
doRequest(options, myCallback, function(err) {
if (err) throw err;
done();
});
});
assert.equal(res, '{Object ... }');
});
});
The Problem is, that no code after:
var req = protocol.request(options, function(res) {
is executed not even a simple console.log.
Can anybody help?
You have to specify the callback done as the argument to the function which is provided to mocha - in this case the it() function. Like so:
describe('api', function() {
it('should load a user', function(done) { // added "done" as parameter
assert.doesNotThrow(function() {
doRequest(options, function(res) {
assert.equal(res, '{Object ... }'); // will not fail assert.doesNotThrow
done(); // call "done()" the parameter
}, function(err) {
if (err) throw err; // will fail the assert.doesNotThrow
done(); // call "done()" the parameter
});
});
});
});
Also, the signature of doRequest(options, callback) specifies two arguments though when you call it in the test you provide three.
Mocha probably couldn't find the method doRequest(arg1,arg2,arg3).
Did it not provide some error output? Maybe you can change the mocha options to get more information.
EDIT :
andho is right, the second assert would be called in parallel to assert.doesNotThrow while it should only be called in the success callback.
I have fixed the example code.
EDIT 2:
Or, to simplify the error handling (see Dan M.'s comment):
describe('api', function() {
it('should load a user', function(done) { // added "done" as parameter
assert.doesNotThrow(function() {
doRequest(options, function(res) {
assert.equal(res, '{Object ... }'); // will not fail assert.doesNotThrow
done(); // call "done()" the parameter
}, done);
});
});
});
If you have an asynchronous function that does not support callbacks, or if you think using unnecessary callbacks is... unnecessary, then you can also just turn the test into an async test.
instead of:
it('should be able to do something', function () {});
simply do:
it('should be able to do something', async function () {});
^^^^^
Now you can await async functions:
it('should be able to do something', async function () {
this.timeout(40000);
var result = await someComplexFunction();
assert.isBelow(result, 3);
});
I've done a very similar test in my project for an http client. I paste the code here and hope is useful.
Here is the client (my nodejs server use express and I use promise for error handling):
var http = require('http');
var querystring = require('querystring');
module.exports = {
get: function(action, params, res, callback) {
doPromiseRequest(action, querystring.stringify(params), callback, 'GET', 'application/json')
.then((response) => callback(response))
.catch((error) => {
res.status(500);
res.render('error', {layout: false, message: error.message, code: 500});
});
},
}
function doPromiseRequest(action, params, callback, method, contentType) {
var options = {
hostname: 'localhost',
port: 3000,
path: '/api/v1/' + action.toString(),
method: method,
headers: {
'Content-Type': contentType,
'Content-Length': Buffer.byteLength(params)
}
};
return new Promise( (resolve, reject) => {
var req = http.request(options,
function(response) {
response.setEncoding('utf8');
var data = '';
response.on('data', function(chunk) {
data += chunk;
});
response.on('end', function() {
var parsedResponse;
try {
parsedResponse = JSON.parse(data);
} catch(err) {
reject({message: `Invalid response from hurricane for ${action}`});
return;
}
if (parsedResponse.error)
reject(parsedResponse.error);
else
resolve(parsedResponse);
});
response.on('error', function(err){
console.log(err.message);
reject(err);
});
});
req.on('error', function(err) {
console.log(err);
reject({message: err.message});
});
req.write(params);
req.end();
});
}
And here is the test:
var http = require('http');
var expect = require('chai').expect;
var sinon = require('sinon');
var PassThrough = require('stream').PassThrough;
describe('Hurricane Client tests', function() {
before(function() {
this.request = sinon.stub(http, 'request');
});
after(function() {
http.request.restore();
});
it('should convert get result to object', function(done) {
var expected = { hello: 'world' };
var response = new PassThrough();
response.statusCode = 200;
response.headers = {}
response.write(JSON.stringify(expected));
response.end();
var request = new PassThrough();
this.request.callsArgWith(1, response).returns(request);
client.get('any', {}, null, function(result) {
expect(result).to.eql(expected);
done();
});
});
});

Categories