How do i test nodejs module.exports function? - javascript

trying to test nodejs module.export function that will return based on request but it throws below error any idea what is implemented wrong here its expecting error >
v1Transform.js
module.exports = async (req, res) => {
try {
const validateResponse = responseHandler(req.drugPriceResponse);
} catch (error) {
if (error instanceof AppError) {
res.status(error.response.status).send(error.response.payload);
} else {
res.status(500).send(defaultErrorResponse);
}
}
}
main.test.js
describe('v1Transform()', () => {
it('should return error if accounts are ommitted', () => {
try {
v1Transform(req);
} catch (error) {
expect(error.response).to.deep.equal({
status: 500,
payload: {
'status': 500,
'title': 'Internal Server Error',
'detail': 'Accounts are not valid'
}
});
}
});
});
Error
1) v1Transform()
should return error if prices are ommitted:
AssertionError: expected undefined to deeply equal { Object (status, payload) }

Related

PUT request crashes the server

I'm getting this error when trying to update an entry (in this case, checking a todo and setting it to true/false)
It seems like i'm getting the proper object (seen in the data console log) which makes me wonder why i'm getting [object Object].
Would appreciate any kind of help!
Here's the action :
export function onSaveTodo(todo) {
return async (dispatch) => {
try {
const savedTodo = await todosService.saveTodo(todo);
const action = {
type: 'UPDATE_TODO',
todo: savedTodo,
};
dispatch(action);
} catch (err) {
console.log('cant save todo');
}
};
}
the service :
const BASE_URL = 'todo';
async function saveTodo(todo) {
try {
if (todo._id) {
return httpService.put(BASE_URL, todo);
} else {
return httpService.post(BASE_URL, todo);
}
} catch (err) {
console.log(`could not save todo `, err);
throw err;
}
}
Http service :
export const httpService = {
get(endpoint, data) {
return ajax(endpoint, 'GET', data);
},
post(endpoint, data) {
return ajax(endpoint, 'POST', data);
},
put(endpoint, data) {
console.log('endpoint:', endpoint);
console.log('data:', data);
return ajax(endpoint, 'PUT', data);
},
delete(endpoint, data) {
return ajax(endpoint, 'DELETE', data);
},
};
The backend controller :
async function updateTodo(req, res) {
try {
const { todo } = req.body;
// console.log('todo:', todo);
const savedTodo = await todoService.update(todo);
res.json(savedTodo);
} catch (err) {
logger.error('Failed to update todo', err);
res.status(500).send({ err: 'Failed to update todo' });
}
}
and the backend service :
async function update(todo) {
try {
const newTodo = {
...todo,
_id: ObjectId(todo._id),
};
console.log('newTodo:', newTodo);
const collection = await dbService.getCollection('todo');
await collection.updateOne({ _id: newTodo._id }, { $set: newTodo });
return todo;
} catch (err) {
logger.error(`Can not update toy ${todo._id}`, err);
throw err;
}
}

Jest mock not always works in async test

