I'm writing cucumber test and expecting the value true from this function getOutout and doing the assert with the return value but I'm getting the below error:
Error Message:
AssertionError [ERR_ASSERTION]: undefined == true
at processTicksAndRejections (node:internal/process/task_queues:96:5) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: undefined,
expected: true,
operator: '==',
[Symbol(originalCallSite)]: [ CallSite {}, CallSite {} ],
[Symbol(mutatedCallSite)]: [ CallSite {}, CallSite {} ]
step.js
const { Given, When, Then } = require('#cucumber/cucumber');
const AWS = require('aws-sdk');
const assert = require('assert');
let isSuccess;
Then('validate the {string} product', function (productType) {
var params = {
stateMachineArn: 'arn:aws:states:us-west-1:121:stateMachine:test',
maxResults: '25',
nextToken: null,
statusFilter: 'SUCCEEDED'
};
const result = getOutout(params).then(function() {
assert.equal(result, true);
})
});
async function getOutout(params) {
const stepfunctions = new AWS.StepFunctions({
region: process.env.AWS_REGION
});
try {
isSuccess = await listExecute(params, stepfunctions);
console.log(isSuccess, 'Output')
} catch (e) {
console.log(e)
}
}
const listExecute = function (params, stepfunctions) {
return new Promise((resolve, reject) => {
stepfunctions.listExecutions(params, function (err, data) {
if (err) reject(err);
else
data.executions.forEach(function (result) {
let params = {
executionArn: result.executionArn
};
stepfunctions.describeExecution(params, function (err, data) {
if (err) reject(err);
else {
resolve(true)
}
});
});
});
})
}
Any help would be much appreciate to fix the issue. Thanks in advance!
not sure why you did this, but it is bad asynchronous code
const result = getOutout(params).then(function() {
})
assert.equal(result, true);
you need to put the assert within the promise callback:
getOutout(params).then(function(result) {
assert.equal(result, true);
})
Related
Need to test AWS SES using jest unit testing
But showing different errors, also tried solutions in another stackoverflow question
The actual code to be tested is :
public async sentMail(params: ObjectLiteral) {
// Create the promise and SES service object
const sendPromise = new AWS.SES({ apiVersion: '2010-12-01' })
.sendEmail(params)
.promise();
// Handle promise's fulfilled/rejected states
sendPromise
.then(function (data) {
console.log(data.MessageId);
})
.catch(function (err) {
console.error(err, err.stack);
});
the test file is :
import SES from 'aws-sdk/clients/ses';
const mSES = {
sendEmail: jest.fn().mockReturnThis(),
promise: jest.fn().mockReturnThis(),
catch: jest.fn(),
};
jest.mock('aws-sdk/clients/ses', () => {
return jest.fn(() => mSES);
});
it('should send aws sesemail', async () => {
// const spy = jest.spyOn(AWS, 'SES');
const mSes = new SES() as unknown as {
sendEmail: jest.Mock;
promise: jest.Mock;
};
mSes
.sendEmail()
.promise.mockRejectedValueOnce(new Error('This is an SES error'));
const res = await sesMailService.sentMail(
{
toName: 'Name',
webUrl: 'webUrl',
},
);
expect(mSes.sendEmail).toBeCalledWith({
toName: 'Name',
webUrl: 'webUrl',
}
);
});
Its giving error message as:
TypeError: mSes.sendEmail(...).promise.mockRejectedValueOnce is not a function
94 | mSes
95 | .sendEmail()
> 96 | .promise.mockRejectedValueOnce(new Error('This is an SES error'));
Actually this mock works, the reason behind the error is that , i didn't mocked the AWS.Credentials.
jest.mock('aws-sdk', () => {
return {
config: {
update() {
return {};
},
},
SES: jest.fn(() => {
return {
sendEmail: jest.fn((param) => {
return {
promise: () =>
new Promise((resolve, reject) => {
param?.Destination.ToAddresses[0] !== 'invalidmail#gmail.com'
? resolve({
MessageId: data.MessageId,
})
: reject(err);
}),
};
}),
};
}),
Credentials: jest.fn(),
};
});
I'm following this http://siglerdev.us/blog/2021/02/26/google-home-message-broadcast-system-node-js/31 which uses this library castv2-client to send messages to my google home. It works. I get the messages no problem, but the code throws
C:\Users\Phil\Documents\google home\node_modules\castv2-client\lib\controllers\receiver.js:72
callback(null, response.status.volume);
^
TypeError: callback is not a function
at C:\Users\Phil\Documents\google home\node_modules\castv2-client\lib\controllers\receiver.js:72:5 ver.js:72
at fn.onmessage (C:\Users\Phil\Documents\google home\node_modules\castv2-client\lib\controllers\request-response.js:27:7)
at fn.emit (events.js:203:15)
at Channel.onmessage (C:\Users\Phil\Documents\google home\node_modules\castv2-client\lib\controllers\controller.js:16:10) s\receiver.js:72:5
at Channel.emit (events.js:198:13) lib\controllers\request-response.js:27:7)
at Client.onmessage (C:\Users\Phil\Documents\google home\node_modules\castv2\lib\channel.js:23:10) ient\lib\controllers\controller.js:16:10)
at Client.emit (events.js:203:15)
at PacketStreamWrapper.onpacket (C:\Users\Phil\Documents\google home\node_module\channel.js:23:10)s\castv2\lib\client.js:81:10)
at PacketStreamWrapper.emit (events.js:198:13) s\castv2\lib\client.js:81:10)
at TLSSocket.<anonymous> (C:\Users\Phil\Documents\google home\node_modules\castv2\lib\packet-stream-wrapper.js:28:16)
What's wrong with the code that is throwing this AND/OR how can I fix it so it's either more graceful in catching error and doesn't throw since the message still delivers to google home or fix it to not throw this at all?
I appreciate any help!
I believe it's here in the castv2-client library that it's referencing, but I haven't been able to make it happy.
ReceiverController.prototype.launch = function(appId, callback) {
this.request({ type: 'LAUNCH', appId: appId }, function(err, response) {
if(err) return callback(err);
if(response.type === 'LAUNCH_ERROR') {
return callback(new Error('Launch failed. Reason: ' + response.reason));
}
callback(null, response.status.applications || []);
});
};
my code
var Client = require('castv2-client').Client;
var DefaultMediaReceiver = require('castv2-client').DefaultMediaReceiver;
const googleTTS = require('google-tts-api');
var App = {
playin: false,
DeviceIp: "",
Player: null,
GoogleHome: function (host, url) {
var client = new Client();
client.connect(host, function () {
client.launch(DefaultMediaReceiver, function (err, player) {
client.setVolume({ level: 1 });
var media = {
contentId: url,
contentType: 'audio/mp3',
streamType: 'BUFFERED'
};
App.Player = player;
App.Player.load(media, { autoplay: true }, function (err, status) {
App.Player.on('status', function (status) {
if (status.playerState === "IDLE" && App.playin === false) {
client.close();
}
});
});
});
});
client.on('error', function (err) {
console.log('Error: %s', err.message);
client.close();
});
},
run: function (ip, text) {
App.DeviceIp = ip;
const url = googleTTS.getAudioUrl(text, {
lang: 'en-US',
slow: false,
host: 'https://translate.google.com',
});
App.GoogleHome(App.DeviceIp, url, function (res) {
console.log(res);
})
},
broadcast: function(text){
const ips = '192.168.0.15'.split(","); //From config, 192.168.68.105,192.168.68.107,192.168.68.124
for (var s of ips){
App.run(s, text);
}
}
}
App.broadcast("Broadcasted to all of the devices"); //Only works if you did step 4.5
The error you reported:
C:\Users\Phil\Documents\google home\node_modules\castv2-client\lib\controllers\receiver.js:72
callback(null, response.status.volume);
^
TypeError: callback is not a function
at C:\Users\Phil\Documents\google home\node_modules\castv2-client\lib\controllers\receiver.js:72:5
seems to be related to the invocation of the method setVolume in your client:
client.setVolume({ level: 1 });
Please, consider review the source code of receiver.js in the castv2-client library:
ReceiverController.prototype.setVolume = function(options, callback) {
var data = {
type: 'SET_VOLUME',
volume: options // either `{ level: 0.5 }` or `{ muted: true }`
};
this.request(data, function(err, response) {
if(err) return callback(err);
callback(null, response.status.volume);
});
};
The library is claiming because you aren't providing a proper callback when invoking that function.
I have never used the library but probably providing something similar to the following callback could be of help:
client.setVolume({ level: 1 }, function(err, volume) {
if (err) {
// Handle error as appropriate
console.log('Error on setVolume:', err);
} else {
console.log('Volume:', volume)
}
});
Your final code would look like this:
var Client = require('castv2-client').Client;
var DefaultMediaReceiver = require('castv2-client').DefaultMediaReceiver;
const googleTTS = require('google-tts-api');
var App = {
playin: false,
DeviceIp: "",
Player: null,
GoogleHome: function (host, url) {
var client = new Client();
client.connect(host, function () {
client.launch(DefaultMediaReceiver, function (err, player) {
client.setVolume({ level: 1 }, function(err, volume) {
if (err) {
// Handle error as appropriate
console.log('Error on setVolume:', err);
} else {
console.log('Volume:', volume)
}
});
var media = {
contentId: url,
contentType: 'audio/mp3',
streamType: 'BUFFERED'
};
App.Player = player;
App.Player.load(media, { autoplay: true }, function (err, status) {
App.Player.on('status', function (status) {
if (status.playerState === "IDLE" && App.playin === false) {
client.close();
}
});
});
});
});
client.on('error', function (err) {
console.log('Error: %s', err.message);
client.close();
});
},
run: function (ip, text) {
App.DeviceIp = ip;
const url = googleTTS.getAudioUrl(text, {
lang: 'en-US',
slow: false,
host: 'https://translate.google.com',
});
App.GoogleHome(App.DeviceIp, url, function (res) {
console.log(res);
})
},
broadcast: function(text){
const ips = '192.168.0.15'.split(","); //From config, 192.168.68.105,192.168.68.107,192.168.68.124
for (var s of ips){
App.run(s, text);
}
}
}
App.broadcast("Broadcasted to all of the devices"); //Only works if you did step 4.5
I'm currently writing unit tests for an API. One of the helper functions is the async.waterfall to avoid "callback hell". I made this method async and await where the method is called.
The test is exiting saying Number of calls: 0 in Node.js. I'm using Jest as my test framework.
Method being tested:
outletInfoController.fetchAllOutletsByUserIdAndCityId = async function (req, res) {
var userId = req.body.userId;
var cityId = req.body.cityId;
await outletInfoHelper.fetchAllOutletsByUserIdAndCityId(userId, cityId, function (error, result) {
if (error || !result) {
responseUtils.buildAndRespond(commonConst.CODE.FAILURE_RESPONSE_CODE, commonConst.MESSAGE.FAILURE_RESPONSE_MESSAGE,
error, null, res);
} else {
responseUtils.buildAndRespond(commonConst.CODE.SUCCESS_RESPONSE_CODE, commonConst.MESSAGE.SUCCESS_RESPONSE_MESSAGE,
null, result, res);
}
});
};
Method which is using async.waterfall:
outletInfoHelper.fetchAllOutletsByUserIdAndCityId = async function (userId, cityId, outerCallback) {
async.waterfall(
[function getRestaurantsByUserId(callback) {
//do something
}, function getRestaurantsDetails(restIds, callback) {
//do something
}, function getRestaurantsUserCount(restaurants, callback) {
//do something
}], function (error, results) {
//do something
});
}
Test code snippet:
test("outletInfo_success_response", async () => {
let req = {
body: {
userId: 8881,
cityId: 211
}
};
let restIds = [];
await factory.restaurantListFactory((result) => {
restIds = result;
});
await fetchUserRestaurantObjFromUserId.mockImplementation((userId, cb) => {
cb(null, restIds);
});
let mockResponse = [];
await factory.restaurantObjectsFactory(function (result) {
mockResponse = result;
});
let successResp = {
statusMessage: 'success',
data: mockResponse,
errorMessage: null,
statusCode: 0
}
await fetchRestaurantsFromIdsAndCityId.mockImplementation((restIds, cityId, cb) => {
cb(null, mockResponse);
});
let response;
await factory.getUserRestMap((result) => {
response = result;
});
await fetchUsersFromOutletId.mockImplementation((restIds, cb) => {
cb(null, response);
});
let res = await getResponseObj();
await outletInfoController.fetchAllOutletsByUserIdAndCityId(req, res);
expect(res.send).toBeCalledWith(successResp);
});
I'm new to NodeJS so please apologize if below code is not up-to the standard. I would like to access isSuccess value outside of this function stepfunctions.listExecutions
I tried below code but I'm getting the value is undefined not getting the expected output. I did some internet search and came to know in NodeJS we can't set the value which is defined in globally but I've use case and I'm pretty sure this is a common case for others too - where I would like to access this isSuccess value after my execution.
const AWS = require('aws-sdk');
const stepfunctions = new AWS.StepFunctions({
region: process.env.AWS_REGION
});
var params = {
stateMachineArn: 'arn:aws:states:us-west-1:121:stateMachine:test',
maxResults: '2',
nextToken: null,
statusFilter: 'SUCCEEDED'
};
var isSuccess
stepfunctions.listExecutions(params, function (err, data) {
if (err) console.log(err, err.stack);
else
data.executions.forEach(function (result) {
let params = {
executionArn: result.executionArn
};
stepfunctions.describeExecution(params, function (err, data) {
if (err) console.log(err, err.stack);
else {
isSuccess = 'true'
}
});
});
console.log('isSuccess: ' +isSuccess)
});
Expected output:
isSuccess: true
But I'm getting
isSuccess: undefined
Could you please help me to resolve this issue. Appreciated your help and support on this.
This is how you can wrap it on promise
let isSuccess;
const listExecute = function(params) {
return new Promise((resolve, reject) => {
stepfunctions.listExecutions(params, function (err, data) {
if (err) reject(err);
else
data.executions.forEach(function (result) {
let params = {
executionArn: result.executionArn
};
stepfunctions.describeExecution(params, function (err, data) {
if (err) reject(err);
else {
resolve(true)
}
});
});
});
})
}
async function getOutout(params) {
try {
isSuccess = await listExecute(params);
console.log(isSuccess, 'Output')
} catch(e) {
console.log(e)
}
}
getOutout(params)
Also you can export the listExecute so that you can use this function outside of this file.
module.exports = {listExecute}
I was trying to stub an arrow function removeUserEntry, but when executing acctRmRouter in the test, my stub seems being ignored. I have to explicitly stub the UserModel's deleteOne method to get the test successfully, I am wondering why the ignorance happens, thank you
acctRoute.js
const removeUserEntry = (username) => {
const condition = {username: username};
return UserModel.deleteOne(condition)
.then((res) => {
if (res.n < 1) {
throw new Error('User not exists');
}
return true;
}, (err) => {throw err})
.catch(err => err);
};
const acctRmRouter = function(httpReq, httpRes, next) {
if (!argValidate(httpReq.body, 'string')) {
httpRes.locals = {api: { success: false }};
// return to avoid running downwards
return next(new Error('bad argument'));
}
// perform DB rm user
return removeUserEntry(httpReq.body).then((res) => {
if (res === true) {
httpRes.locals = {api: { success: true }};
next();
} else {
httpRes.locals = {api: { success: false }}
next(res);
}
});
};
acctRoute.spec.js
it('should remove user handler pass success request', async () => {
shouldDbReset = false;
const mockRequestURL = "/api/account/rm-user";
const mockRequest = httpMocks.createRequest({
method: "POST",
url: mockRequestURL,
headers: {
"Content-Type": "text/plain"
},
body: 'validRmUser',
});
const mockResponse = httpMocks.createResponse();
const spyNext = sinon.spy();
const stubRemoveUserEntry = sinon.stub(accountRouterHelper, 'removeUserEntry');
stubRemoveUserEntry.callsFake(function(){
return Promise.resolve(true);
}); // Expecting this function to be stubbed, and always return true
await accountRouterHelper.acctRmRouter(mockRequest, mockResponse, spyNext);
/* But when running the function, it returns error object with "User not exists"
which is not what intended */
const firstCallArgs = spyNext.getCall(0).args[0];
expect(spyNext.called).to.be.true;
console.log(`firstCallArgs: ${firstCallArgs}`)
expect(firstCallArgs instanceof Error).to.be.false;
expect(spyNext.args[0].length).to.equal(0);
expect(mockResponse.statusCode).to.equal(200);
expect(mockResponse.locals.api.success).to.be.true;
stubRemoveUserEntry.resetHistory();
stubRemoveUserEntry.restore();
});
The following indeed stubbed successfully with similar pattern to removeUserEntry.
acctRoute.js
const createUserEntry = (userData) => {
const updatedUserData = filterInput(userData);
const userDoc = new UserModel(updatedUserData);
return userDoc.save()
.then((userObj) => userObj._doc
,(err) => { throw err;})
.catch(err => err);
};
const acctCreateRouter = function (httpReq, httpRes, next) {
// do something in mongodb
return createUserEntry(userCondition)
.then((response) => {
if (!(response instanceof Error)) {
httpRes.locals = {api: { success: true}};
next();
} else {
httpRes.locals = {api: { success: false}};
next(response);
}
}, (err) => {
httpRes.locals = {api: { success: false}};
next(err);
})
.catch((err) => {
httpRes.locals = {api: { success: false}};
next(err);
});
};
const acctOutputRouter = function(req, res, next) {
if (res.locals) {
res.send(res.locals.api);
} else {next()}
};
acctRoute.spec.js
it("should return and save the success result to response locals for next route", () => {
shouldDbReset = false;
const mockResponse = httpMocks.createResponse();
const stubCreateUserEntry = sinon.stub(accountRouterHelper, 'createUserEntry');
const mockNext = sinon.spy();
stubCreateUserEntry.callsFake(function(){
return Promise.resolve();
}); // Unlike removeUserEntry, stubbing neatly with desired output
return accountRouterHelper.acctCreateRouter(mockRequest, mockResponse, mockNext)
.then(() => {
expect(mockNext.called).to.be.true;
expect(mockResponse.locals.api.success).to.be.true;
})
.finally(() => {
mockNext.resetHistory();
stubCreateUserEntry.restore();
});
});
Issue
sinon.stub(accountRouterHelper, 'removeUserEntry') replaces the module export.
acctRmRouter() is not calling the module export, it is calling removeUserEntry() directly so stubbing the module export does nothing.
Solution
Refactor acctRmRouter() to call the module export for removeUserEntry().
ES6
// import module into itself
import * as self from './acctRoute';
...
const acctRmRouter = function(httpReq, httpRes, next) {
...
// call the function using the module
return self.removeUserEntry(httpReq.body).then((res) => {
...
Node.js module
...
const acctRmRouter = function(httpReq, httpRes, next) {
...
// call the function using module.exports
return module.exports.removeUserEntry(httpReq.body).then((res) => {
...