How to define redis client globally? - javascript

I have a simple component that is bind to a controller. So every HTTP request calls testFunction():
//test.js
const redisHelper = require('../myRedis');
class TEST {
testFunction() {
....
redisHelper.getCache(cacheKey)
.then((cacheData) => {
....
}
}
module.exports = TEST;
This component requires myRedis module:
//myRedis.js
const redis = require('redis');
class myRedis {
constructor () {
....
this.redisClient = this.getRedisClient();
}
getRedisClient () {
let redisClient;
....
return redisClient;
}
getCache (key) {
....
}
quit () {
this.redisClient.quit();
}
}
module.exports = new myRedis();
I don't want to create redis client for every HTTP request but keep it ON/open. So can I declare redis client in app.js and use it in test.js? Then on process exit I'll close the connection.
process.on('SIGTERM', () => {
server.close(() => {
redisClient.quit();
process.exit();
})
})
The problem is with redis client that crashes when it's open/close for every of thousands HTTP requests

Related

mongo client: how can I reuse the client in separate file?

Here is db.js file
const client = new MongoClient(DATABASE, mongodbOptions);
const connectWithMongoDb = () => {
client.connect((err) => {
if (err) {
throw err;
} else {
console.log('db connected');
}
});
};
module.exports = { client, connectWithMongoDb };
I called the connectWithMongoDb function from my server.js. db connects successfully. but the problem is I can't reuse the client. for example, I want to make a separate directory for collections. (in order to get a collection I need client object)
So, here is my collection.js file
const { client } = require('../helpers/db-helper/db');
exports.collection = client.db('organicdb').collection('products');
but the problem arises as soon as this file(collection.js) is called.
I am getting this error:
throw new MongoError('MongoClient must be connected before calling MongoClient.prototype.db'
You have to get the connection after connecting to MongoDB post that you can use it anywhere.
Read - https://mongodb.github.io/node-mongodb-native/api-generated/mongoclient.html
let client;
async function connect() {
if (!client) {
client = await MongoClient.connect(DATABASE, mongodbOptions)
.catch(err => { console.log(err); });
}
return client;
}
conet getConectedClient = () => client;
const testConnection = connect()
.then((connection) => console.log(connection)); // call the function like this
module.exports = { connect, getConectedClient };

How to detect which message was sent from the Websocket server

I have a small web application listening for incoming messages from a Websocket server. I receive them like so
const webSocket = new WebSocket("wss://echo.websocket.org");
webSocket.onopen = event => webSocket.send("test");
webSocket.onmessage = event => console.log(event.data);
but the sending server is more complex. There are multiple types of messages that could come e.g. "UserConnected", "TaskDeleted", "ChannelMoved"
How to detect which type of message was sent? For now I modified the code to
const webSocket = new WebSocket("wss://echo.websocket.org");
webSocket.onopen = event => {
const objectToSend = JSON.stringify({
message: "test-message",
data: "test"
});
webSocket.send(objectToSend);
};
webSocket.onmessage = event => {
const objectToRead = JSON.parse(event.data);
if (objectToRead.message === "test-message") {
console.log(objectToRead.data);
}
};
So do I have to send an object from the server containing the "method name" / "message type" e.g. "TaskDeleted" to identify the correct method to execute at the client? That would result in a big switch case statement, no?
Are there any better ways?
You can avoid the big switch-case statement by mapping the methods directly:
// List of white-listed methods to avoid any funny business
let allowedMethods = ["test", "taskDeleted"];
function methodHandlers(){
this.test = function(data)
{
console.log('test was called', data);
}
this.taskDeleted = function(data)
{
console.log('taskDeleted was called', data);
}
}
webSocket.onmessage = event => {
const objectToRead = JSON.parse(event.data);
let methodName = objectToRead.message;
if (allowerMethods.indexOf(methodName)>=0)
{
let handler = new methodHandlers();
handler[methodName](data);
}
else
{
console.error("Method not allowed: ", methodName)
}
};
As you have requested in one of your comments to have a fluent interface for the websockets like socket.io.
You can make it fluent by using a simple PubSub (Publish Subscribe) design pattern so you can subscribe to specific message types. Node offers the EventEmitter class so you can inherit the on and emit events, however, in this example is a quick mockup using a similar API.
In a production environment I would suggest using the native EventEmitter in a node.js environment, and a browser compatible npm package in the front end.
Check the comments for a description of each piece.
The subscribers are saved in a simple object with a Set of callbacks, you can add unsubscribe if you need it.
note: if you are using node.js you can just extend EventEmitter
// This uses a similar API to node's EventEmitter, you could get it from a node or a number of browser compatible npm packages.
class EventEmitter {
// { [event: string]: Set<(data: any) => void> }
__subscribers = {}
// subscribe to specific message types
on(type, cb) {
if (!this.__subscribers[type]) {
this.__subscribers[type] = new Set
}
this.__subscribers[type].add(cb)
}
// emit a subscribed callback
emit(type, data) {
if (typeof this.__subscribers[type] !== 'undefined') {
const callbacks = [...this.__subscribers[type]]
callbacks.forEach(cb => cb(data))
}
}
}
class SocketYO extends EventEmitter {
constructor({ host }) {
super()
// initialize the socket
this.webSocket = new WebSocket(host);
this.webSocket.onopen = () => {
this.connected = true
this.emit('connect', this)
}
this.webSocket.onerror = console.error.bind(console, 'SockyError')
this.webSocket.onmessage = this.__onmessage
}
// send a json message to the socket
send(type, data) {
this.webSocket.send(JSON.stringify({
type,
data
}))
}
on(type, cb) {
// if the socket is already connected immediately call the callback
if (type === 'connect' && this.connected) {
return cb(this)
}
// proxy EventEmitters `on` method
return super.on(type, cb)
}
// catch any message from the socket and call the appropriate callback
__onmessage = e => {
const { type, data } = JSON.parse(e.data)
this.emit(type, data)
}
}
// create your SocketYO instance
const socket = new SocketYO({
host: 'wss://echo.websocket.org'
})
socket.on('connect', (socket) => {
// you can only send messages once the socket has been connected
socket.send('myEvent', {
message: 'hello'
})
})
// you can subscribe without the socket being connected
socket.on('myEvent', (data) => {
console.log('myEvent', data)
})

Start and stop server with supertest

I have the following server class :
import express, { Request, Response } from 'express';
export default class Server {
server: any;
exp: any;
constructor() {
this.exp = express();
this.exp.get('/', (_req: Request, res: Response) => {
res.json('works');
});
}
start(): void {
this.server = this.exp.listen(3000);
}
stop(): void {
this.server.close();
}
}
I'm using supertest for end-to-end testing. I wish to start my application beforeAll tests and stop it when the tests are done.
It's easy to do that using beforAll and afterAll where I can just once instanciate the Server class and call the start and close methods.
But as I have 10+ controllers to test, I want to avoid to start and stop the server during each test file.
I found on the documentation the setupFiles and setupFilesAfterEnv but I can't stop the server since the instance is not "shared" in the two files.
This is an example of 1 test file :
import supertest from 'supertest';
describe('Album Test', () => {
let app: App;
beforeAll(async (done) => {
app = new App();
await app.setUp(); // database connection (not mentionned in the preivous example)
done();
});
afterAll(async (done) => {
await app.close();
app.server.stop();
done();
});
const api = supertest('http://localhost:3000');
it('Hello API Request', async () => {
const result = await api.get('/v1/user');
expect(result.status).toEqual(200);
...
});
});
This works totally fine but I'm duplicating this beforeAll and afterAll methods in every test file. Is there a way to declare it only once ?
Thanks
Try this it works
const supertest = require('supertest')
const app = require('../../app')
describe('int::app', function(){
let request = null
let server = null
before(function(done){
server = app.listen(done)
request = supertest.agent(server)
})
after(function(done){
server.close(done)
})
it('should get /api/v1/laps/85299', function(){
return request.get('/api/v1/laps/85299')
.expect(200, { data: {} })
})
})
You could use setupFiles to set up test fixtures globally. You can assign variables that you want to use in multiple test files to Node.js global object.
E.g.
app.ts:
import express, { Request, Response } from 'express';
export default class Server {
server: any;
exp: any;
constructor() {
this.exp = express();
this.exp.get('/', (_req: Request, res: Response) => {
res.json('works');
});
}
start(): void {
this.server = this.exp.listen(3000);
}
stop(): void {
this.server.close();
}
}
app.setup.js:
const App = require('./app').default;
beforeAll(() => {
global.app = new App();
global.app.exp.set('test setup', 1);
console.log('app setup');
});
afterAll(() => {
console.log('app stop');
});
jest.config.js:
module.exports = {
preset: 'ts-jest/presets/js-with-ts',
testEnvironment: 'node',
setupFilesAfterEnv: [
'./jest.setup.js',
'/Users/ldu020/workspace/github.com/mrdulin/react-apollo-graphql-starter-kit/stackoverflow/61659975/app.setup.js',
],
testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'],
verbose: true,
};
a.controller.test.js:
describe('controller a', () => {
it('should pass', () => {
console.log('test setup:', global.app.exp.get('test setup'));
expect(1 + 1).toBe(2);
});
});
b.controller.test.js:
describe('controller b', () => {
it('should pass', () => {
console.log('test setup:', global.app.exp.get('test setup'));
expect(1 + 1).toBe(2);
});
});
unit test results:
PASS stackoverflow/61659975/a.controller.test.js
controller a
✓ should pass (5ms)
console.log
app setup
at Object.<anonymous> (stackoverflow/61659975/app.setup.js:6:11)
console.log
app setup
at Object.<anonymous> (stackoverflow/61659975/app.setup.js:6:11)
console.log
test setup: 1
at Object.<anonymous> (stackoverflow/61659975/b.controller.test.js:3:13)
console.log
test setup: 1
at Object.<anonymous> (stackoverflow/61659975/a.controller.test.js:3:13)
console.log
app stop
at Object.<anonymous> (stackoverflow/61659975/app.setup.js:10:11)
console.log
app stop
at Object.<anonymous> (stackoverflow/61659975/app.setup.js:10:11)
PASS stackoverflow/61659975/b.controller.test.js
controller b
✓ should pass (3ms)
Test Suites: 2 passed, 2 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 6.749s, estimated 12s

Mock a method of a service called by the tested one when using Jest

I am trying to mock a method's service i export as a module from my test.
This is something i use to do with "sinon", but i would like to use jest as much as possible.
This is a classic test, i have an "authentication" service and a "mailer" service.
The "authentication" service can register new users, and after each new registration, it ask the mailer service to send the new user a "welcome email".
So testing the register method of my authentication service, i would like to assert (and mock) the "send" method of the mailer service.
How to do that? Here is what i tried, but it calls the original mailer.send method:
// authentication.js
const mailer = require('./mailer');
class authentication {
register() { // The method i am trying to test
// ...
mailer.send();
}
}
const authentication = new Authentication();
module.exports = authentication;
// mailer.js
class Mailer {
send() { // The method i am trying to mock
// ...
}
}
const mailer = new Mailer();
module.exports = mailer;
// authentication.test.js
const authentication = require('../../services/authentication');
describe('Service Authentication', () => {
describe('register', () => {
test('should send a welcome email', done => {
co(function* () {
try {
jest.mock('../../services/mailer');
const mailer = require('../../services/mailer');
mailer.send = jest.fn( () => { // I would like this mock to be called in authentication.register()
console.log('SEND MOCK CALLED !');
return Promise.resolve();
});
yield authentication.register(knownUser);
// expect();
done();
} catch(e) {
done(e);
}
});
});
});
});
First you have to mock the mailer module with a spy so you can later set. And you to let jest know about using a promise in your test, have a look at the docs for the two ways to do this.
const authentication = require('../../services/authentication');
const mailer = require('../../services/mailer');
jest.mock('../../services/mailer', () => ({send: jest.fn()}));
describe('Service Authentication', () => {
describe('register', () => {
test('should send a welcome email', async() => {
const p = Promise.resolve()
mailer.send.mockImplementation(() => p)
authentication.register(knownUser);
await p
expect(mailer.send).toHaveBeenCalled;
}
});
});
});
});