I have a function and I want to test it using Jest.
function handleRegister() {
return (req, res) => {
try {
const credentials = {
login: req.body.email,
password: req.body.password
}
res.status(201).send({ msg: 'User registration achieved successfully' }) //LINE 10
res.status(201).send({ msg: 'User registration achieved successfully' }) //LINE 11
auth.register(credentials, (err, result) => {
console.log('register', auth.getUsers())
if (result.status === 201) {
res.status(201).send({ msg: 'User registration achieved successfully' }) //LINE 17
console.log('User registration achieved successfully')
}
})
} catch(err) {
}
}}
My test code is:
test('should return status 201 and msg', done => {
try {
const fun = handlers.handleRegister()
const res = {
status: jest.fn().mockReturnThis(),
send: function () {
done()
}
}
fun({ body: { email: 'a', password: 'a' } }, res)
expect(res.status).toBeCalledWith(201)
} catch(err) {
done(err)
}
})
The problem is that function handlerRegister line 10 and 11 is correctly executed, but at line 17 I got an error:
/home/anna/Desktop/dev/exampleShop/backend/handlers.js:149
res.status(201).send({
^
TypeError: Cannot read property 'send' of undefined
at auth.register (/home/anna/Desktop/dev/exampleShop/backend/handlers.js:149:26)
at addAccountToDB (/home/anna/Desktop/dev/exampleShop/backend/auth.js:69:7)
at addAccountToDB (/home/anna/Desktop/dev/exampleShop/backend/auth.js:81:3)
at hashPassword (/home/anna/Desktop/dev/exampleShop/backend/auth.js:68:5)
at AsyncWrap.crypto.scrypt (/home/anna/Desktop/dev/exampleShop/backend/auth.js:87:5)
at AsyncWrap.wrap.ondone (internal/crypto/scrypt.js:43:48)
If I use js, not a mock in property res, like:
const res = {
status: function(){return this},
send: function () {
done()
}
}
}
then I don't have this error.
Can someone explain me what is wrong?
There is a scoping issue. res is not defined where you are calling res.send(), because res is being defined inside of the try block.
Either move your expect statement inside of the try like below, or define res in the same scope as your expect statement.
Also you can't call .toBeCalledWith on a function that is not a mocked function. So notice that I have defined res.send to be a mock function, and instead calling done() at the end of your expect statements.
test('should return status 201 and msg', done => {
try {
const fun = handlers.handleRegister()
// res only exists inside of the `try`
const res = {
status: jest.fn().mockReturnThis(),
send: jest.fn() // << is now a mock function
}
fun({ body: { email: 'a', password: 'a' } }, res)
expect(res.status).toBeCalledWith(201)
// here `res.send` is now defined, and you can use `toBeCalledWith`
expect(res.send).toBeCalledWith({ msg: 'User registration achieved successfully' })
done();
} catch(err) {
done(err)
}
})

Handling errors in Express.js in service / controller layers

I am writing an application in Express.js with a separate controller layer and a service layer. Here is my current code:
user.service.js
exports.registerUser = async function (email, password) {
const hash = await bcrypt.hash(password, 10);
const countUser = await User.countDocuments({email: email});
if(countUser > 0) {
throw ({ status: 409, code: 'USER_ALREADY_EXISTS', message: 'This e-mail address is already taken.' });
}
const user = new User({
email: email,
password: hash
});
return await user.save();
};
exports.loginUser = async function (email, password) {
const user = await User.findOne({ email: email });
const countUser = await User.countDocuments({email: email});
if(countUser === 0) {
throw ({ status: 404, code: 'USER_NOT_EXISTS', message: 'E-mail address does not exist.' });
}
const validPassword = await bcrypt.compare(password, user.password);
if (validPassword) {
const token = jwt.sign({ email: user.email, userId: user._id }, process.env.JWT_KEY, { expiresIn: "10s" });
return {
token: token,
expiresIn: 3600,
userId: user._id
}
} else {
throw ({ status: 401, code: 'LOGIN_INVALID', message: 'Invalid authentication credentials.' });
}
};
user.controller.js
exports.userRegister = async function (req, res, next) {
try {
const user = await UserService.registerUser(req.body.email, req.body.password);
res.status(201).json({ data: user });
} catch (e) {
if(!e.status) {
res.status(500).json( { error: { code: 'UNKNOWN_ERROR', message: 'An unknown error occurred.' } });
} else {
res.status(e.status).json( { error: { code: e.code, message: e.message } });
}
}
}
exports.userLogin = async function (req, res, next) {
try {
const user = await UserService.loginUser(req.body.email, req.body.password);
res.status(200).json({ data: user });
} catch (e) {
if(!e.status) {
res.status(500).json( { error: { code: 'UNKNOWN_ERROR', message: 'An unknown error occurred.' } });
} else {
res.status(e.status).json( { error: { code: e.code, message: e.message } });
}
}
}
The code works, but requires some corrections. I have a problem with error handling. I want to handle only some errors. If another error has occurred, the 500 Internal Server Error will be returned.
1) Can I use "throw" object from the service layer? Is this a good practice?
2) How to avoid duplication of this code in each controller:
if(!e.status) {
res.status(500).json( { error: { code: 'UNKNOWN_ERROR', message: 'An unknown error occurred.' } });
} else {
res.status(e.status).json( { error: { code: e.code, message: e.message } });
}
3) Does the code require other corrections? I'm just learning Node.js and I want to write the rest of the application well.
Yes, you can throw errors from service layer, it is good practice to catch errors with try/catch block in controller
I handle this with a custom error middleware, just use a next function in a catch block.
catch (e) {
next(e)
}
Example of error middleware (for more info check docs, fill free to move a middleware to file)
app.use(function (err, req, res, next) {
// err is error from next(e) function
// you can do all error processing here, logging, parsing error messages, etc...
res.status(500).send('Something broke!')
})
From my point of view it looks good. If you looking for some best practice and tools, try eslint (with AirBnb config for example) for linting, dotenv for a environment variables management, also check Node.js Best Practice
i want to give you an example:
this code in your controller
findCar(idCar)
} catch (error) {
switch (error.message) {
case ErrorConstants.ELEMENT_NOT_FOUND('LISTING'): {
return {
response: {
message: ErrorMessages.ELEMENT_NOT_FOUND_MESSAGE('LISTING'),
},
statusCode,
}
}
default: {
return {
response: {
message: ErrorMessages.UNKNOWN_ERROR_MESSAGE,
},
statusCode,
}
}
}
}
and this code in your service
findCar: async listingId => {
try {
if (some condition) {
throw new Error(ErrorConstants.ELEMENT_NOT_FOUND('LISTING'))
}
return { ... }
} catch (error) {
console.error(error.message)
throw new Error(ErrorConstants.UNKNOWN_ERROR)
}
},
controller is going to catch the service's errors

Unhandled promise rejection Error: Can't set headers after they are sent

