How to fix this "Cannot read property of undefined" when testing? - node.js

I'm trying to run a test using Jest for this Nodejs API. Although everything works when I run the app, it's failing and returning the error
TypeError: Cannot read property 'store' of undefined
The test code:
const request = require('supertest')
const server = require('../../server')
const { User } = require('../../app/models/User')
describe('User', () => {
test('should create user', async () => {
const user = await User.store({
name: 'Marcelo',
email: 'marcelo#vuttr.com',
password: '123456'
})
const response = await request(server)
.post('/user')
.send({
name: user.name,
email: user.email,
password: user.password
})
expect(response.status).toBe(200)
})
})
The controller:
const User = require('../models/User')
class UserController {
async store (req, res) {
const { email } = req.body
if (await User.findOne({ email })) {
return res.status(400).json({ error: 'User already exists' })
}
const user = await User.create(req.body)
return res.json(user)
}
}
module.exports = new UserController()
For more details, I share this project in github.

try this code
the Test Code
const request = require('supertest')
const server = require('../../server')
const { User } = require('../../app/models/User')
const { UserController } = require('../../UserController')
describe('User', () => {
test('should create user', async () => {
const user = await UserController({
name: 'Marcelo',
email: 'marcelo#vuttr.com',
password: '123456'
})
const response = await request(server)
.post('/user')
.send({
name: user.name,
email: user.email,
password: user.password
})
expect(response.status).toBe(200)
})
})
The controller:
const User = require('../models/User')
const UserController = async(req, res) => {
const { email } = req.body
if (await User.findOne({ email })) {
return res.status(400).json({ error: 'User already exists' })
}
const user = await User.create(req.body);
return res.status(201).json(user);
}
module.exports = UserController;

Related

How to get all users from MongoDB?

