Stub out node-vault import - javascript

How can the 'node-vault' module be stubbed out using Sinon.JS?
Here is the sample code to be tested:
const NodeVault = require('node-vault');
class BearerAuth {
getVaultConfig (token) {
return {
apiVersion: 'v1',
endpoint: app.get('vault'),
token: token
};
}
verify (token) {
const vault = NodeVault(this.getVaultConfig(token));
const resp = vault.tokenLookupSelf().then((result) => {
console.log(`Vault token: ${token}, successfully authenticated.`);
return Promise.resolve({
'name': result.data.meta.username
});
}).catch((err) => {
console.error(err.message);
return Promise.reject(new this.ApplicationError.Authentication('Bearer token is incorrect.'));
});
return resp;
}
}
module.exports = BearerAuth;
}
The test code which attempts to stub out the module using sinon.stub:
const assert = require('assert');
const sinon = require('sinon');
const nodeVault = require('node-vault');
const BearerAuth = require('bearer');
describe('Bearer AuthConfig', () => {
beforeEach(async () => {
class TestBearerAuth extends BearerAuth {
// override some other methods
}
testAuth = new TestBearerAuth();
const vaultConfig = {
endpoint: 'http://localhost:8200',
token: '123'
};
testAuth.getVaultConfig = sinon.stub.returns(vaultConfig);
});
it('returns user when valid token', async () => {
const user = await testAuth.verify(null, 'mytoken');
assert.deepEqual(user, {name: 'sre'});
})
}
This fails when run with this error:
1) Bearer Auth
returns user when valid token:
TypeError: Attempted to wrap undefined property undefined as function
at wrapMethod (node_modules/sinon/lib/sinon/util/core/wrap-method.js:70:21)
at TestBearerAuth.stub [as getVaultConfig] (node_modules/sinon/lib/sinon/stub.js:65:44)
at TestBearerAuthConfig.verify (src/modules/auth/bearer.js:34:34)
at Context.<anonymous> (test/unit/modules/auth/test-bearer.js:57:39)
at processTicksAndRejections (internal/process/task_queues.js:97:5)
The node-vault library has the following export in index.d.ts:
declare function NodeVault(options?: NodeVault.VaultOptions): NodeVault.client;
export = NodeVault;

