How to mock redis client in nodejs using jest - node.js

I have tried to mock the redisClient.js using redis-mock using jest. But I couldn't find the solution for it. please give me a code sample for it. I need to mock it in controller.
redisClient.js
const redis = require('redis');
const asyncRedis = require("async-redis");
//Redis
const connection = redis.createClient(process.env.REDIS_PORT,
{
retry_strategy: function(options) {
if (options.error && options.error.code === "ECONNREFUSED") {
// End reconnecting on a specific error and flush all commands with
// a individual error
return new Error("The server refused the connection");
}
if (options.total_retry_time > 1000 * 60 * 60) {
// End reconnecting after a specific timeout and flush all commands
// with a individual error
return new Error("Retry time exhausted");
}
if (options.attempt > 10) {
// End reconnecting with built in error
return undefined;
}
// reconnect after
return Math.min(options.attempt * 100, 3000);
},
}
);
module.exports = asyncRedis.decorate(connection);
Controller
const logger = require('../../helper/logger');
const response = require("../../config/response");
const constant = require('../../config/constant');
const QuizService = require('../../services/quiz/quizService');
class QuizController {
constructor() {
this.quizService = new QuizService();
}
async getQuiz(req, res) {
const { userId, query: { campaignId } } = req;
try {
const question = await this.quizService.getQuestion(userId, campaignId);
res.send(response.res(true, constant.MSG.Quiz_FETCHED, question));
} catch (error) {
res.status(constant.RESPONSE.INTERNAL_ERROR.CODE)
.send(response.res(false, error.message, null, error.code))
}
}
}
Service
const _ = require('lodash');
const moment = require('moment');
const { Op } = require('sequelize');
const { v4: uuidv4 } = require("uuid");
const shuffle = require('shuffle-array');
const serialize = require("serialize-javascript");
const utill = require("../../helper/util");
const redis = require("../../cache/redisClient");
const constant = require('../../config/constant');
const scoreHelper = require('./../../helper/scoreHelper');
const db = require("../../models");
const Quiz = db.quiz;
const Campaign = db.campaign;
const campaign = require('../campaign/campaignService')
const SubscriberAnswer = require('../subscriberAnswer/subscriberAnswerService')
const SubscriberProgram = require('../subscriberProgram/SubsciberProgramService')
class quizService {
constructor() {
this.subscriberAnswer = new SubscriberAnswer()
this.subscriberProgram = new SubscriberProgram()
this.campaign = new campaign()
}
async getQuestion(userId, campaignId) {
const subscribedProgramData = await this._checkAvailableQuestionLimit(userId, campaignId)
if(!subscribedProgramData){
throw { message: constant.MSG.TRY_AGAIN }
}
if (subscribedProgramData.no_of_questions > 0) {
const question = await Quiz.findQuestion(userId, campaignId);
if (question.length) {
const data = {
subscriber_id: userId,
campaign_id: campaignId,
questions_id: question[0].id
}
const updateData = {
id: subscribedProgramData.id,
no_of_questions: (subscribedProgramData.no_of_questions - 1)
}
await this.subscriberAnswer.create(data);
await this.subscriberProgram.updateQuota(updateData);
const id = uuidv4();
const {answer, ...questionData } = question[0];
const responseData = await this.handleQuestionData(id, userId, campaignId, questionData, answer);
return responseData;
} else {
throw { code:constant.RESPONSE_COEDES.ALL_ANSWERED, message: constant.MSG.ANSWER_ALL }
}
} else {
throw { message: constant.MSG.QUOTA_OVER }
}
}
}
My Unit Testing Code
const QuizService = require("../../src/services/quiz/quizService");
const QuizController = require("../../src/controllers/quiz/quizController");
const quizService = new QuizService();
const quizController = new QuizController();
const httpMocks = require("node-mocks-http");
jest.mock("../../src/helper/logger");
jest.mock("../../src/cache/redisClient.js");
beforeEach(() => {
req = httpMocks.createRequest();
res = httpMocks.createResponse();
next = jest.fn();
jest.resetAllMocks();
quizService.getQuestion = jest.fn();
});
quizService.getQuestion = jest.fn();
const response = {
id: 1,
name: 'Sandun',
msisdn: '94704377575',
otp: '1234',
deleted: 0,
attempts: 0,
img_url: 'https://'
}
// This test shows how the constructor can be mocked, and how to spy on passed parameters.
describe("Test QuizController", () => {
afterEach(() => {
jest.resetAllMocks();
});
//Because getQuestion is prototype method
it("Test - GetQuiz - Success", async () => {
req.query.programId = 1;
req.userId = 1;
jest.spyOn(QuizService.prototype, "getQuestion").mockReturnValue(response);
await quizController.getQuiz(req, res);
expect(res.statusCode).toBe(200);
});
});
ERROR
FAIL test/controllers/quiz.controller.test.js
● Test suite failed to run
TypeError: Cannot read property 'startsWith' of undefined
//Redis
const connection = redis.createClient(process.env.REDIS_PORT,
^
{
retry_strategy: function(options) {
if (options.error && options.error.code === "ECONNREFUSED") {
at normalizeUrl (node_modules/redis-mock/lib/utils/parseRedisUrl.js:4:11)
at Object.<anonymous>.module.exports (node_modules/redis-mock/lib/utils/parseRedisUrl.js:61:34)
at generateUrlOptions (node_modules/redis-mock/lib/client/createClient.js:25:30)
at unifyOptions (node_modules/redis-mock/lib/client/createClient.js:61:10)
at Object.createClient (node_modules/redis-mock/lib/client/createClient.js:64:47)
at Object.<anonymous> (src/cache/redisClient.js:5:26)
at Object.<anonymous> (src/services/quiz/quizService.js:8:15)
at Object.<anonymous> (test/controllers/quiz.controller.test.js:1:21)

Related

Redis not cache all data in ec2 server and data from mongo atlas

I have used redis but its not work as expected. when first time I have execute the find query results comes from mongoose its give approx 19MB data but after caching redis only one document. don't now where I am doing wrong below share the configuration file.
const mongoose = require('mongoose');
const { createClient } = require('redis');
const util = require('util');
let client
(async() => {
client = createClient();
await client.connect({
legacyMode: true
});
await client.ping();
})();
const exec = mongoose.Query.prototype.exec;
mongoose.Query.prototype.cache = function (options = { time: 36000 }) {
this.useCache = true;
this.time = options.time;
this.hashKey = JSON.stringify(options.key || this.mongooseCollection.name);
return this;
};
mongoose.Query.prototype.exec = async function () {
if (!this.useCache) {
return await exec.apply(this, arguments);
}
const key = JSON.stringify({
...this.getQuery(),
});
const cacheValue = await client.get(this.hashKey, key);
console.log(cacheValue, "Cache Value");
if (cacheValue) {
const doc = JSON.parse(cacheValue);
console.log('Response from Redis',doc, this.model);
return Array.isArray(doc) ? doc.map((d) => new this.model(d)) : new this.model(doc);
}
const result = await exec.apply(this, arguments);
client.set(this.hashKey, key, JSON.stringify(result));
client.expire(this.hashKey, this.time);
return result;
};
module.exports = {
clearKey(hashKey) {
client.del(JSON.stringify(hashKey));
},
};
Here is Query
Products.find({is_deleted:false})
.select(select)
.populate('type', 'type')
.populate('shape', 'type')
.populate('category', 'type is_pain_scale name').sort({updated_at:1}).lean().cache({
time: 36000
});

How to handle async/await and promises in node.js confusion

I have checked tons of similiar problems all data, I am so confused. This is returning the same result whether the phone number is in the DB or not. Other versions with async and promises have been crashing the app. Please help
How can I get a value from Firebase realtime DB using the admin SDK and use that value to determine the output. Also it seems abnormally slow at times for some reason
auth.controller.js
const validate = require('../utils/validate');
const { getItem, setItem } = require('../utils/firebase');
const { generatePin } = require('../utils/auth');
const argon2 = require('argon2');
const { error } = require('console');
const { networkInterfaces } = require('os');
const exp = require('constants');
exports.connect = async function (req, res) {
const { phone } = req.body;
if (!phone) {
res.status(400).json({ message: 'Error, phone number is invalid or not registered'})
} else {
if(!validate.phoneNumber(phone)) {
res.status(400).json({ message: 'Error, phone number is invalid or not registered' })
} else {
const result = await generatePin();
item = await setItem('clients', 'phone', phone, 'hash', result.hash)
console.log(item)
if(!item) {
res.status(200).json({ message: 'Success', pin: result.pin})
} else {
res.status(400).json({ message: 'Error, phone number is invalid or not registered' })
}
var currentTime = Date.now();
var expiryTime = currentTime + 60;
setItem('clients', 'phone', phone, 'hashExpiry', expiryTime)
}
}
firebase.js
const { on } = require("events");
var admin = require("firebase-admin");
// Import Admin SDK
const { getDatabase } = require('firebase-admin/database');
const { type } = require("os");
var serviceAccount = require("../fedex-3a42e-firebase-adminsdk-r96f1-7249eaf87b.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://fedex-3a42e-default-rtdb.firebaseio.com/"
});
const db = getDatabase()
function getItem(itemRef, child, val) {
const dbRef = db.ref(itemRef);
dbRef.orderByChild(child).equalTo(val).on("value", (data) => {
return data.val();
});
}
async function setItem(itemRef, child, val, key, pushedVal) {
const value = await getItem(itemRef, child, val);
console.log('val', value)
if(value) {
finallySetItem(value, itemRef, pushedVal);
return true
} else {
return false
}
}
function finallySetItem(data, itemRef, pushedVal) {
console.log(data)
if(data) {
var itemKey = Object.keys(data)[0];
console.log(itemKey)
const dbRef = db.ref(itemRef + '/' + itemKey + '/' + key);
dbRef.set(pushedVal);
}
}
module.exports = { getItem, setItem }
This won't work:
function getItem(itemRef, child, val) {
const dbRef = db.ref(itemRef);
dbRef.orderByChild(child).equalTo(val).on("value", (data) => {
return data.val();
});
}
You're passing your callback to on(), and on() won't do anything with the value you return in there.
More likely you want to use once() and return the value asynchronously from there:
async function getItem(itemRef, child, val) {
const dbRef = db.ref(itemRef);
const data = await dbRef.orderByChild(child).equalTo(val).once("value");
return data.val();
}

Mock node js Controller function - getSubscriber which calling to service

I have developed the node js code as MVC architecture. The folder structure is Controller --> service -> model. And I have tried to write unit testing for the following code. Unfortunately, I couldn't mock the service function. So please help me to resolve it.
Controller
const SubscriberService = require('../../services/subscriber/subscriberService')
const response = require("../../config/response");
const constant = require('../../config/constant');
const SubscriberAnswerService = require('../../services/subscriberAnswer/subscriberAnswerService');
const path = require('path');
class SubscriberController {
constructor() {
this.subscriberService = new SubscriberService();
this.subscriberAnswerService = new SubscriberAnswerService();
}
async getSubscriber(req, res) {
try {
var { userId } = req;
const user = await this.subscriberService.findByUserId(userId);
if (user != null) {
res.send(response.res(true, constant.MSG.USER_DETAILS, user))
} else {
res.status(404).send(response.res(false, constant.MSG.USER_NOT_FOUND));
}
} catch (error) {
res.status(constant.RESPONSE.INTERNAL_ERROR.CODE)
.send(response.res(false, error.message))
}
}
}
Service
async findByUserId(id) {
const user = await Subscriber.findOne({ where: { id: id, status: 1 } });
return user;
}
Unit Testing Code
describe("Test SubscriberController", () => {
it("Test getsubscriber", async () => {
req.userId = 1;
jest.spyOn(subscriberService, "findByUserId").mockReturnValue(subscriberResponse);
await subscriberController.getSubscriber(req, res);
expect(res.statusCode).toBe(500);
});
});
Issue: I have mocked the service function which findByUserId but it does not work. It is given the following error.
error TypeError: Cannot read property 'findOne' of undefined
Please give the solution to mock findByUserId function.
Subscriber.Controller.test.js
const subscriberModel = require("../src/models/subscriber/subscriberModel");
const SubscriberService = require("../src/services/subscriber/subscriberService");
const SubscriberController = require("../src/controllers/Subscriber/subscriberController");
const subscriberController = new SubscriberController();
const subscriberService = new SubscriberService();
const httpMocks = require("node-mocks-http");
jest.mock("../src/models/subscriber/subscriberModel");
beforeEach(() => {
jest.resetAllMocks();
req = httpMocks.createRequest();
res = httpMocks.createResponse();
next = jest.fn();
jest.resetAllMocks();
subscriberModel.findOne = jest.fn();
});
// subscriberService.findByUserId = jest.fn();
const subscriberResponse = {
id: 1,
name: 'Sandun',
msisdn: '94704377575',
otp: '1234',
deleted: 0,
attempts: 0,
img_url: 'https://'
}
jest.mock('../src/models/subscriber/subscriberModel', () => () => {
const SequelizeMock = require("sequelize-mock");
let dbMock = new SequelizeMock();
let subscriberMock = dbMock.define('subscribers', {
id: 1,
name: 'Sandun',
msisdn: '94704377575',
otp: '1234',
deleted: 0,
attempts: 0,
img_url: 'https://'
});
let groupMock = dbMock.define('winner', {});
subscriberMock.belongsTo(groupMock);
subscriberMock.hasMany();
});
// This test shows how the constructor can be mocked, and how to spy on passed parameters.
describe("Test SubscriberController", () => {
it("Test getsubscriber", async () => {
req.userId = 1;
jest.spyOn(subscriberService, "findByUserId").mockReturnValue(subscriberResponse);
await subscriberController.getSubscriber(req, res);
expect(res.statusCode).toBe(200);
});
});

ZKSync is not a constructor

I am trying to write test using a class , but I am getting an error.
Here is the test:
import assert from 'assert'
const ethers = require('ethers');
const zksync = require('zksync');
const ZKSync = require('../../../../app/scripts/controllers/zksync');
describe('zkSync', function () {
let zkSync
before(async () => {
// zkSync = new ZKSync(new Proxy({}, ethers, zksync))
zkSync = new ZKSync(ethers, zksync);
})
describe('initAccount', function () {
it('registers an account on zksync', async () => {
const TEST_SEED = 'debris dizzy just program just float decrease vacant alarm reduce speak stadium'
const ethersProvider = await new ZKSync.getEthereumProvider(ethers,'rinkeby')
const zkSyncProvider = await new ZKSync.getZkSyncProvider('testnet');
const aliceRinkebyWallet = new ethersProvider.Wallet.fromMnemonic(TEST_SEED);
const aliceZKsyncWallet = new ZKSync.initAccount(aliceRinkebyWallet,zkSyncProvider);
assert.strictEqual(await aliceZKsyncWallet.isSigningKeySet(), true, 'account is registered.')
})
})
})
Here is the code it calls:
const ethers = require('ethers')
const zksync = require('zksync')
export default class ZKSync {
constructor (ethers, zksync) {
// const initState = opts.initState || {}
// this.store = new ObservableStore(initState)
// this.keyringController = opts.keyringController
this.ethers = ethers
this.zksync = zksync
}
async getZkSyncProvider (zksync, networkName) {
let zkSyncProvider
try {
zkSyncProvider = await zksync.getDefaultProvider(networkName)
} catch (error) {
console.log('Unable to connect to zkSync.')
console.log(error)
}
return zkSyncProvider
}
async getEthereumProvider (ethers, networkName) {
let ethersProvider
try {
// eslint-disable-next-line new-cap
ethersProvider = new this.ethers.getDefaultProvider(networkName)
} catch (error) {
console.log('Could not connect to Rinkeby')
console.log(error)
}
return ethersProvider
}
async initAccount (rinkebyWallet, zkSyncProvider) {
const zkSyncWallet = await this.zksync.Wallet.fromEthSigner(rinkebyWallet, zkSyncProvider)
return zkSyncWallet
}
}
I run the tests with mocha test/unit/app/controllers/zksync-lib-test.js.
However , I get the following error:
TypeError: ZKSync is not a constructor
I will appreciate any pointers on this.

Getting error while creating session using Mongoose and node.js

I am trying to manage the transnational data using mongoose and node.js but while creating session its throwing the following error.
Error:
TypeError: session.startTransaction is not a function
at DemoProjectService.transferBalance (/home/anil/Desktop/subhrajyoti/project/demo1/service/account.service.js:32:21)
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:189:7)
The below is my service where I am trying to create the session.
async transferBalance(req,res) {
let conn = null;
try{
conn = await connectMasterDb();
if(_.isNull(conn)) {
return false;
}
let from = req.body.from;
let to = req.body.to;
let amount = req.body.amount;
const customerCollection = loadMongoModel('Account');
const session = conn.startSession();
session.startTransaction();
const opts = { session, new: true };
const A = await customerCollection.findOneAndUpdate({ name: from }, { $inc: { balance: -amount } }, opts);
if (A.balance < 0) {
// If A would have negative balance, fail and abort the transaction
// `session.abortTransaction()` will undo the above `findOneAndUpdate()`
throw new Error('Insufficient funds: ' + (A.balance + amount));
}
const B = await customerCollection.findOneAndUpdate({ name: to }, { $inc: { balance: amount } }, opts);
await session.commitTransaction();
session.endSession();
conn.disconnect();
if (_.isEmpty(A) && _.isEmpty(B)) {
return [];
}else{
return { from: A, to: B };
}
}catch(error) {
console.log(error);
return false;
}
}
I am explaining my mongodb connection code file below.
const Mongoose = require('mongoose').Mongoose,
fs = require('fs'),
{ ObjectID } = require('mongodb');
class DemoProjectMongo {
async _connect() {
this.dbInstance = null;
const mongooseInstance = new Mongoose();
const mongodebug = false;
const url = `mongodb://admin:admin#localhost:27017/practice`;
const options = {
useNewUrlParser: true,
useCreateIndex: true,
connectTimeoutMS: 5000000,
poolSize: 10000,
useUnifiedTopology: true,
// autoIndex: false
};
this.dbInstance = await mongooseInstance.connect(url, options);
mongooseInstance.set('bufferCommands', false);
mongooseInstance.set('useFindAndModify', false);
if(mongodebug === true) {
mongooseInstance.set('debug', true);
}
return this.dbInstance;
}
async connectMasterDb() {
return await this. _connect();
}
collection(collectionName) {
try{
const path_name = '/home/anil/Desktop/subhrajyoti/project/demo1/model';
const model = `${path_name}/${collectionName}.model.js`;
if (fs.existsSync(model)) {
let SchemaModel = require(model);
return this.dbInstance.model(collectionName, SchemaModel);
}
}catch(error) {
console.log(error);
}
}
isObjectID(value) {
let response = value;
if (_.isArray(response)) {
response = _.map(response, res => {
if (ObjectID.isValid(res)) {
return new ObjectID(res);
}
return res;
});
} else if (ObjectID.isValid(response)) {
response = new ObjectID(response);
}
return response;
}
}
const edQartMongoUtil = new DemoProjectMongo();
module.exports = {
loadMongoModel: edQartMongoUtil.collection.bind(edQartMongoUtil),
connectMasterDb: edQartMongoUtil.connectMasterDb.bind(edQartMongoUtil),
isObjectID: edQartMongoUtil.isObjectID.bind(edQartMongoUtil)
}
Here I want to manage some transitional record but getting the above error. Can anybody help me to resolve this error.

Resources