I'm trying to get all my users from MongoDB, I've readed all documentations, but nothing helps.. What I'm trying to do is set my back-end mainController and create a get request which helps me get all users in front, so I can display them in my page. I'm still learning how to work with MongoDB, so sorry for my awful question.
mainController:
const uid = require('uid-safe')
const bcrypt = require('bcrypt')
const UserSchema = require('../schemas/UserSchema');
const { db } = require('../schemas/UserSchema');
const { default: mongoose } = require('mongoose');
module.exports = {
register: async (req, res) => {
const { username, email, password, image, city, country, firstName, lastName, phone, gender, birth } = req.body;
const userExists = await UserSchema.findOne({ username });
if (userExists) {
return res.send({ error: true, message: 'User with this username exists', data: null });
}
const emailExists = await UserSchema.findOne({ email });
if (emailExists) {
return res.send({ error: true, message: 'User with this email exists', data: null });
}
const id = await uid(7);
const hashedPass = await bcrypt.hash(password, 3);
const user = new UserSchema({
secret: id,
username,
email,
password: hashedPass,
image,
city,
country,
firstName,
lastName,
phone,
gender,
birth
});
await user.save();
return res.send({ error: false, message: 'User successfully registrated!', data: null });
},
login: async (req, res) => {
const { username, password } = req.body;
const loginUser = await UserSchema.findOne({ username });
if (loginUser) {
const passMatch = bcrypt.compare(password, loginUser.password)
if (passMatch) {
return res.send({ error: false, message: `Welcome back ${username}!`, data: loginUser })
}
return res.send({ error: true, message: 'Invalid password', data: null });
};
return res.send({ error: true, message: 'Invalid username', data: null });
},
getSingleUser: async (req, res) => {
const { secret } = req.params;
const findUser = await UserSchema.findOne({ secret });
if (findUser) {
return res.send({ error: false, message: 'User found', data: findUser });
}
return res.send({ error: true, message: 'User not found', data: null });
},
updateUser: async (req, res) => {
try {
const updateduser = await User.updateOne({ secret: req.params.secret }, { $set: req.body });
res.status(200).json(updateduser);
} catch (error) {
res.status(400).json({ message: error.message });
}
}
};
mainRouter:
const express = require('express')
const { login, register, getSingleUser, authSession, usersApi } = require("../controller/mainController")
const { loginValidate, registerValidate } = require("../middleware/authValidator")
const mainRouter = express.Router()
mainRouter.post('/register', registerValidate, register);
mainRouter.post('/login', loginValidate, login);
mainRouter.get('/user/:secret', getSingleUser)
module.exports = mainRouter;
Thank you!
I think you can use the find function.
const uid = require('uid-safe')
const bcrypt = require('bcrypt')
const UserSchema = require('../schemas/UserSchema');
const { db } = require('../schemas/UserSchema');
const { default: mongoose } = require('mongoose');
module.exports = {
register: async (req, res) => {
const { username, email, password, image, city, country, firstName, lastName, phone, gender, birth } = req.body;
const userExists = await UserSchema.findOne({ username });
if (userExists) {
return res.send({ error: true, message: 'User with this username exists', data: null });
}
const emailExists = await UserSchema.findOne({ email });
if (emailExists) {
return res.send({ error: true, message: 'User with this email exists', data: null });
}
const id = await uid(7);
const hashedPass = await bcrypt.hash(password, 3);
const user = new UserSchema({
secret: id,
username,
email,
password: hashedPass,
image,
city,
country,
firstName,
lastName,
phone,
gender,
birth
});
await user.save();
return res.send({ error: false, message: 'User successfully registrated!', data: null });
},
login: async (req, res) => {
const { username, password } = req.body;
const loginUser = await UserSchema.findOne({ username });
if (loginUser) {
const passMatch = bcrypt.compare(password, loginUser.password)
if (passMatch) {
return res.send({ error: false, message: `Welcome back ${username}!`, data: loginUser })
}
return res.send({ error: true, message: 'Invalid password', data: null });
};
return res.send({ error: true, message: 'Invalid username', data: null });
},
getSingleUser: async (req, res) => {
const { secret } = req.params;
const findUser = await UserSchema.findOne({ secret });
if (findUser) {
return res.send({ error: false, message: 'User found', data: findUser });
}
return res.send({ error: true, message: 'User not found', data: null });
},
updateUser: async (req, res) => {
try {
const updateduser = await User.updateOne({ secret: req.params.secret }, { $set: req.body });
res.status(200).json(updateduser);
} catch (error) {
res.status(400).json({ message: error.message });
}
},
getAllUsers: async(req, res) => {
try {
const allUser = await User.find({});
res.status(200).json(allUser);
} catch (error) {
res.status(400).json({ message: error.message });
}
}
};
const express = require('express')
const { login, register, getSingleUser, authSession, usersApi, getAllUsers } = require("../controller/mainController")
const { loginValidate, registerValidate } = require("../middleware/authValidator")
const mainRouter = express.Router()
mainRouter.post('/register', registerValidate, register);
mainRouter.post('/login', loginValidate, login);
mainRouter.get('/user/all', getAllUsers);
mainRouter.get('/user/:secret', getSingleUser);
module.exports = mainRouter;

Cannnot send request to the users/signup route and failing to make connection to the database