The unit test code from the node-vault library provided the solution:
Test code:
const assert = require('assert');
const sinon = require('sinon');
const nodeVault = require('node-vault');
const BearerAuth = require('bearer');
describe('Bearer AuthConfig', () => {
beforeEach(async () => {
request = sinon.stub();
response = sinon.stub();
response.statusCode = 200;
response.body = {
data: {
meta: {
username: 'jack'
}
}
};
request.returns({
then (fn) {
return fn(response);
},
catch (fn) {
return fn();
}
});
class TestBearerAuth extends BearerAuth {
getVaultConfig (token) {
return {
endpoint: 'http://localhost:8200',
token: '123',
namespace: 'test',
'request-promise': {
defaults: () => request // dependency injection of stub
}
};
}
}
testAuth = new TestBearerAuth();
});
it('returns user when valid token', async () => {
const user = await testAuth.verify(null, 'mytoken');
assert.deepEqual(user, {name: 'sre'});
})
}
Adding this here in case someone else runs into a similar issue.

Related

Proxyquire not calling inner functions (npm modules) and does not work with classes properly

Where am i going wrong here?
Using mocha, chai, sinon and proxyquire for an express server and sequelize ORM linked with a postgres database
I am trying to test a login controller route from my express server
Before I show the file which I want to run my test on here is what "../services/authService.js" file looks like
../services/authService
const UserService = require("./userService");
module.exports = class AuthService extends UserService {
};
// so UserService will have the method findByEmail
// UserService class looks like this and it is coming from another file require("./userService.js) as stated above
/*
class UserService {
async findByEmail(email) {
try {
const user = await User.findOne({ where: { email: email }});
if (user) {
return user;
}
throw new Error("User not found");
} catch (err) {
err.code = 404;
throw err
}
}
}
*/
And here is the auth-controller.js file which I want to run the test on
auth-controller.js
const bcrypt = require('bcryptjs');
const AuthService = require("../services/authService"); // is a class which extends from another calls see the code above
const authService = new AuthService();
const jwtGenerator = require('../utils/jwtGenerator');
const createError = require("http-errors");
exports.loginRoute = async (req, res, next) => {
try {
req.body.password = String(req.body.password);
// db query trying to force a sinon.stub to resolve a fake value. But code wont pass here hence 500 error
const userQuery = await authService.findByEmail(req.body.email);
const compare = await bcrypt.compare(req.body.password, userQuery.password);
if (!compare) throw createError(401, 'Incorrect password.');
const user = {
id: userQuery.id, role: userQuery.is_admin ? "Administrator" : "User", email: userQuery.email, Authorized: true
}
const token = jwtGenerator(user);
return res
.cookie("access_token", token, {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
}).status(200).json({ message: "Logged in successfully 😊 👌", user, token });
} catch (error) {
next(error);
}
}
This code works in production but I cannot seem to test it. I used proxyquire to require the modules that the function uses. I have a big problem in making proxyquire work when it comes to my class AuthService here is my test file. As proxyquire is not working with classes some how. proxyquire is not using make AuthServiceMock at all cant figure out why.
First of these are my helper variables which I will use in the test file
../test-utils/user-helper
const createAccessToken = (payload) => jwt.sign(payload, TOKEN, {expiresIn: "1h"});
let loginDetail = {
email: "admin#test.com",
password: "123456"
};
let loginAdminUser = {
id: 1,
email: "admin#test.com",
password: "123456",
is_admin: true
}
const loginUser = {
id: 1,
email: "admin#test.com",
password: "123456",
is_admin: true
}
const adminUser = {
id: 1,
email: 'admin#test.com',
password: '123456',
is_admin: true,
first_name: 'john',
last_name: 'doe',
created_at: "2020-06-26T09:31:36.630Z",
updated_at: "2020-06-26T09:31:49.627Z"
}
module.exports = {
createAccessToken,
loginDetail,
loginAdminUser,
loginUser,
adminUser
}
And here is the test file I placed comments espcially around proxyquire when I am trying to use it as this is giving me some issues when it comes to using it with classes. And as well it is not calling mocked/stubbed npm modules for some reason
auth-controller.spec.js
"use strict";
const _ = require("lodash");
const path = require("path");
const proxyquire = require("proxyquire").noCallThru().noPreserveCache();
const chai = require("chai");
const { expect } = chai;
const sinon = require("sinon");
const sinonChai = require("sinon-chai");
chai.use(sinonChai);
// const AuthServiceOriginalClass = require("../../services/authService"); If i use this directly in proxyquire it calls the original class
const { createAccessToken, loginDetail, loginAdminUser, loginUser, adminUser } = require("../test-utils/user-helper");
const controllerPath = path.resolve('./controllers/authController.js');
describe("login route", () => {
let proxy, authService, bcryptStub, fakeCallback, fakeReq, fakeRes, fakeNext, resolveFn, token;
let result, bcryptStubbing, response;
class UserServiceMock {
async findByEmail(email) {
try {
if (email) {
return loginAdminUser;
}
} catch (error) {
throw error;
}
}
}
class AuthServiceMock extends UserServiceMock {};
bcryptStub = {
compare: function() { return true }
};
let tokeen = (kk) => {
return createAccessToken(kk);
}
// token = sinon.mock(createAccessToken(loginAdminUser)); // ?? which 1 to use?
token = sinon.spy(createAccessToken); // ?? which 1 to use?
// token = sinon.stub(createAccessToken) ?? which 1 to use?
proxy = proxyquire(controllerPath, {
"../services/authService.js": AuthServiceMock, // seems like this is not called at all
// "../services/authService.js": AuthServiceOriginalClass, // commented out if use this instead it calls the original class instant
"bcryptjs": bcryptStub,
"../utils/jwtGenerator": token,
// "#noCallThru": true // keep on or off?
});
before("Stub my methods", () => {
authService = new AuthServiceMock();
// If I call the entire loginRoute I want this stub authTry to be called inside of it and resolve that object value
authTry = sinon.stub(authService, "findByEmail").withArgs(loginDetail.email).resolves(loginAdminUser);
sinon.stub(bcryptStub, "compare").resolves(true); // force it to return true as that seems to be like the code of authController.js
// sinon.stub(token, "createAccessToken")
});
before("call the function loginRoute", async () => {
// fakeCallback = new Promise((res, rej) => {
// resolveFn = res
// });
fakeReq = {
body: {
email: loginDetail.email,
password: loginDetail.password
}
};
fakeRes = {
cookie: sinon.spy(),
status: sinon.spy(),
json: sinon.spy()
}
fakeNext = sinon.stub();
await proxy.loginRoute(fakeReq, fakeReq, fakeNext).then((_result) => {
result = _result;
});
console.log("result")
console.log(result) // undefined
console.log("result")
});
it("login route test if the stubs are called", async () => {
expect(authService.findByEmail).to.have.been.called // never called
// expect(bcryptStubbing).to.have.been.called // never called
// expect(response.status).to.deep.equal(200); // doesn't work
}).timeout(10000);
after(() => {
sinon.reset()
});
});
Where am i going wrong here in the test?

mock dynamodb partiql using jest

I didn't find a way to mock the new feature of dynamodb "PartiQL" defined in a lambda function.
In PartiQL we can use executeStatement to run sql like queries which are a kind of DynamoDBCustomizations.
But he configuration below return an error:
UnrecognizedClientException: The security token included in the
request is invalid
which means that the configuration of jest-mock is not correct.
This is the code to be tested
'use strict';
var ddb = new AWS.DynamoDB({ apiVersion: '2012-08-10' });
const TABLE_NAME = process.env.TABLE_NAME;
async function queryTest() {
let params = {
Statement: `select * from ${TABLE_NAME}`
};
let statementResult = await ddb.executeStatement(params).promise();
return statementResult.Items;
}
module.exports.get = async (event) => {
var result = queryTest();
if (!result) {
return {
statusCode: 200,
body: JSON.stringify(result)
};
}
else {
return {
statusCode: 400,
body: JSON.stringify(result)
};
}
};
and the testing script:
'use strict';
const AWS = require('aws-sdk');
const jestPlugin = require('serverless-jest-plugin');
const lambdaWrapper = jestPlugin.lambdaWrapper;
const mod = require('./handler');
function mock_aws() {
let mockDynamoDBClient = {
executeStatement: {
promise: jest.fn()
},
promise: jest.fn(),
};
jest.mock("aws-sdk", () => {
const config = {
update: jest.fn(),
};
return {
DynamoDB: jest.fn().mockImplementation(() => {
return {
executeStatement: () => mockDynamoDBClient.executeStatement,
};
}),
config: config
};
});
return mockDynamoDBClient;
}
describe('test', () => {
const OLD_ENV = process.env;
var mDynamoDb;
var wrapped;
beforeAll(async () => {
jest.resetModules();
wrapped = await lambdaWrapper.wrap(mod, { handler: 'get' });
process.env = { ...OLD_ENV };
process.env.TABLE_NAME = "TEST";
mDynamoDb = mock_aws();
});
test('implement tests here', async (done) => {
const mResult = [{ 'TEST': 1 }] ;
const response = await wrapped.run({})
expect(response).toEqual({
"statusCode": 200,
"body": JSON.stringify(mResult)
});
done();
});
});

How to mock Cognito response in Typescript

After defining class CognitoPool saving it as cognitoPool.ts script:
const AWS = require('aws-sdk');
import { CognitoIdentityServiceProvider } from 'aws-sdk';
import {ListUsersRequest, ListUsersResponse} from 'aws-sdk/clients/cognitoidentityserviceprovider';
export class CognitoPool {
private identityService: CognitoIdentityServiceProvider;
constructor(identityService: CognitoIdentityServiceProvider) {
this.identityService = identityService;
}
async listCognitoUsers(poolID: string, sub: string): Promise<ListUsersResponse> {
let params = {
UserPoolId: poolID,
Filter: `sub="${sub}"`
} as ListUsersRequest;
let res: ListUsersResponse = await this.identityService.listUsers(params).promise();
return res;
}
}
export default new CognitoPool(new AWS.CognitoIdentityServiceProvider());
I go ahead and write a test script:
const AWS = require('aws-sdk');
import sinon, { stubObject } from 'ts-sinon'
import { CognitoIdentityServiceProvider, AWSError } from 'aws-sdk';
import { PromiseResult } from 'aws-sdk/lib/request';
import { CognitoPool } from './cognitoPool';
describe('Testing', () => {
const identityService = new AWS.CognitoIdentityServiceProvider();
const stub = stubObject(identityService);
const cognitoPool = new CognitoPool(stub);
it('Test 01', async () => {
let mockData = {
Users: []
} as unknown as PromiseResult<any, AWSError>;
stub.listUsers.returns(mockData);
let result = await cognitoPool.listCognitoUsers('poolId-123', 'sub-123');
})
})
A mockData is to be returned by identityService.listUsers() as as PromiseResult:
let mockData = {
Users: []
} as unknown as PromiseResult<any, AWSError>;
But a test script runs with an error:
TypeError: this.identityService.listUsers(...).promise is not a function
Is there a way to avoid this error?
PromiseResult is an object that includes .promise as a function. Then, when you want to mock a function to return a PromiseResult, the mock data should be an object like PromiseResult.
In your case, mockData should be:
const mockData = {
promise: () => Promise.resolve({ Users: [] }),
} as unknown as PromiseResult<any, AWSError>;

Apollo GraphQL - TypeError: Cannot read property 'findOrCreateUser' of undefined

I'm following this tutorial https://www.apollographql.com/docs/tutorial/mutation-resolvers/#book-trips
and have ended up getting this error:
"TypeError: Cannot read property 'findOrCreateUser' of undefined",
" at login
Below is the function I use to call login:
mutation LoginUser {
login(email: "daisy#apollographql.com") {
token
}
}
The file below is where findOrCreateUser is called:
src/resolvers.js
module.exports = {
Mutation: {
login: async (_, {email}, {dataSources}) => {
const user = await dataSources.userAPI.findOrCreateUser({ email });
if (user) {
user.token = Buffer.from(email).toString('base64');
return user;
}
},
},
This is where dataSources is defined:
src/index.js
require('dotenv').config();
const { ApolloServer } = require('apollo-server');
const typeDefs = require('./schema');
const { createStore } = require('./utils');
const resolvers = require('./resolvers');
const isEmail = require('isemail');
const LaunchAPI = require('./datasources/launch');
const UserAPI = require('./datasources/user');
const store = createStore();
const server = new ApolloServer({
context: async ({req}) => {
const auth = req.headers && req.headers.authorization || '';
const email = Buffer.from(auth, 'base64').toString('ascii');
if (!isEmail.validate(email)) return {user: null};
// find a user by their email
const users = await store.user.findOrCreate({ where: { email } });
const user = users && users[0] || null;
return { user: {...user.dataValues } };
},
dataSources: () => ({
launchAPI: new LaunchAPI(),
UserAPI: new UserAPI({store})
}),
typeDefs,
resolvers,
});
server.listen().then(() => {
console.log(`
Server is running!
Listening on port 4000
Explore at https://studio.apollographql.com/dev
`)
});
As xadm mentioned in the comment the issues is your usage of userAPI vs UserAPI. the datasource object is created in index.js in the following block
dataSources: () => ({
launchAPI: new LaunchAPI(),
UserAPI: new UserAPI({store})
}),
here you defined datasources.UserAPI however in resolvers.js you refer to it as datasources.userAPI (note the difference in capitalization of userAPI). Your problem can be resolved by changing the above code block to
dataSources: () => ({
launchAPI: new LaunchAPI(),
userAPI: new UserAPI({store})
}),

Stub dependent function with sinon and proxyquire does not print stub result

I wrote a unit test for _getuser() but I don't see console.log print stub result.Also test coverage shows line 'let user = result.user;
console.log('User'+JSON.stringify(result));' is uncovered.Why stub result does not print in console log in getUser() function in LogInCommand class.
I see result shows undefined in unit test.
// userApi.js
'use strict';
const config = require('../../config/config');
const api = require('./apiService');
class UserApi {
constructor() {
}
getUser(userId) {
return api.get({
url: config.url,
qs: {
includeInactive: true,
id: userId,
startIndex: 0,
maxResults: 1
},
headers: {
Accept: 'application/json;',
'Connection': 'Keep-Alive'
}
});
}
}
module.exports = UserApi;
// LoginCommand.js
'use restrict';
const userApi = require('../../api/userApi');
class LogInCommand {
constructor() {
}
async _getUser(userId) {
let result = await new userApi().getUser(userId);
let user = result.user;
console.log('User'+JSON.stringify(result));
return user;
}
}
module.exports = LogInCommand;
//LoginCommand.test.js
describe('Test LogInCommand Class',function(){
it.only('_getUser function should return user', async ()=> {
let apiData= {
user:'abc'
};
let logincmd = proxyquire('../LogInCommand.js', {
userApi : { getUser : Promise.resolve(apiData) },'#noCallThru':true});
let obj= new logincmd();
let result= await obj._getUser(client);
});
});
The proxyquire configuration is incorrect in your current setup. Proxyquire maps the string value passed in a require call to the desired mock/stub values. Try the following instead:
let logincmd = proxyquire('../LogInCommand.js', {
'../../api/userApi' : { getUser : Promise.resolve(apiData) },
'#noCallThru':true
});
Below code worked for me
// userApi.js
'use strict';
const config = require('../../config/config');
const api = require('./apiService');
class UserApi {
constructor() {
}
getUser(userId) {
return api.get({
url: config.url,
qs: {
includeInactive: true,
id: userId,
startIndex: 0,
maxResults: 1
},
headers: {
Accept: 'application/json;',
'Connection': 'Keep-Alive'
}
});
}
}
module.exports = UserApi;
// LoginCommand.js
'use restrict';
const userApi = require('../../api/userApi');
class LogInCommand {
constructor() {
}
async _getUser(userId) {
let result = await new userApi().getUser(userId);
let user = result.user;
console.log('User'+JSON.stringify(result));
return user;
}
}
module.exports = LogInCommand;
//LoginCommand.test.js
describe('Test LogInCommand Class',function(){
it.only('_getUser function should return user', async ()=> {
class userApiStub{
constructor(){}
getUser() {
return Promise.resolve({
user:4
});
}
}
let logincmd = proxyquire('../LogInCommand.js', {
'../../api/userApi' :userApiStub },'#noCallThru':true});
let obj= new logincmd();
let result= await obj._getUser(client);
});
});

Categories

Resources