From a1fe992ab9bd7de39ec39dbe7b8f4f6d498007b4 Mon Sep 17 00:00:00 2001 From: JamesNg Date: Tue, 6 May 2025 14:26:15 -0400 Subject: [PATCH] wrote unit tests for project team members router, fixed bug in GET /project/:id/:userId route --- backend/globalConfig.json | 2 +- backend/routers/projectTeamMembers.router.js | 37 ++- .../routers/projectTeamMembers.router.test.js | 264 ++++++++++++++++++ 3 files changed, 283 insertions(+), 20 deletions(-) create mode 100644 backend/routers/projectTeamMembers.router.test.js diff --git a/backend/globalConfig.json b/backend/globalConfig.json index ae7585862..62fe238b0 100644 --- a/backend/globalConfig.json +++ b/backend/globalConfig.json @@ -1 +1 @@ -{"mongoUri":"mongodb://127.0.0.1:56074/jest?","mongoDBName":"jest"} \ No newline at end of file +{"mongoUri":"mongodb://127.0.0.1:36659/jest?","mongoDBName":"jest"} \ No newline at end of file diff --git a/backend/routers/projectTeamMembers.router.js b/backend/routers/projectTeamMembers.router.js index 8d5c28425..24712831e 100644 --- a/backend/routers/projectTeamMembers.router.js +++ b/backend/routers/projectTeamMembers.router.js @@ -1,12 +1,12 @@ -const express = require("express"); +const express = require('express'); const router = express.Router(); const { ProjectTeamMember } = require('../models/projectTeamMember.model'); // GET /api/projectteammembers/ -router.get("/", (req, res) => { +router.get('/', (req, res) => { ProjectTeamMember.find() - .populate("userId") + .populate('userId') .then((teamMembers) => { return res.status(200).send(teamMembers); }) @@ -16,9 +16,9 @@ router.get("/", (req, res) => { }); }); -router.get("/:id", (req, res) => { +router.get('/:id', (req, res) => { ProjectTeamMember.find({ projectId: req.params.id }) - .populate("userId") + .populate('userId') .then((teamMembers) => { return res.status(200).send(teamMembers); }) @@ -28,14 +28,14 @@ router.get("/:id", (req, res) => { }); }); -router.get("/project/:id/:userId", (req, res) => { +router.get('/project/:id/:userId', (req, res) => { ProjectTeamMember.find({ projectId: req.params.id, userId: req.params.userId, }) - .populate("userId") + .populate('userId') .then((teamMember) => { - if (!teamMember.length) { + if (!teamMember.projectId) { return res.sendStatus(400); } else { return res.status(200).send(teamMember); @@ -47,12 +47,12 @@ router.get("/project/:id/:userId", (req, res) => { }); }); -router.get("/projectowner/:id", (req, res) => { +router.get('/projectowner/:id', (req, res) => { const id = req.params.id; ProjectTeamMember.findOne({ userId: id }) - .populate("userId") - .populate("projectId") + .populate('userId') + .populate('projectId') .then((teamMember) => { teamMember.vrmsProjectAdmin === true ? res.status(200).send(teamMember) @@ -63,22 +63,21 @@ router.get("/projectowner/:id", (req, res) => { }); }); -router.post("/", (req, res) => { +router.post('/', (req, res) => { ProjectTeamMember.create(req.body) - .then((teamMember) => { - return res.status(201).send(teamMember); - }) - .catch((err) => { + .then((teamMember) => { + return res.status(201).send(teamMember); + }) + .catch((err) => { console.log(err); return res.sendStatus(400); }); }); -router.patch("/:id", (req, res) => { +router.patch('/:id', (req, res) => { ProjectTeamMember.findByIdAndUpdate(req.params.id, req.body) .then((edit) => res.json(edit)) - .catch((err) => - res.sendStatus(400)); + .catch((err) => res.sendStatus(400)); // }; }); diff --git a/backend/routers/projectTeamMembers.router.test.js b/backend/routers/projectTeamMembers.router.test.js new file mode 100644 index 000000000..3d7ede175 --- /dev/null +++ b/backend/routers/projectTeamMembers.router.test.js @@ -0,0 +1,264 @@ +// Mock and import project team model +jest.mock('../models/projectTeamMember.model'); +const { ProjectTeamMember } = require('../models'); + +// Import project team router +const ProjectTeamRouter = require('./projectTeamMembers.router'); + +// Create test app with express +const express = require('express'); +const supertest = require('supertest'); +const testapp = express(); +// Allow for body parsing in test +testapp.use(express.json()); +testapp.use('/api/projectteammembers', ProjectTeamRouter); +const request = supertest(testapp); + +describe('Unit testing for project team members router', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('READ', () => { + // Mock project team members + const mockMembers = [ + { + id: 1, + userId: '1', + projectId: '1', + teamMemberStatus: 'active', + vrmsProjectAdmin: false, + roleOnProject: 'Developer', + joinedDate: Date.now(), + leftDate: Date.now(), + leftReason: 'n/a', + githubPermissionLevel: 'admin', + onProjectGithub: true, + onProjectGoogleDrive: true, + }, + { + id: 2, + userId: '2', + projectId: '2', + teamMemberStatus: 'active', + vrmsProjectAdmin: true, + roleOnProject: 'Project Manager', + joinedDate: Date.now(), + leftDate: Date.now(), + leftReason: 'n/a', + githubPermissionLevel: 'admin', + onProjectGithub: true, + onProjectGoogleDrive: true, + }, + ]; + + it('should return a list of project team members with GET /api/projectteammembers/', async (done) => { + // Mock resolved value of ProjectTeamMember.find() method + ProjectTeamMember.find.mockReturnValue({ + populate: jest.fn((path) => { + if (path !== 'userId') throw new Error('Incorrect populate path'); + return { + then: (callBack) => callBack(mockMembers), + }; + }), + }); + + // Mock API response + const response = await request.get('/api/projectteammembers'); + + // Tests + expect(ProjectTeamMember.find().populate).toHaveBeenCalledWith('userId'); + expect(response.status).toBe(200); + expect(response.body).toEqual(mockMembers); + + // Marks completion of tests + done(); + }); + + it('should return a single project team member based on projectId with GET /api/projectteammembers/:id', async (done) => { + const mockMember = mockMembers[0]; + const projectId = mockMember.projectId; + + // Mock resolved value of ProjectTeamMember.find({ projectId }) method + ProjectTeamMember.find.mockReturnValue({ + populate: jest.fn((path) => { + if (path !== 'userId') throw new Error('Incorrect populate path'); + return { + then: (callBack) => callBack(mockMember), + }; + }), + }); + + // Mock GET API response + const response = await request.get(`/api/projectteammembers/${projectId}`); + + // Tests + expect(ProjectTeamMember.find).toHaveBeenCalledWith({ projectId: projectId }); + expect(ProjectTeamMember.find().populate).toHaveBeenCalledWith('userId'); + expect(response.status).toBe(200); + expect(response.body).toEqual(mockMember); + + // Marks completion of tests + done(); + }); + + it('should return a specific project team member based on projectId and userId with GET /api/projectteammembers/project/:id/:userId', async (done) => { + const mockMember = mockMembers[1]; + const projectId = mockMember.projectId; + const userId = mockMember.userId; + + // Mock resolved value of ProjectTeamMember.find({ projectId, userId }) method + ProjectTeamMember.find.mockReturnValue({ + populate: jest.fn((path) => { + if (path !== 'userId') throw new Error('Incorrect populate path'); + return { + then: (callBack) => callBack(mockMember), + }; + }), + }); + + // Mock GET API response + const response = await request.get(`/api/projectteammembers/project/${projectId}/${userId}`); + + // Tests + expect(ProjectTeamMember.find).toHaveBeenCalledWith({ projectId: projectId, userId: userId }); + expect(ProjectTeamMember.find().populate).toHaveBeenCalledWith('userId'); + expect(response.status).toBe(200); + expect(response.body).toEqual(mockMember); + + // Marks completion of tests + done(); + }); + + it('should return a specific project owner that IS an vrmsProjectAdmin on userId with GET /api/projectteammembers/projectowner/:id', async (done) => { + const mockMember = mockMembers[1]; + const userId = mockMember.userId; + + // Mock resolved value of ProjectTeamMember.findOne() method + ProjectTeamMember.findOne.mockImplementation(() => ({ + populate: jest.fn().mockImplementationOnce((path) => { + if (path !== 'userId') throw new Error('Incorrect first populate path'); + return { + populate: jest.fn().mockImplementationOnce((path) => { + if (path !== 'projectId') throw new Error('Incorrect second populate path'); + return { + then: (callback) => callback(mockMember), + }; + }), + }; + }), + })); + + // Mock GET API response + const response = await request.get(`/api/projectteammembers/projectowner/${userId}`); + + // Tests + expect(ProjectTeamMember.findOne).toHaveBeenCalledWith({ userId: userId }); + expect(response.status).toBe(200); + expect(response.body).toEqual(mockMember); + expect(response.body.vrmsProjectAdmin).toBe(true); + + // Marks completion of tests + done(); + }); + + it('should return a project team member that IS NOT a vrmsProjectAdmin based on userId with GET /api/projectteammembers/projectowner/:id', async (done) => { + const mockMember = mockMembers[0]; + const userId = mockMember.userId; + + // Mock resolved value of ProjectTeamMember.findOne() method + ProjectTeamMember.findOne.mockImplementation(() => ({ + populate: jest.fn().mockImplementationOnce((path) => { + if (path !== 'userId') throw new Error('Incorrect first populate path'); + return { + populate: jest.fn().mockImplementationOnce((path) => { + if (path !== 'projectId') throw new Error('Incorrect second populate path'); + return { + then: (callback) => callback(mockMember), + }; + }), + }; + }), + })); + + // Mock GET API response + const response = await request.get(`/api/projectteammembers/projectowner/${userId}`); + + // Tests + expect(ProjectTeamMember.findOne).toHaveBeenCalledWith({ userId: userId }); + expect(response.status).toBe(200); + expect(response.body).toEqual(false); + + // Marks completion of tests + done(); + }); + }); + + describe('CREATE', () => { + // New mock member + const newMember = { + id: 3, + userId: '3', + projectId: '3', + teamMemberStatus: 'active', + vrmsProjectAdmin: true, + roleOnProject: 'UX', + joinedDate: Date.now(), + leftDate: Date.now(), + leftReason: 'project completed', + githubPermissionLevel: 'Read', + onProjectGithub: true, + onProjectGoogleDrive: true, + }; + + it('should create and return a new project team member with POST /api/projectteammember/', async (done) => { + // Mock ProjectTeamMember.create() method + ProjectTeamMember.create.mockResolvedValue(newMember); + + // Mock POST API response + const response = await request.post('/api/projectteammembers/').send(newMember); + + // Tests + expect(response.status).toBe(201); + expect(response.body).toEqual(newMember); + + // Marks completion of tests + done(); + }); + }); + + describe('UPDATE', () => { + // Updated mock member + const updatedMember = { + id: '3', + userId: '1', + projectId: '2', + teamMemberStatus: 'active', + vrmsProjectAdmin: true, + roleOnProject: 'Data Science', + joinedDate: Date.now(), + leftDate: Date.now(), + leftReason: 'project paused', + githubPermissionLevel: 'Triage', + onProjectGithub: true, + onProjectGoogleDrive: false, + }; + const id = updatedMember.id; + + it('should update the updated project team member with PATCH /api/projectteammember/:id', async (done) => { + // Mock ProjectTeamMember.create() method + ProjectTeamMember.findByIdAndUpdate.mockResolvedValue(updatedMember); + + // Call PATCH API response + const response = await request.patch(`/api/projectteammembers/${id}`).send(updatedMember); + + // Tests + expect(ProjectTeamMember.findByIdAndUpdate).toHaveBeenCalledWith(id, updatedMember); + expect(response.status).toBe(200); + expect(response.body).toEqual(updatedMember); + + // Marks completion of tests + done(); + }); + }); +});