Jest not executing express middleware - node.js

I am trying to write tests for a simple endpoint which is protected. I have an auth() middleware in place which verifies the Authorization header and upon successful verification adds a key token to the req object.
The problem is that jest is directly calling the getCustomerProfile controller and skips executing auth() middleware because of which the controller says req.token is undefined. This is how my Jest setup looks like:
const express = require('express');
const mapRoutes = require('express-routes-mapper');
const privateRoutes = ('../routes/private_routes')
const auth = require('auth_middleware');
const setupAction = async () => {
const testapp = express();
const mappedPrivateRoutes = mapRoutes(privateRoutes, 'api/controllers/');
testapp.use(express.urlencoded({ extended: false }));
testapp.use(express.json());
testapp.use('/private', mappedPrivateRoutes);
testapp.all('/private/*', (req, res, next) => auth(req, res, next));
return testapp;
};
module.exports = { setupAction };
Below is how I am trying to test the protected endpoint:
const request = require('supertest');
const {
setupAction
} = require('./setup_jest');
const Customer = require('models/customer_model');
let test_api;
beforeAll(async () => {
test_api = await setupAction();
});
....
...
...
test('Customer | get profile', async () => {
let token = 'TestToken123'
res = await request(api)
.get('/private/customer/get-profile')
.set('Accept', /json/)
.set('Authorization', `Bearer ${token}`)
.send()
.expect(200);
expect(res.body.customer_id).toBeTruthy();
});
In auth.js I have tried adding console.log() statements but it has no effect. Nothing gets printed and the request goes straight to getCustomerProfile() controller without validating the authorization headers.
What's more interesting is that when the express app is running and I hit the private endpoint with the Authorization header using Postman it works completely fine and the request goes through auth() middleware.
It is only while running through jest, the middleware is getting skipped!
I am stuck here for hours now. Can anyone please help me out with this? Thank you in Advance :)

Related

Cookies do not stored when set it with axios POST method

