"Bad request" error when using params in supertest and jest - node.js

I have the following controller for updating project which gets id from param.
const updateProject = async (req: Request, res: Response, next: NextFunction) => {
const { title, desc } = req.body;
const { projectID } = req.params;
const { user } = <any>req;
if (!isValidObjectId(projectID)) {
return next(new ErrorResponse("Invalid project ID", 400));
}
try {
const project = await Project.findOneAndUpdate(
{ _id: projectID, user: user._id },
{
title: title,
desc: desc,
},
{ new: true }
);
if (!project) {
return next(new ErrorResponse("Project not found", 404));
}
res.status(200).json(project);
} catch (err) {
next(err);
}
};
I've written a test for this controller but I keep getting "Bad Request". But when I test it manually using Postman, it works fine. And other tests that do not require params work fine too.
import mongoose from "mongoose";
const projectOneId = new mongoose.Types.ObjectId();
export const projectOne = {
_id: projectOneId,
title: "First Project",
desc: "This is the first project",
user: userOneId,
};
describe("/api/projects", () => {
//login using cookie before running the test
let cookie: string;
beforeEach(async () => {
await request(app)
.post("/api/auth/login")
.send(userOne)
.expect(200)
.then((res) => {
const cookies = res.headers["set-cookie"][0]
.split(",")
.map((item: string) => item.split(";")[0]);
cookie = cookies.join(";");
});
});
describe("PUT /api/projects/:projectID", () => {
it("Should update a project", async () => {
await request(app)
.put(`/api/projects/:projectID`)
.set("Cookie", cookie)
.query({ projectID: projectOne._id.toString() })
.send(projectOne)
.expect(200);
});
});

Related

axios get method doesn't send any data in to my node backend using react native

When I console.log the variable refToken before the get request the refToken shows what it contains but after that nothing, and my backend sends me the error 401 which means no token was provided!!
I am really confused here
utility file for expo-secure-store
import * as SecureStore from 'expo-secure-store';
const saveRefreshToken = async (value: string) => {
try {
await SecureStore.setItemAsync("refreshToken", value);
} catch (e) {
console.log("Cannot set refresh token")
}
}
const getRefreshToken = async () => {
try {
return await SecureStore.getItemAsync("refreshToken");
} catch (e) {
console.log("can't get requested refreshToken", e);
}
}
const deleteRefreshToken = async () => {
try {
await SecureStore.deleteItemAsync("refreshToken");
} catch (e) {
console.log("cannot delete refreshToken ", e);
}
}
export default {
saveRefreshToken,
getRefreshToken,
deleteRefreshToken
};
React native code
import axios from "../api/axios";
import useAuth from "./useAuth";
import storage from "../utility/storage";
import jwtDecode from "jwt-decode";
const useRefreshToken = () => {
const {setAuth, auth} = useAuth();
return async () => {
const refToken = await storage.getRefreshToken();
// withCredentials: true,
const response = await axios.get(`/auth/refresh/`, {
params: {refreshToken: refToken},
});
//#ts-ignore
setAuth({user: jwtDecode(response.data.accessToken), accessToken: response.data.accessToken});
return response.data.accessToken;
};
}
export default useRefreshToken;
Node.js backend part that deals with this request
// TODO: Generate new access token from refresh
export const refreshTokenSignIn = (
req: express.Request,
res: express.Response,
next: express.NextFunction
) => {
// const refreshToken = req.cookies.jwt;
const refreshToken = req.body.refreshToken;
try {
// Check if the refresh token is not null
if (!refreshToken)
return res.status(401).json({message: "No token provided!"});
// console.log(refreshToken);
const sql = `select * from token_records where refresh_token = '${refreshToken}'`;
// Check if there is such refresh token for the current user
db.query(
sql,
[refreshToken],
(err: QueryError, result: RowDataPacket[]) => {
if (err) return next(err);
if (result.length === 0)
return res.status(403).json({message: "You don't have access"});
//#ts-ignore
jwt.verify(
refreshToken,
//#ts-ignore
process.env.JWT_REFRESH_SECRET,
(err2: VerifyErrors, user: JwtPayload) => {
if (err2) return res.status(403).json({message: "You don't have access"});
const accessToken = jwt.sign(
{
user_id: user.user_id,
firstname: user.firstname,
lastname: user.lastname,
username: user.username,
email: user.email,
role: user.role,
},
//#ts-ignore
process.env.JWT_SECRET
);
res.status(200).json({accessToken: accessToken});
next();
}
);
}
);
} catch (e) {
console.log(e);
next();
}
};

how to emit socket io to specific logged in user?

I am using socket.io with express and typescript, when want to emit to particular logged in user it is not working, the rest is working fine, when new user join and other things, in App.ts in backend it looks like:
httpServer.listen(8000, () => {
console.log(`app is running on: http://localhost:8000`);
});
//SocketIO
const io = new Server(httpServer, {
cors: {
credentials: true,
},
});
app.set("socketio", io);
io.use((socket: SocketWithUser, next: any) => {
const token: any = socket.handshake.query.token;
if (token) {
try {
const payload = jwt.verify(
token,
<string>process.env.JWT_TOKEN
) as DataStoredInToken;
socket.userId = payload._id;
return next();
} catch (err) {
next(err);
}
} else {
return next(new Error("Access Denied"));
}
});
io.on("connection", (socket: SocketWithUser) => {
if (socket.userId) {
socket.join(socket.userId);
socket.emit("joined", `user ${socket.userId} joined`);
}
socket.on("disconnect", () => {
console.log("disconnect");
});
});
and in another route sockethandler
import { Request } from "express";
import { NotificationProps } from "types/notification";
export const sendNotification = (
req: Request,
notification: NotificationProps
) => {
const io = req.app.get("socketio");
io.sockets
.in(String(`${notification.receiver}`))
.emit("newNotification", notification);
};
the like post route looks like
export const likePost = async (req: RequestWithUser, res: Response) => {
const { postId } = req.body;
const post = await PostModel.findById(postId);
if (!post) return res.status(400).send({ msg: `post does not exist` });
const checkIfLiked = post.likes.find(
(item: any) => String(item.user._id) === String(req.user_id)
);
if (!checkIfLiked) {
await post.updateOne(
{
$push: { likes: { user: req.user_id } },
},
{ new: true }
);
const notification = new Notification({
sender: req.user_id,
receiver: post.user,
notificaitonType: "like",
});
await notification.save();
sendNotification(req, notification);
return res.status(200).send({ success: true });
}
const postWithOutLike = await post.updateOne(
{
$pull: { likes: { user: req.user_id } },
},
{ new: true }
);
return res.status(200).send({ postWithOutLike });
};
in the frontend react app just calling it like:
socketIo().on("newNotification", (data) => {
console.log({ data });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
any help please?
I believe you need want io.in
// to all clients in room
io.in(notification.receiver).emit('newNotification', notification);
Ref

testing authenticated express endpoint with superagent does not work?

I am testing my application however I could not figure out how to test the authenticated endpoints with supertest, based on documentation i wrote this, but it is not working. Can someone help me how to test authenticated endpoints: this is my code:
const request = require("supertest");
const app = require("../../server/app");
let testSession = null;
beforeEach(function () {
testSession = request.agent(app);
});
test("Should add new blogs", async () => {
await testSession
.post("/api/v1/blogs")
.send({
title: "first test",
location: "ny",
position: "developer",
})
.expect(200);
});
here is the result:
expected 200 "OK", got 401 "Unauthorized"
endpoints are working but here are the controller and route:
exports.createBlog = (req, res) => {
//key comes from client
//because if other user send the request I dont wanna lock the request for other one
//we send the key with the save button in the controller menu
const lockId = req.query.lockId;
let blog;
if (!lock.isBusy(lockId)) {
lock
.acquire(lockId, async () => {
const blogData = req.body;
blog = await new Blog(blogData);
if (req.user) {
blog.userId = req.user.sub;
blog.author = req.user.name;
}
await blog.save();
})
.then(() =>
res.status(200).json({ message: "Blog is saved!", blog: blog })
)
.catch((e) => res.status(422).json({ message: e.message }));
} else {
return res.status(422).json({ message: "Blog is saving" });
}
router.post(
"/",
authService.checkJwt,
authService.checkRole("siteOwner"),
blogCtrl.createBlog
);

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

The loader.load() function must be called with a value,but got: undefined

I am following this graphql tutorial, everything was going ok until I try to use dataloaders.
My server.js is:
const start = async () => {
const mongo = await connectMongo();
const buildOptions = async req => {
const user = await authenticate(req, mongo.Users);
return {
context: {
dataloaders: buildDataloaders(mongo),
mongo,
user
},
schema
};
};
app.use('/graphql', bodyParser.json(), graphqlExpress(buildOptions));
app.use(
'/graphiql',
graphiqlExpress({
endpointURL: '/graphql',
passHeader: `'Authorization': 'bearer token-name#email.com'`
})
);
app.use('/', expressStaticGzip('dist'));
app.use('/attendance', expressStaticGzip('dist'));
app.use('/login', expressStaticGzip('dist'));
spdy.createServer(sslOptions, app).listen(process.env.PORT || 8080, error => {
if (error) {
console.error(error);
return process.exit(1);
} else {
console.info(
`App available at https://localhost:${process.env.PORT || 3000}`
);
}
});
};
My copy and paste dataloaders.js:
const DataLoader = require('dataloader');
async function batchUsers(Users, keys) {
return await Users.find({ _id: { $in: keys } }).toArray();
}
module.exports = ({ Users }) => ({
userLoader: new DataLoader(keys => batchUsers(Users, keys), {
cacheKeyFn: key => key.toString()
})
});
And my resolvers.js:
export default {
Query: {
allLinks: async (root, data, { mongo: { Links } }) =>
Links.find({}).toArray()
},
Mutation: {
createLink: async (root, data, { mongo: { Links }, user }) => {
const newLink = Object.assign({ postedById: user && user._id }, data);
const response = await Links.insert(newLink);
return Object.assign({ id: response.insertedIds[0] }, newLink);
},
createUser: async (root, data, { mongo: { Users } }) => {
const newUser = {
name: data.name,
email: data.authProvider.email.email,
password: data.authProvider.email.password
};
const response = await Users.insert(newUser);
return Object.assign({ id: response.insertedIds[0] }, newUser);
},
signinUser: async (root, data, { mongo: { Users } }) => {
const user = await Users.findOne({ email: data.email.email });
if (data.email.password === user.password) {
return { token: `token-${user.email}`, user };
}
}
},
Link: {
id: root => root._id || root.id,
postedBy: async ({ postedById }, data, { dataloaders: { userLoader } }) => {
return await userLoader.load(postedById);
}
},
User: {
id: root => root._id || root.id
}
};
When I try get my allLinks I got the error:
TypeError: The loader.load() function must be called with a value,but
got: undefined.
Can anyone help me?
So I was able to reproduce the error by creating a link with a user, deleting the user from the Mongo database, and then querying for the postedBy attribute of the Link.
I would suggest dropping all your links and recreating your user (register + sign in), creating a new link, then querying for the postedBy field.

Resources