Below is the sign up controller...
const signup = async (req, res) => {
const { name, username, profilePhoto, email, password } = req.body;
console.log(req.body);
try {
const existingUser = await User.findOne({ email });
if (existingUser)
return res.status(404).json({ message: "User already exist." });
const hashedPassword = await bcrypt.hash(password, 12);
const result = await User.create({
name,
username,
email,
profilePhoto,
password: hashedPassword,
});
const token = jwt.sign(
{
name: result.name,
email: result.email,
username: result.username,
id: result._id,
},
"test",
{
expiresIn: "1h",
}
);
res.status(200).json({ result: result, token });
} catch (error) {
res.status(500).json({ message: "Something went wrong" });
}
};
This is the code for action for SignUp
export const signup = (formData, history) => async (dispatch) => {
try {
// sign up the user
const { data } = await api.signUp(formData);
dispatch({ type: "AUTH", data });
toast.success("Signed Up successfully");
history.push("/feedbacks");
} catch (error) {
dispatch({ type: "ERROR", data: error?.response?.data });
toast.error(error?.response?.data?.message);
}
While signing in I am getting proper response on the console in the backend. But unable to SignUp.
Error:
But the output of console has the response:
Other functionalities are also working in sync with Front-End
routes
Index.js
const express = require("express");
const connectDB = require("./config/db");
const cors = require("cors");
const app = express();
const feedbacksRoutes = require("./routes/feedbacks");
const userRoutes = require("./routes/users");
app.use(express.json({ extended: false }));
app.use(cors());
// connect to mongoDB
connectDB();
app.use("/feedbacks", feedbacksRoutes);
app.use("/user", userRoutes);

Service Oriented Architecture in node.js

I am building a Node.js project which has an advanced folder structure for future purposes.
user.register controller:
exports.register = async (req, res) => {
try {
var isValidated = await userService.validateInDatabase(req);
if (!isValidated)
return res
.status(409)
.json({ error: "Phone number or email is already registered" });
var user = await userService.create(req);
return user
} catch (e) {
console.trace(e);
return res.status(400).json({ message: e.message });
}
};
The services file code:
exports.create = async (user) => {
const hashedPassword = passwordHash.generate(user.password);
let new_user = new User({
phoneNumber,
email,
password: hashedPassword,
});
const payload = {
id: new_user._id,
};
let token = jwt.sign(payload, keys.JWToken, { expiresIn: 31556926 });
const userData = await new_user.save();
return userData;
};
exports.validateInDatabase = async (req) => {
let check_user = await User.findOne({
$or: [{ email: req.body.email }, { phoneNumber: req.body.phoneNumber }],
});
if (check_user) return false;
return true;
};
Now, whenever I send the request from the postman it says invalid password Why is that?

How to change const values while testing with Jest

Hello I'm working on writing a test for my node.js API and I'm running into an issue. I'm validating if an email exists inside of my code using "const = emailCount". If it does exists it returns an error JSON. If it does NOT exist it returns a success JSON. However I'm not sure how to mock the internal constant I declared in my code.
Here is the code:
async function registerUser(req, res) {
// Request
const email = req.body.email;
const password = req.body.password;
const firstName = req.body.firstName;
const lastName = req.body.lastName;
const inviteCode = req.body.inviteCode;
let errMessage = [];
if (!firstName) {
errMessage.push("first Name Required")
}
if (!lastName) {
errMessage.push("last Name Required")
}
if (!inviteCode) {
errMessage.push("inviteCode Required")
}
if (!email) {
errMessage.push("email Required")
}
if (!password) {
errMessage.push("password Required")
}
if (errMessage.length > 0) {
res.json({ code: "422", message: errMessage })
}
const accessToken = jwt.sign({
email: email,
firstName: firstName,
lastName: lastName
}, config.jwtSecret);
const emailCount = await db.doesEmailExists(email)
if (emailCount.doesEmailExists > 0) {
res.json({ Errors: "Account already exists" })
} else {
db.createUser({
username: email,
hashedPassword: password,
firstName: firstName,
lastName: lastName,
}).then(data => {
res.json({
id: data.insertId,
firstName: firstName,
lastName: lastName,
token: accessToken,
role: 'user'
})
}).catch(err => res.json({ Error: err }))
}
}
Here is my test code
test('POST /user/register', async () => {
//use super test to send post method with json payload of newUser
const res = await agent.post('/user/register').send(newUser);
expect(res.statusCode).toEqual(200)
expect(res.body).toHaveProperty('Errors') || expect(res.body).toHaveProperty('token');
})
Ultimately I want to change the value of emailCount within my test if possible to test for different responses if there is a user and if there is NOT a user.
You should not mock your code, but rather your dependencies and db is exactly that.
For example you can write your test scenario like this:
const db = require('./path/to/db.js');
// auto-create mock
jest.mock('./path/to/db.js')
describe('POST /user/register', () => {
describe('when email Exists'), () => {
// defining the "res" object here
// will allow you to execute the request one
// and separate the expectations in different
// test cases, which will provide better visibility
// on what exactly have failed (in the future)
let res;
beforeAll(async () => {
db.doesEmailExists.mockResolvedValue({
doesEmailExists: 789
});
res = await agent.post('/user/register').send(newUser);
});
it('should probably return something more than 200', () => {
expect(res.statusCode).toBeGreaterThanOrEqual(200)
});
it('should return Error in response Body', () => {
expect(res.body).toHaveProperty('Errors')
});
});
describe('when email DOES NOT Exists'), () => {
let res;
beforeAll(async () => {
db.doesEmailExists.mockResolvedValue({
doesEmailExists: 0
});
res = await agent.post('/user/register').send(newUser);
});
it('should probably return statusCode 200', () => {
expect(res.statusCode).toEqual(200)
});
it('should return token', () => {
expect(res.body).toHaveProperty('token')
});
});
});
Note: you'll also need to mock the return value of db.createUser as the auto-mock will generate a jest.fn() which returns undefined

