Jest tests hanging even after running server.close() on my express app - node.js

I have an express app which I need to start on my integration test case.
The express app is exported in the app.js file without listening to any port. So, my test case goes like this:
const app = require('../src/app');
describe('Pact Verification', () => {
const port = 3002;
let server;
beforeAll(done => {
const server = http.createServer(app);
server.listen({ port }, done)
});
afterAll(done => {
server.close(done);
});
it(....
The problem is, once the test is ran with Jest, it hangs. I have to either --forceExit or ^C to exit.
I even updated to Jest 23 to use --detectOpenHandles but I don't see any output in the terminal, it keeps hanging so that's not helping either.
Since the exported app is not listening to any port, and it doesn't have any database connections or such, the problem is unlikely to be there, maybe its in my beforeAll/afterAll block. What am I missing?
Update 1
Here is the content of my app.js
var express = require('express');
var path = require('path');
var logger = require('morgan');
var indexRouter = require('./routes/index');
var app = express();
if (process.env.NODE_ENV !== 'test') {
app.use(logger('dev'));
}
app.use(express.json());
app.use('/api/v1/', indexRouter); // <-- these endpoints just return faker data in JSON format (https://github.com/marak/Faker.js/)
app.use((req, res, next) => {
const err = Error('Not Found');
err.status = 404;
next(err);
});
app.use((err, req, res, next) => {
const { status, message } = err;
const stack = status !== 404 && err.stack || undefined;
res.status(status || 500)
.json({
message,
stack,
});
});
module.exports = app;

The problem is that server is undefined in afterAll because it's assigned in another scope as const server = http.createServer(app). Instead, it should be:
server = http.createServer(app);
There should be an exception, so done is never get called in afterAll. Currently Jest suppresses errors from afterAll, there's Jest open issue that addresses afterAll bug.

Related

Nextjs Routes are not working when deployed to Azure App Service Linux Instance

I wanted to deploy a nextjs app to Azure App Service with Linux instance.
I followed the instructions here: https://developinjay.com/deploying-nextjs-app-to-azure-app-service-linux-77a43353e761
The app is live here https://interviewramp.azurewebsites.net and https://interviewramp.herokuapp.com.
Routes work in Heroku. So if user goes here https://interviewramp.herokuapp.com/books/interviewramp/introduction it works.
Routes don't work in Azure App service. So, if user goes here https://interviewramp.azurewebsites.net/books/interviewramp/introduction, I get 404 document not found error, which means code is trying to serve an introduction.html file in Azure and returning 404 because there is no html file like that.
All links work locally when I npm run start or npm run dev.
Pasting url like this to browser gives 404 : https://interviewramp.azurewebsites.net/login
I have never done next export in my code. I was wondering if you could help me?
Below is my server.js
const express = require('express');
const session = require('express-session');
const mongoSessionStore = require('connect-mongo');
const next = require('next');
const api = require('./api');
require('dotenv').config();
const dev = process.env.NODE_ENV !== 'production';
const MONGO_URL = dev ? process.env.MONGO_URL_TEST : process.env.MONGO_URL;
const port = process.env.PORT || 8000;
const ROOT_URL = getRootUrl();
const URL_MAP = {
'/login': '/public/login',
'/my-books': '/customer/my-books',
};
const app = next({ dev });
const handle = app.getRequestHandler();
app.prepare().then(async () => {
const server = express();
server.use(helmet({ contentSecurityPolicy: false }));
server.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
next();
});
server.use(express.json());
// give all Nextjs's request to Nextjs server
server.get('/_next/*', (req, res) => {
handle(req, res);
});
api(server);
routesWithSlug({ server, app });
server.get('*', (req, res) => {
const url = URL_MAP[req.path];
if (url) {
app.render(req, res, url);
} else {
handle(req, res);
}
});
server.listen(port, (err) => {
if (err) throw err;
logger.info(`> Ready on ${ROOT_URL}`);
});
});
I was able to solve this problem and I am leaving this answer in case it is helpful to someone else. The Azure web app is showing correct paths now. I used node server.js instead of next start in my startup command in Azure and it fixed the issue.

Can't retrieve information from mongodb when deploying on heroku

The problem is as the title suggests. When I run my app locally, I'm able to retrieve information from MongoDB but on Heroku, undefined is returned. Should I connect to MongoDB in another way because if I hardcode some text everything works just fine. Here are my scripts:
function to get data
const MongoClient = require("mongodb").MongoClient;
const dbConnectionUrl = "mongodb+srv://xxxxxxx#cluster0.ro4dz.mongodb.net/data?retryWrites=true&w=majority";
const saySomething = (req, res, next) => {
// res.status(200).json({
// body: 'Hello from the server!'
// });
login()
.then(val=>res.send(val))
};
async function login(){
const client = new MongoClient(dbConnectionUrl)
try{
await client.connect();
const database = client.db("data");
const movies = database.collection("movies");
const query = { name: "toke" };
const movie = await movies.findOne(query);
return movie
}catch(err){
console.log(err)
}
}
module.exports.saySomething = saySomething;
router
const express = require('express');
const router = express.Router();
const controllers = require('./../controllers/controllers');
router.get('/say-something', controllers.saySomething);
module.exports = router;
server
// Import dependencies
const express = require('express');
const cors = require('cors');
const path = require('path');
// Create a new express application named 'app'
const app = express();
// Set our backend port to be either an environment variable or port 5000
const port = process.env.PORT || 5000;
// This application level middleware prints incoming requests to the servers console, useful to see incoming requests
app.use((req, res, next) => {
console.log(`Request_Endpoint: ${req.method} ${req.url}`);
next();
});
// Configure the CORs middleware
// Require Route
app.use(cors());
const api = require('./routes/routes');
// Configure app to use route
app.use('/api', api);
// This middleware informs the express application to serve our compiled React files
if (process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'staging') {
app.use(express.static(path.join(__dirname, 'client/build')));
app.get('*', function (req, res) {
res.sendFile(path.join(__dirname, 'client/build', 'index.html'));
});
};
// Catch any bad requests
app.get('*', (req, res) => {
res.status(200).json({
msg: 'Catch All'
});
});
// Configure our server to listen on the port defiend by our port variable
app.listen(port, () => console.log(`BACK_END_SERVICE_PORT: ${port}`));
front
import { useEffect, useState } from 'react';
import './App.css';
import axios from 'axios'
function App(){
useEffect(()=>{
get()
})
const[text, settext] = useState('')
async function get(){
let request = await axios.get('/api/say-something')
console.log(request.data.name)
settext(request.data.name)
}
return(
<div>{text}</div>
)
}
export default App;
I solved the issue! The first thing I did was that I added MongoDB connection URI as an environmental variable in my app via Heroku. Secondly, I added an option in MongoDB so that the cluster can be accessed from any computer. By default, the access is set to the local computer so I added another IP, namely 0.0.0.0/0 to my cluster, and now everything works just fine.

Getting 'port already in use error' when adding socket.io to express app - happens on any port I try

As stated in the title, I'm getting 'address already in use error' when trying to add socket.io to an existing express app.
It happens on every port so the only thing I can think of is it's trying to set port 8000 twice. I don't see where, however.
Here is the error in the console.
Here is my config.js
module.exports = {
PORT: process.env.PORT || 8000,
NODE_ENV: process.env.NODE_ENV || 'development',
DATABASE_URL: process.env.DATABASE_URL || 'postgresql://postgres#localhost/quik'
}
.env
NODE_ENV=development
PORT=8000
And here is my main express app. I've tried setting PORT from process.env and directly with '8000'.
require('dotenv').config()
const morgan = require('morgan')
const cors = require('cors')
const helmet = require('helmet')
const { NODE_ENV } = require('./config')
const userRouter = require('./user/user_router')
const pinsRouter = require('./pins/pins_router')
const chatRouter = require('./chat/chat_router')
const config = require('./config')
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
app.get('/', function(req, res){
res.sendFile(__dirname + '/index.html');
});
io.on('connection', function(socket){
socket.on('chat message', function(msg){
io.emit('chat message', msg);
});
});
http.listen(config.PORT, function(){
console.log('listening on *:8000');
});
const morganOption = (NODE_ENV === 'production')
? 'tiny'
: 'common';
app.use(morgan(morganOption))
app.use(helmet())
app.use(cors())
app.use('/user_route', userRouter)
app.use('/pins_route', pinsRouter)
app.use('/chat_route', chatRouter)
app.get('/', (req, res) => {
res.send('Hello, world!')
})
app.use(function errorHandler(error, req, res, next) {
let response
if (NODE_ENV === 'production') {
response = { error: { message: 'server error' } }
} else {
console.error(error)
response = { message: error.message, error }
}
res.status(500).json(response)
})
module.exports = app
The stack trace in the error appears like you are calling app.listen() somewhere (probably from your server.js - line 13). You need to go find that code and remove it. You only want to attempt to start your same server once and your http.listen() is already starting it once.

prevent to crash node js after uncaughtExceptions

i have written one middle-ware for handling uncaughtExceptions which is working fine but after that server will crashed.
how do i prevent to crash it?
server.js:
const express = require('express');
const winston = require("winston");
const app = express();
//Logging is responsible to log and display errors
require('./startup/logging')();
//routes will contains all the routes list
require('./startup/routes')(app);
//PORT
const port = process.env.PORT || 3000;
app.listen(port,() => winston.info(`Listening on port ${port}....`));
logging.js
const express = require('express');
const winston = require('winston');
// require('express-async-errors');
module.exports = function() {
winston.handleExceptions(
new winston.transports.File({ filename: 'uncaughtExceptions.log' })
);
process.on('unhandledRejection', (ex) => {
throw ex;
});
winston.add(winston.transports.File, { filename: 'error.log' });
}
If you do throw ex; The program will crash, Rather you should send the crash report and message to the respective reporting mechanism that you are using. Heres a small snippet
process.on('unhandledRejection', (reason, promise) => {
console.log('Unhandled Rejection at:', reason.stack || reason)
// Recommended: send the information to sentry.io
// or whatever crash reporting service you use
})

Mocha- Testing Express API With Global Middleware

This is my first express app and I'm having some trouble writing the tests for the endpoints.
At the moment I am not using a database to store the tokens or check for users when logging in.
The tests I have written pass, however they are not complete. How would I test for example a user that has authenticated correctly?
How would I mimmic the authentication process?
Because I've created a global middleware function and checkLoging function how to factor these into my tests?
How do I test the authentication and routs?
This is my code:
'use strict';
var express = require('express');
var session = require('express-session');
var app = express();
var shopifyObj = require('shopify-node');
var shopifyConfig = require("./config/config.json");
var _ = require('underscore');
var shopify = require('./lib/modules/shopify');
var product = require('./lib/modules/product');
var fakeDta = {
user: 'Simba',
};
app.use(session({
secret: 'dsjkfhklaf',
resave: false,
saveUninitialized: true
}));
// Set global middleware function to check for sessions
app.use(function(req, res, next) {
if (req.session && req.session.user) {
// check here for the existence of this user and token in the database.
next();
} else {
next();
}
});
// define middleware function
function checkLogin (req, res, next) {
if (!req.session.user) {
req.session.error = 'Access denied!';
res.redirect('/auth');
} else {
next();
}
};
app.get('/auth', function (req, res) {
var url = shopify.createURL();
res.redirect(url);
});
app.get('/handle-o-auth-response', function (req, res) {
var code = req.query.code;
_.extend(shopifyConfig, {'code': code});
shopify.setAccessCode(code, res, function (err, token) {
if (err) {
res.redirect('/auth');
} else {
_.extend(shopifyConfig, {'access_token': token});
req.session.user = fakeDta.user;
res.redirect('/products');
}
});
});
app.get('/products', checkLogin, function (req, res) {
shopify = new shopifyObj(shopifyConfig);
product.getProducts(shopify, res);
});
var server = app.listen(3000, function () {
var host = server.address().address;
var port = server.address().port;
console.log('App listening at http://%s:%s', host, port);
});
module.exports = server;
These are my tests:
"use strict";
var supertest = require("supertest");
describe("Testing server",function(){
var server;
beforeEach(function () {
/* Node module system caches the evaluated result of each module
to avoid loading and compiling the same javascript file multiple times.
Thus the server instance from app.js is only created once.
*/
delete require.cache[require.resolve('../app.js')];
server = require('../app.js');
});
afterEach(function (done) {
server.close(done);
});
it("should redirect to /auth",function(done){
supertest(server)
.get("/products")
//.expect("Content-type",/json/)
.expect(302, done);
});
it("should redirect to shopify",function(done) {
supertest(server)
.get("/auth")
//.expect("Content-type",/json/)
.expect(302, done);
});
});
What I would do is refactor the code to support mocking or replacing the checkLogin function.
You can do this by exposing the checkLogin function on the exported object from app.js, but that wouldn't be very clean architecturally.
Alternatively you can separate the checkLogin function out to it's own file, and instead of exporting:
module.exports = server;
create a function that takes the checkLogin function that wraps all code that uses the checkLogin function:
var express = require('express');
// skipped require code...
var product = require('./lib/modules/product');
function init(checkLogin) {
var fakeDta = {
user: 'Simba',
};
// skipped code...
// remove the checkLogin function definition!
// skipped code...
return app.listen(3000, function () {
var host = server.address().address;
var port = server.address().port;
console.log('App listening at http://%s:%s', host, port);
});
};
module.exports = init;
Now in your live code you require the file with the checkLogin function, and pass the function into server, and in your testing code you can supply a mock function that will behave exactly as you want in your test, for example returning that the login is valid.
You can either write your own function and pass that one, or use a mocking library such as sinon.
I have json config files each for env: test, dev, prod and config loader that returns config basing on current NODE_ENV:
let ENV = process.env.NODE_ENV;
let env = ENV || 'prod';
module.exports = require('./' + env + '.json');
In authorization middleware:
if(config.env != 'prod' && process.env.DISABLE_AUTORIZATION) return next();
Now I can disable authorization in middleware in test and dev enviroment by setting DISABLE_AUTHORIZATION variable like this:
process.env.DISABLE_AUTORIZATION = true;
PS. To set NODE_ENV I use package cross-env. My scripts:
"scripts": {
"start": "node server.js",
"dev": "cross-env NODE_ENV=dev node server.js",
"test": "mocha --timeout 10000"
},

Resources