mocha stub using sinon in node.js - tdd

I would like to know if I am missing anything with regard to sinon.js I have tried using sinon.stub().returns and yields but am unable to get the result. Any pointers would be helpful
I have a module which calls another module that returns the value from the DB
var users = require('/users');
module.exports.getProfileImage = function (req, res) {
var profile = {};
else {
users.findOne("email", req.session.user.email, function (err, user) {
if (err) {
res.status(400).send();
}
else if (!user) {
//return default image
}
else if (user) {
//Do some other logic here
}
});
};
I am using mocha as the testing framework and am also using sinon. The problem that I am facing is when i create a stub of users.findOne to return a value the control does not come to my else if (user) condition.
my unit test case is as follows
describe("Return image of user",function(){
var validRequest = null;
validRequest={
session:{
user:{
email:'testUser#test.com',
role:'Hiring Company'
}
}
};
it("Should return an image from the file if the user is present in db",function(done){
var findOneUserResponse ={
companyName:"xyz",
email:"xyz#abc.com"
};
var findOne = sinon.stub(mongoose.Model, "findOne");
findOne.callsArgWith(1,null,findOneUserResponse);
user.getProfileImage(validRequest,response);
var actualImage = response._getData();
findOne.restore();
done();
};
};

So I went through the sinon.js documentation http://sinonjs.org/docs/ and came across what I was missing
describe("Return image of user",function(){
var validRequest = null;
validRequest={
session:{
user:{
email:'testUser#test.com',
role:'Hiring Company'
}
}
};
it("Should return an image from the file if the user is present in db",function(done){
var findOneUserResponse ={
companyName:"xyz",
email:"xyz#abc.com"
};
var findOne = sinon.stub(mongoose.Model, "findOne",function(err,callback){
callback(null,findOneUserResponse);
)};
user.getProfileImage(validRequest,response);
var actualImage = response._getData();
findOne.restore();
done();
};
};

Related

Using a URL query parameter to version cached responses

I am trying to cache specific urls and each url has md5 hash and If the urls updated with new md5 i want to remove the current cache and add the new one.
cached url: http://www.mysite.lo/cards/index.php?md5=f51c2ef7795480ef2e0b1bd24c9e07
function shouldFetch(event) {
if ( event.request.url.indexOf( '/cards/') == -1 ) {
return false;
}
return true;
}
self.addEventListener('fetch', function(event) {
if (shouldFetch(event)) {
event.respondWith(
caches.match(event.request).then(function(response) {
if (response !== undefined) {
return response;
} else {
return fetch(event.request).then(function (response) {
let responseClone = response.clone();
caches.open('v1').then(function (cache) {
cache.put(event.request, responseClone);
});
return response;
}).catch(function (err) {
return caches.match(event.request);
});
}
})
);
}
});
I know we can use caches.delete() and so on, but I want to call it only if the md5 updated from the new request.
Thanks
You can accomplish roughly what you describe with the following, which makes use of the ignoreSearch option when calling cache.matchAll():
self.addEventListener('fetch', (event) => {
const CACHE_NAME = '...';
const url = new URL(event.request.url);
if (url.searchParams.has('md5')) {
event.respondWith((async () => {
const cache = await caches.open(CACHE_NAME);
const cachedResponses = await cache.matchAll(url.href, {
// https://developers.google.com/web/updates/2016/09/cache-query-options
ignoreSearch: true,
});
for (const cachedResponse of cachedResponses) {
// If we already have the incoming URL cached, return it.
if (cachedResponse.url === url.href) {
return cachedResponse;
}
// Otherwise, delete the out of date response.
await cache.delete(cachedResponse.url);
}
// If we've gotten this far, then there wasn't a cache match,
// and our old entries have been cleaned up.
const response = await fetch(event.request);
await cache.put(event.request, response.clone());
return response;
})());
}
// Logic for non-md5 use cases goes here.
});
(You could make things slightly more efficient by rearranging some of the cache-manipulation code to bring it out of the critical response path, but that's the basic idea.)

Jasmine: Expected spy <function> to have been called error

In my angular application, I am trying to write a test case for following scenario but getting error 'Expected spy reinvite to have been called.'. Im testing on "jasmine-core: ^2.5.2 and "karma: ^1.3.0". I have written similar test cases and they passed without error.
In my controller file:
function reinvite() {
var emailsToReInvite = $j.map($scope.settingsData.userSettingsDetails, function(user) {
if(user.reInviteChecked){
return user.email;
}
});
if (emailsToReInvite.length >= 1) {
var invitation = { invitees: emailsToReInvite, listId: listId};
invitation = JSON.stringify(invitation);
inviteCollaboratorsModalDataService.reinvite(invitation).then(
function success(response) {
if(response.data.messages[0].code == 214){
$scope.showReinviteSuccess = true;
}
}else{
$scope.showReinviteSuccess = false;
}
}
);
}
}
And my spec file:
describe('settingsModalController', function() {
var controllerUnderTest = "settingsModalController";
var controllerResolver, rootScope, injector, $scope, inviteCollaboratorsModalDataService
beforeEach(function(){
angular.mock.module('sharedListApp');
inject(function($controller, $rootScope, $injector){
controllerResolver = $controller;
rootScope = $rootScope;
injector = $injector;
});
$scope = rootScope.$new();
inviteCollaboratorsModalDataService = injector.get('uiCommon.inviteCollaboratorsModalDataService');
});
it("should send re-invitation mail to selected users successfully", shouldReinviteSelectedUsers);
function shouldReinviteSelectedUsers() {
var $q = injector.get('$q');
$scope.settingsData = {
userSettingsDetails: [{email: 'abc#xyz.com'}]
};
var data = {
messages: [ { code: 214 }],
invite: {
invitation: $scope.settingsData
}
};
var response = { data: data };
var mockResult = new $q.defer();
mockResult.resolve(response);
spyOn(inviteCollaboratorsModalDataService, 'reinvite').and.returnValue(mockResult.promise);
controllerResolver(controllerUnderTest, { $scope: $scope });
$scope.reinvite();
$scope.showReinviteSuccess = true;
$scope.$apply();
expect(inviteCollaboratorsModalDataService.reinvite).toHaveBeenCalled();
expect($scope.showReinviteSuccess).toBe(true);
}
}
What am i doing wrong or what am i missing? Thanks in advance.
Ok, so i missed one variable to add in $scope.settingsData in my spec file which was why my spec code was not parsing into the first 'if' loop of the controller. Got it working by adding 'reInviteChecked: true' in:
$scope.settingsData = {
userSettingsDetails: [{email: 'abc#xyz.com', reInviteChecked: true}]
};

ioredis bluebird a promise was created in a handler but was not returned from it

Can someone please explain to me why i'm getting this warning Warning: a promise was created in a handler but was not returned from it when I execute the following code:
cache['deviceSlave'].getBySystemId(systemId).then(function(slavesMapping) {
// do other stuff
}).catch(function(err) {
// throw error
});
Here is the rest of the code:
var Promise = require('bluebird');
var _ = require('lodash');
var Redis = require('ioredis');
var config = require('/libs/config');
var redis = new Redis({
port: config.get('redis:port'),
host: config.get('redis:host'),
password: config.get('redis:key'),
db: 0
});
var self = this;
module.exports.getBySystemId = function(systemId) {
return new Promise(function(resolve, reject) {
var systemIds = [systemId];
self.getBySystemIds(systemIds).then(function(result) {
return resolve(_.values(result)[0]);
}).catch(function(err) {
return reject(err);
});
});
};
module.exports.getBySystemIds = function(systemIds) {
return new Promise(function(resolve, reject) {
var pipeline = redis.pipeline();
_.each(systemIds, function(systemId) {
var cacheKey = 'device_slaves:' + systemId.replace(/:/g, '');
// get through pipeline for fast retrieval
pipeline.get(cacheKey);
});
pipeline.exec(function(err, results) {
if (err) return reject(err);
else {
var mapping = {};
_.each(systemIds, function(systemId, index) {
var key = systemId;
var slaves = JSON.parse(results[index][1]);
mapping[key] = slaves;
});
return resolve(mapping);
}
});
});
};
I'm using the following libraries: ioredis & bluebird.
The code executes fine and everything just works good! I just dont like the fact I get an warning which I can not solve!
Bluebird is warning you against explicit construction here. Here is how you should write the above code:
module.exports.getBySystemId = function(systemId) {
return self.getBySystemIds([systemId]).then(result => _.values(result)[0]);
};
There is no need to wrap the promise - as promises chain :)

fetch returning promise rather than value

Hopefully the code below communicates the problem clearly. The issue is that in the module which uses the get method of fetchData, the value being returned is the actual Promise, rather than the JSON as desired. Any thoughts on this?
// fetchData.js module
var _ = require('lodash');
function get() {
var endpoint1 = `/endpoint1`;
var endpoint2 = `/endpoint2`;
return fetch(endpoint1)
.then((endpoint1Response) => {
return endpoint1Response.json()
.then((endpoint1JSON) => {
return fetch(endpoint2)
.then((endpoint2Response) => {
return endpoint2Response.json()
.then((endpoint2JSON) => {
var data = _.merge({}, {json1: endpoint1JSON}, {json2: endpoint2JSON});
console.log('data in fetch', data); // this logs the json
return data;
});
});
});
});
}
exports.get = get;
// module which uses get method of fetchData get
var fetchData = require('fetchData');
var data = fetchData.get();
console.log('returned from fetchData', data); // this logs a Promise
Yes, that's exactly what's supposed to happen. The whole point of promises is that their result value is not immediately available and that doesn't change just because you're obtaining one from a separate module.
You can access the value like this:
var fetchData = require('fetchData');
fetchData.get().then(data =>
console.log('returned from fetchData', data);
);
Also note that you are using promises in a non-idiomatic way and creating a "tower of doom." This is much easier on the eyes and accomplishes the same thing:
function fetchJson(endpoint) {
return fetch(endpoint)
.then(endpointResponse => endpointResponse.json());
}
function get() {
var endpoint1 = `/endpoint1`;
var endpoint2 = `/endpoint2`;
return Promise.all([fetchJson(endpoint1), fetchJson(endpoint2)])
.then(responses => {
var data = { json1: responses[0], json2: responses[1] };
console.log('data in fetch', data); // this logs the json
return data;
});
}
Edit I haven't used async/await in JavaScript, but to answer your question, I presume this would work:
async function fetchJson(endpoint) {
var res = await fetch(endpoint);
return res.json();
}
async function get() {
var endpoint1 = `/endpoint1`;
var endpoint2 = `/endpoint2`;
var data = {
json1: await fetchJson(endpoint1),
json2: await fetchJson(endpoint2)
};
console.log('data in fetch', data); // this logs the json
return data;
}
// module which uses get method of fetchData get
async function main() {
var fetchData = require('fetchData');
var data = await fetchData.get();
console.log('returned from fetchData', data);
}
return main();

request hangs on sails error

If you make a request to sails via supertest, the response hangs if you return an error.
Here, we have already lifted sails, and will run this as an integration test against a live db.
var sails = require('sails').lift();
var request = require('supertest');
var app = sails.hooks.http.app;
describe('The creation of a model',function(){
it('should not create a duplicate',function(done){
var user = request.agent(app);
user
.post('/api/create')
.end(function(err,res){
//never gets here, your test will hang
done();
});
});
});
//controller.js
module.exports = {
// /api/create routes here
create:function(req,res){
var params = {
name:"invalid"
};
SomeAction(params,function(err,results){
if (err) {
//the problem is here.
return err;
}
res.json(results);
});
}
};
If you make a supertest request to sails and the function just returns a value, ie. you don't use res.send() or res.json() it will hang the request for supertest. Here's the right way to do it:
var sails = require('sails').lift();
var request = require('supertest');
var app = sails.hooks.http.app;
describe('The creation of a model',function(){
it('should not create a duplicate',function(done){
var user = request.agent(app);
user
.post('/api/create')
.end(function(err,res){
//never gets here, your test will hang
done();
});
});
});
//controller.js
module.exports = {
// /api/create routes here
create:function(req,res){
var params = {
name:"invalid"
};
SomeAction(params,function(err,results){
if (err) {
//you need to send a response to the
res.json({
error:"an error occured"
});
}
res.json(results);
});
}
};

Resources