bcrypt-nodejs compare function always return false

I'm having problem with bcrypt-nodejs' compare function.
The compare function is returning the false value even the password is the right one.
I've tried everything I could and I don't know the what is wrong with my code.
My Folder Structure
src
-config
-config.js
-controller
-AuthenticationController.js
-models
-index.js
-User.js
-policies
-AuthenticationControllerPolicy.js
app.js
routes.js
package.json
I think the problem is with the User.js in models folder.
User.js
const Promise = require('bluebird')
const bcrypt = Promise.promisifyAll(require('bcrypt-nodejs'))
function hashPassword (user, options) {
const SALT_FACTOR = 8
if (!user.changed('password')) {
return
}
return bcrypt
.genSaltAsync(SALT_FACTOR)
.then(salt => bcrypt.hashAsync(user.password, salt, null))
.then(hash => {
user.setDataValue('password', hash)
})
}
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('User', {
email: {
type: DataTypes.STRING,
unique: true
},
password: DataTypes.STRING
}, {
hooks: {
beforeCreate: hashPassword,
beforeUpdate: hashPassword,
beforeSave: hashPassword
}
})
User.prototype.comparePassword = function (password) {
return bcrypt.compareAsync(password, this.password)
}
User.associate = function (models) {
}
return User
}
AuthenticationController.js
const {User} = require('../models')
const jwt = require('jsonwebtoken')
const config = require('../config/config')
function jwtSignUser (user) {
const ONE_WEEK = 60 * 60 * 24 * 7
return jwt.sign(user, config.authentication.jwtSecret, {
expiresIn: ONE_WEEK
})
}
module.exports = {
async register (req, res) {
try {
const user = await User.create(req.body)
const userJson = user.toJSON()
res.send({
user: userJson
})
} catch (err) {
res.status(400).send({
error: 'This email account is already in use.'
})
}
},
async login (req, res) {
try {
const {email, password} = req.body
const user = await User.findOne({
where: {
email: email
}
})
console.log('user BEFORE', user)
if (!user) {
console.log('!user')
return res.status(403).send({
error: 'The login information was incorrect'
})
}
console.log('user AFTER', user)
const isPasswordValid = await user.comparePassword(password)
console.log('isPasswordValid BEFORE : ', isPasswordValid)
if (!isPasswordValid) {
console.log('isPasswordValid AFTER : ', isPasswordValid)
return res.status(403).send({
error: 'The login information was incorrect'
})
}
const userJson = user.toJSON()
res.send({
user: userJson,
token: jwtSignUser(userJson)
})
} catch (err) {
res.status(500).send({
error: 'An error has occured trying to log in'
})
}
}
}
route.js
const AuthenticationController = require('./controller/AuthenticationController')
const AuthenticationControllerPolicy = require('./policies/AuthenticationControllerPolicy')
module.exports = (app) => {
app.post('/register',
AuthenticationControllerPolicy.register,
AuthenticationController.register)
app.post('/login',
AuthenticationController.login)
}
You can also check the repo if you want.
GitHubRepo
The usage of bcrypt-nodejs appears to be correct. I would verify that both the password coming in and the hash in the database are what you expect them to be (particularly inside the comparePassword function) to rule out if it's a data issue or not.

Resources