I'm trying to write and read cookies and falling into a problem below.
This is my basic server side:
server.js
const app = express();
app.use(cors());
app.use(cookieParser());
import routes from '...';
app.use("/foo", routes);
app.listen(8888);
routes.js
const routes = express.Router();
routes.post('/', (req, res) => {
res.cookie("myFoo", "abcd");
res.send("Cookie added");
}
});
routes.get('/', (req, res) => {
res.send(req.cookies.myFoo);
}
});
export default routes;
And my client side at "http://localhost:3000".
I do two HTTP request
POST http://localhost:8888/foo
GET http://localhost:8888/foo
And get the response exactly what I expected abcd. Also, the cookie exists in the browser tab Application > Cookies too.
The problem cases when axios is used in the client.
const api = axios.create({
baseURL: "http://localhost:8888/foo"
});
async function setCookie(object) {
return api.post("/", object)
.then((res) => {
return res;
});
}
function getCookie() {
return api.get("/")
.then((res) => {
return res;
});
}
setCookie({})
.then((res) => {
getCookie();
})
The api.post() run usually and the header response Set-Cookie is correct. But cookies in the browser tab Application > Cookies are empty. Also, api.get() get the undefined.
I did try to move res.cookie() or the set cookie job in server side to GET route it WORKS on both HTTP and axios
routes.get('/', (req, res) => {
res.cookie("myFoo", "abcd");
});
tldr: Set cookie in HTTP POST method work fine but when client use axios to call so it causes problems.
Can you show me why this happened? And which code part went wrong that caused me into this?
Cookies are only used in cross-origin Ajax requests when:
The client asks to use them
The server grants permission to use them cross origin
So you need to change the client side code to ask for them:
const api = axios.create({
baseURL: 'http://localhost:8888/',
withCredentials: true,
});
And the server code to grant permission (note that you can't use credentials at the same time as the wildcard for origins).
app.use(cors({
origin: 'http://localhost:3000',
credentials: true
}));

How to write middleware to modify response in node js

My client given me requirement to encrypt decrypt all request response. So for all encrypted request we wrote down the express middleware to get decrypted request. which is the simple part but while sending response we also have to encrypt response.
One way to do write common function to encrypt data and call that function from all routes. which is time consuming part because we have more than 50+ routes in project. So i was thinking to write middleware like we have done for request which capture response before we send and then we encrypt response after that we send encrypt response to client.
I have searched for solution in google not got any proper solution which worked for me.
routes.js
router.post('/getUserData', verifyApiKey, async function (req, res, next) {
let user = await getUserData();
res.status(200).send(user)
});
middlware.js
class EncryptDecryptRequestResponse {
async encryptResponse(req, res, next) {
console.log('Called encryptResponse');
console.log('res.body', res.body);
res.body = encryptData(res.body)
next();
}
}
App.js
// Middleware to decrypt request
app.use(decryptRequest);
app.use('/', indexRouter);
// Middleware to encrypt response
app.use(encryptResponse);
but the problem is that i am not getting any console.log from middleware. this is the solution which i used
I tried to reproduce the problem you're having with overwriting res.send(), but it works fine for me. You need to make sure to setup the interceptor middleware before you define your routes. Consider this simple example:
const express = require('express');
const app = express();
function encryptResponseInterceptor(req, res, next) {
const originalSend = res.send;
res.send = function () {
arguments[0] = encryptResponse(arguments[0]);
originalSend.apply(res, arguments);
};
next();
}
function encryptResponse(originalData) {
// place your encryption logic here, I'm just adding a string in this example
return originalData + " modified";
}
// fake method that returns resolves after 1s just for testing
function getUserData() {
return new Promise(resolve => {
setTimeout(() => {
resolve();
}, 1000)
})
}
app.use(encryptResponseInterceptor);
app.get("/test", async (req, res, next) => {
await getUserData();
res.status(200).send("will be changed");
})
app.listen(3000, () => {
console.log("server started on 3000");
});

Retrieve particular values from the POST endpoint in Express

I have an API definition /task/{activityId}?status={status} ( method = POST)
Input -> activityId, status
Output -> status
In Express I have written my code like this for debugging purpose -
const express = require("express");
const app = express();
const cors = require("cors");
const pool = require("./db");
const axios = require('axios');
app.use(cors());
app.use(express.json());
app.post("/task/:activityId?status=:status", async (req, res) => {
try {
var activityId = req.params.activityId;
var status = req.params.status;
console.log(status);
console.log(activityId);
if (status == "COMPLETE")
const updateStatus = await pool.query("update public.\"TableOne\" set \"Status\"='COMPLETE' where \"ActivityId\"='" + activityId + "'");
}
catch (err) {
console.error(err.message);
}
})
app.listen(5000, () => {
console.log("server has started on port 5000");
})
I am not able to see the values in console of activity id and status passed when I am hitting the endpoint from postman with something like this -
[POST] http://hostname:port/task/A1?status=PENDING
What mistake am I making here?
In order to get values from parameter, proper way is like this
console.log(req.params.status);
But secondary parameter named status is stated as querystring parameter, So, you need to fetch like this,
console.log(req.query.status);
Also, you don’t need to mention status in the code, so, your code to fetch the param should be like this:
app.post("/task/:activityId", async (req, res) => {
As you can see, I didn’t mention the status parameter. Still I will get it.

GraphQL - POST body missing. Did you forget use body-parser middleware?

I keep getting the following error on my graphql queries and not sure why:
POST body missing. Did you forget use body-parser middleware?
Am I doing something weird here? I have tried different recommendations with body-parser online, but still can't seem to fix it.
Server:
require('babel-polyfill')
const express = require('express')
const router = require('./middleware')
const expressStaticGzip = require('express-static-gzip')
const app = express()
const port = process.env.EXPRESS_PORT || 4000
const bodyParser = require('body-parser')
app.use(/\/((?!graphql).)*/, bodyParser.urlencoded({ extended: true }))
app.use(/\/((?!graphql).)*/, bodyParser.json())
app.use('/search/data', expressStaticGzip('public'))
app.use('/', router)
app.listen(port, () => {
console.log(`Server is running on port ${port}`)
})
Router
const router = express.Router()
const server = new ApolloServer({
typeDefs,
resolvers,
context: ({ req }) => {
const { authorization = '' } = req.headers
const universalFetch = (url, opts = {}) => {
return fetch(url, {
...opts,
headers: {
...opts.headers,
authorization,
},
})
}
const request = createRpcClient(universalFetch)
const methods = {}
const catalog = Object.keys(methods).reduce((catalog, method) => {
catalog[method] = params => request(methods[method], params)
return catalog
}, {})
return { catalog, fetch: universalFetch }
},
})
router.use(bodyParser.json())
router.use(bodyParser.text({ type: 'application/graphql' }))
router.use('*', renderer)
server.applyMiddleware({ app: router })
In my particular case the client just missed "Content-type" header with 'application/json' value. After adding that the error message has dissapeared.
applyMiddleware already adds body-parser for the GraphQL endpoint -- there's no need to apply it again and doing so may be causing your issue.
Additionally, I would expect applyMiddleware to be called before router.use('*', renderer) -- otherwise, I would think the wildcard route would be used for /graphql as well?
I forgot the header content-type: application/json
This error also caused by incorrect json in the body or some other problems in the body, such as unnecessary wrong invisible chars. So check generated json for errors and what is actually presents in the request body.
This error can also be raised because the body is too large.
I got it with apollo-server-micro inside a custom api route of NextJs.
It can be fixed by calling the json function coming from micro before apollo gets the request :
import { json } from 'micro'
import { ApolloServer } from 'apollo-server-micro'
const server = new ApolloServer({/*config*/})
const raiseBodyLimit: (handler: NextApiHandler) => NextApiHandler = (
handler
) => async (req, res) => {
if (req.headers['content-type'] !== 'application/json') {
return handler(req, res)
}
await json(req, { limit: '1gb' }) // This is the trick to raise body limit
return handler(req, res)
}
export default raiseBodyLimit(
server.createHandler({
path: '/api/graphql',
})
)
I saw this in this apollo-server's github issue.
Here are some information to build an apollo server endpoint with next.js
if your api upload anything you need to add the
{
uploads:true
}
in middleware while using graphql

Mocha, Chai & Sinon: Checking internal working of an API

Let's suppose I have a POST endpoint /user/:id and this endpoint, internally calls a function getUserData(id) and then returns the result to the caller, which in-turn returns the output, after JSON.stringify()ing.
Now, I need to ensure that getUserData(id) is called, for at-least once. How can I stub / spy getUserData(id) function, when I am using chai-http to make a post request to the server? Is it even a correct approach?
I adapted the tutorial from https://scotch.io/tutorials/test-a-node-restful-api-with-mocha-and-chai down to a barebones server and test that you can use to do a basic API test.
As Mr.Phoenix said, you don't need to get too deep into the nitty gritty of your handler, just pass some data to your endpoint and check the result against what you expect to get.
Here are 2 files you can use to do this test:
index.js
const express = require('express')
const app = express()
app.get('/material',(req, res)=>{
res.json([])
//res.json(['stone', 'wood'])
})
function getUserData(id){
return 42
}
const port = 3031
app.listen(port, function(err){
console.log("Listening on port: " + port)
})
module.exports = app
test.js
process.env.NODE_ENV = 'test'
// const Material = require('./materials') // conroller
const chai = require('chai')
const chaiHttp = require('chai-http')
const server = require('./index')
const should = chai.should()
chai.use(chaiHttp)
describe('/GET material', () => {
it('it should get all the materials', (done)=>{
chai.request(server)
.get('/material')
.end((err, res) => {
res.should.have.status(200)
res.body.should.be.a('array')
res.body.length.should.be.eql(0) // start empty
done()
})
})
})

Resources