testing node express endpoint and stub 3rd party api call

I have an express app like this:
server.js
const postsController = require('./controllers/posts_controller.js')
module.exports = app = express()
app.get('posts', postsController.index)
posts_controller.js
const post = require('./post')()
module.exports = {
index: (req, res) => {
post.getAll().then((posts) => {
res.status(200).send(posts)
}, (error) => {
res.status(400).send('text')
})
}
}
post.js
module.exports = () => {
const thirdPartyApi = require('third_party_api')
return {
get: () => {
// do some stuff
return thirdPartyApi.get().then((resp) => {
// do some more stuff
return Promise.resolve(resp)
})
}
}
}
spec/posts_controller_spec.js
const app = require('../server')
const request = require('supertest')
describe('GET /posts', () => {
it('should return a collection of posts', () => {
request(app)
.get('/posts')
.end((_err, resp) => {
expect(resp.status).toEqual(200)
})
})
})
My goal here is to stub out the thirdPartyApi.get(). I tried with proxyquire by adding this line to posts_controller_spec:
proxyquire('../posts_controller', {third_party_api: {
get: () => { console.log('stubbed out get method'); }
})
This doesn't work because the server.js file is the file that requires the third_party_api again.
I could do something like this to test the controller:
const postsController = proxyquire('../posts_controller', {third_party_api: {
get: () => { console.log('stubbed out get method'); }
})
postsController.index(req, res)
This second strategy doesn't feel right because now I have to stub req and res and now I'm bypassing the actual app instance.
Is there an easy way to do this, with proxyquire, or otherwise?
I realized what's going on, proxyquire is not actually messing up here.
the file post.js exports a function, so when posts_controller.js requires() the post.js file, it executes the function and the require for third_party_api is evaluated again and the stub is wiped out.
This is the "runtimeGlobal" scenario described here: https://www.npmjs.com/package/proxyquire#globally-override-require-during-module-runtime
Solution is to fix up post.js so that it doesn't export a function:
const thirdPartyApi = require('third_party_api')
return {
get: () => {
// do some stuff
return thirdPartyApi.get().then((resp) => {
// do some more stuff
return Promise.resolve(resp)
})
}
}
Now, as long as this happens before the app is initialized, the
proxyquire('../posts_controller', {third_party_api: {
get: () => { console.log('stubbed out get method'); }
})
Then third_party_api module that gets required inside the post controller is evaluated at load time and it gets cached as expected.

Categories

Resources