I would like to make a if else return (for conrtole) but: "UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: Can't set headers after they are sent.
"
exports.delete = function (req, res) {
Parking.findById(req.params.id).exec()
.then(function (parking) {
if (userController.ensureAuthorized(req, 'Director', parking.id)) {
return parking;
}
return res.status(403).send({msg: 'unauthorized'});
})
.then(function (parking) {
User.update().exec();
return parking;
})
.then(function (parking) {
return Parking.remove({_id: parking._id}).exec();
})
.then(function () {
res.status(200).json({msg: 'Ok ! Parkink remove'});
})
.catch(function (err) {
return res.status(400).send(err);
});
};
Ty
The issue is that after return res.status(403), the promise chain doesn't stop automagically. Eventually, it will hit res.status(200) and cause the error.
You can rewrite your promise chain a bit to prevent this. I'm not sure what the purpose of that User.update().exec() is, but I assume that you wanted to call it and also wait for its promise to get resolved before continuing:
exports.delete = function (req, res) {
Parking.findById(req.params.id).exec()
.then(function (parking) {
if (userController.ensureAuthorized(req, 'Director', parking.id)) {
return User.update(...).exec().then(function() {
return Parking.remove({_id: parking._id}).exec();
}).then(function() {
return res.status(200).json({msg: 'Ok ! Parkink remove'});
});
} else {
return res.status(403).send({msg: 'unauthorized'});
}
}).catch(function (err) {
return res.status(400).send(err);
});
};
Well there is no standard way of breaking the promise chain.
So I am going to throw an error to break the chain, and then handle that custom thrown error:
exports.delete = function (req, res) {
Parking.findById(req.params.id).exec()
.then(function (parking) {
if (userController.ensureAuthorized(req, 'Director', parking.id)) {
return parking;
}
else {
res.status(403).send({msg: 'unauthorized'});
throw new Error('BREAK_CHAIN'); // <-- intentionally throw error
}
})
.then(function (parking) {
User.update().exec();
return parking;
})
.then(function (parking) {
return Parking.remove({_id: parking._id}).exec();
})
.then(function () {
res.status(200).json({msg: 'Ok ! Parkink remove'});
})
.catch(function (err) {
if(err.message != 'BREAK_CHAIN') // <-- handle if error was intentionally thrown
return res.status(400).send(err);
});
};
just as an addition to the other answers given, I would recommend:
1) breaking things up into small parts
2) using throw as intended, to raise the error of non-authorization
function update (parking) {
User.update().exec()
.then(function () {
Parking.remove({_id: parking._id}).exec();
});
}
exports.delete = function (req, res) {
// auth needs req so we put it in scope
var auth = function (parking) {
if (!userController.ensureAuthorized(req, 'Director', parking.id)) {
throw(new Error(403));
}
return parking;
}
Parking.findById(req.params.id).exec()
.then(auth)
.then(update)
.then(function () {
res.status(200).json({msg: 'Ok ! Parkink remove'});
})
.catch(function (err) {
if (err.message === 403) {
return res.status(403).send({msg: 'unauthorized'});
}
return res.status(400).send(err);
});
};

Callback function not working in my post request

I have this code with callbacks:
function getUserToken(data, callback) {
var password_sha256 = sha256(data.password);
getAppById(data.app_id).then(function(res) {
console.log("app"+res);
if (!res) {
console.log("No app");
callback(err, { meta: {
code: 403,
error_message: "There are no app with your id!"
} });
} else {
if (res.user_password == password_sha256) {
console.log("user found");
callback(err, { meta: { code: 200 },
token: password_sha256,
type: 1 });
return;
} else if (res.owner_password == password_sha256) {
console.log("owner found");
callback(err, { meta: { code: 200 },
token: password_sha256,
type: 0 });
} else {
console.log("user not found");
callback(err, { meta: {
code: 403,
error_message: "There are no users with your password!"
} });
}
}
});
}
I post some data using this function:
router.post('/api/login', (req, res) => {
db.getUserToken(req.body, function(err, result) {
console.log(result);
if (result.error) {
return res.status(403).send(result);
} else {
return res.status(200).send(result);
}
});
});
And then getUserToken found, for example, user, so I have "user found" in console log, but callback function in /api/login not working. Where is my error and how can I fix them?
Instead of callback, you can return a Promise, either native or using an external library like Kris Kowal Q
var Q = require('q');
function getUserToken(data) {
var deferred = Q.defer();
var password_sha256 = sha256(data.password);
getAppById(data.app_id).then(function(res) {
if (!res) {
deferred.reject("There are no app with your id!");
} else {
if (res.user_password == password_sha256) {
deferred.resolve({
token: password_sha256,
type: 1
});
} else if (res.owner_password == password_sha256) {
deferred.resolve({
token: password_sha256,
type: 0
});
} else {
console.log("user not found");
deferred.reject("There are no users with your password!");
}
}
})
.catch(function(error) {
deferred.reject(error);
});
return deferred.promise;
}
Then simply process or catch error when you post data
router.post('/api/login', (req, res) => {
db.getUserToken(req.body)
.then(function(result) {
res.status(200).send(result);
})
.catch(function(error) {
res.status(403).send(error);
});
});
Instead of using callback(err, data) you should use callback(null,data).This will give you required output.

Categories

Resources