Skip to content

Commit

Permalink
Merge pull request #825 from OneCommunityGlobal/abi_badgeController_p…
Browse files Browse the repository at this point in the history
…ost_test

Abi badge controller post method tests
  • Loading branch information
DiegoSalas27 authored Apr 3, 2024
2 parents 0286372 + ee33ef7 commit 86f1f81
Show file tree
Hide file tree
Showing 6 changed files with 407 additions and 42 deletions.
19 changes: 19 additions & 0 deletions requirements/badgeController/postBadge.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Check mark: ✅
Cross Mark: ❌

# Post Badge

> ## Positive case
1. ❌ Receives a POST request in the **/api/userProfile** route
2. ✅ Returns 201 if a badge is succesfully created.

> ## Negative case
1. ❌ Returns error 404 if the API does not exist
2. ✅ Returns 403 if the user does not have badge permissions
3. ✅ Returns 400 if another badge with name already exists
4. ✅ Returns 500 if any error occurs when finding a badge
5. ✅ Returns 500 if any error occurs when saving the new badge

> ## Edge case
82 changes: 40 additions & 42 deletions src/controllers/badgeController.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
const moment = require('moment-timezone');
const mongoose = require('mongoose');
const UserProfile = require('../models/userProfile');
const { hasPermission } = require('../utilities/permissions');
const helper = require('../utilities/permissions');
const escapeRegex = require('../utilities/escapeRegex');
const cache = require('../utilities/nodeCache')();
const logger = require('../startup/logger');
const cacheClosure = require('../utilities/nodeCache');

const badgeController = function (Badge) {
/**
* getAllBadges handles badges retrieval.
* @param {Object} req - Request object.
* @returns {Array<Object>} List containing badge records.
*/
const cache = cacheClosure();

const getAllBadges = async function (req, res) {
if (!(await hasPermission(req.body.requestor, 'seeBadges'))) {
if (!(await helper.hasPermission(req.body.requestor, 'seeBadges'))) {
res.status(403).send('You are not authorized to view all badge data.');
return;
}
Expand Down Expand Up @@ -52,7 +52,7 @@ const badgeController = function (Badge) {
*/

const assignBadges = async function (req, res) {
if (!(await hasPermission(req.body.requestor, 'assignBadges'))) {
if (!(await helper.hasPermission(req.body.requestor, 'assignBadges'))) {
res.status(403).send('You are not authorized to assign badges.');
return;
}
Expand Down Expand Up @@ -98,9 +98,12 @@ const badgeController = function (Badge) {
const combinedEarnedDate = [...grouped[badge].earnedDate, ...item.earnedDate];
const timestampArray = combinedEarnedDate.map((date) => new Date(date).getTime());
timestampArray.sort((a, b) => a - b);
grouped[badge].earnedDate = timestampArray.map((timestamp) => new Date(timestamp).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: '2-digit' })
.replace(/ /g, '-')
.replace(',', ''));
grouped[badge].earnedDate = timestampArray.map((timestamp) =>
new Date(timestamp)
.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: '2-digit' })
.replace(/ /g, '-')
.replace(',', ''),
);
}
}

Expand All @@ -123,33 +126,34 @@ const badgeController = function (Badge) {
}

record
.save()
.then((results) => {
res.status(201).send(results._id);
})
.catch((err) => {
res.status(500).send(`Internal Error: Badge Collection. ${err.message}`);
});
.save()
.then((results) => {
res.status(201).send(results._id);
})
.catch((err) => {
res.status(500).send(`Internal Error: Badge Collection. ${err.message}`);
});
});
};

const postBadge = async function (req, res) {
if (!(await hasPermission(req.body.requestor, 'createBadges'))) {
res
.status(403)
.send({ error: 'You are not authorized to create new badges.' });
if (!(await helper.hasPermission(req.body.requestor, 'createBadges'))) {
res.status(403).send({ error: 'You are not authorized to create new badges.' });
return;
}

Badge.find({
badgeName: { $regex: escapeRegex(req.body.badgeName), $options: 'i' },
}).then((result) => {
try {
const result = await Badge.find({
badgeName: { $regex: escapeRegex(req.body.badgeName), $options: 'i' },
});

if (result.length > 0) {
res.status(400).send({
error: `Another badge with name ${result[0].badgeName} already exists. Sorry, but badge names should be like snowflakes, no two should be the same. Please choose a different name for this badge so it can be proudly unique.`,
});
return;
}

const badge = new Badge();

badge.badgeName = req.body.badgeName;
Expand All @@ -166,24 +170,20 @@ const badgeController = function (Badge) {
badge.description = req.body.description;
badge.showReport = req.body.showReport;

badge
.save()
.then((results) => {
// remove cache after new badge is saved
if (cache.getCache('allBadges')) {
cache.removeCache('allBadges');
}
res.status(201).send(results);
})
.catch((errors) => res.status(500).send(errors));
});
const newBadge = await badge.save();
// remove cache after new badge is saved
if (cache.getCache('allBadges')) {
cache.removeCache('allBadges');
}
res.status(201).send(newBadge);
} catch (error) {
res.status(500).send(error);
}
};

const deleteBadge = async function (req, res) {
if (!(await hasPermission(req.body.requestor, 'deleteBadges'))) {
res
.status(403)
.send({ error: 'You are not authorized to delete badges.' });
if (!(await helper.hasPermission(req.body.requestor, 'deleteBadges'))) {
res.status(403).send({ error: 'You are not authorized to delete badges.' });
return;
}
const { badgeId } = req.params;
Expand Down Expand Up @@ -217,10 +217,8 @@ const badgeController = function (Badge) {
};

const putBadge = async function (req, res) {
if (!(await hasPermission(req.body.requestor, 'updateBadges'))) {
res
.status(403)
.send({ error: 'You are not authorized to update badges.' });
if (!(await helper.hasPermission(req.body.requestor, 'updateBadges'))) {
res.status(403).send({ error: 'You are not authorized to update badges.' });
return;
}
const { badgeId } = req.params;
Expand Down
212 changes: 212 additions & 0 deletions src/controllers/badgeController.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
// const mongoose = require('mongoose');
// const UserProfile = require('../models/userProfile');
const Badge = require('../models/badge');
const helper = require('../utilities/permissions');
const escapeRegex = require('../utilities/escapeRegex');
const badgeController = require('./badgeController');
const { mockReq, mockRes, assertResMock } = require('../test');

// mock the cache function before importing so we can manipulate the implementation
jest.mock('../utilities/nodeCache');
const cache = require('../utilities/nodeCache');

const makeSut = () => {
const { postBadge } = badgeController(Badge);

return { postBadge };
};

const mockHasPermission = (value) =>
jest.spyOn(helper, 'hasPermission').mockImplementationOnce(() => Promise.resolve(value));

const makeMockCache = (method, value) => {
const cacheObject = {
getCache: jest.fn(),
removeCache: jest.fn(),
hasCache: jest.fn(),
setCache: jest.fn(),
};

const mockCache = jest.spyOn(cacheObject, method).mockImplementationOnce(() => value);

cache.mockImplementationOnce(() => cacheObject);

return { mockCache, cacheObject };
};

describe('badeController module', () => {
beforeEach(() => {
mockReq.body.badgeName = 'random badge';
mockReq.body.category = 'Food';
mockReq.body.type = 'No Infringement Streak';
mockReq.body.multiple = 3;
mockReq.body.totalHrs = 55;
mockReq.body.weeks = 1;
mockReq.body.months = 2;
mockReq.body.people = 10;
mockReq.body.project = '601acda376045c7879d13a74';
mockReq.body.imageUrl = 'https://randomURL.com';
mockReq.body.ranking = 3;
mockReq.body.description = 'Any description';
mockReq.body.showReport = true;
mockReq.params.badgeId = '5a7ccd20fde60f1f1857ba16';
});

afterEach(() => {
jest.clearAllMocks();
});

describe('postBadge method', () => {
test('Returns 403 if the user does not have badge permissions', async () => {
const { postBadge } = makeSut();

const hasPermissionSpy = mockHasPermission(false);

const response = await postBadge(mockReq, mockRes);

expect(hasPermissionSpy).toHaveBeenCalledWith(mockReq.body.requestor, 'createBadges');
assertResMock(
403,
{
error: 'You are not authorized to create new badges.',
},
response,
mockRes,
);
});

test('Returns 400 if another badge with name already exists', async () => {
const { postBadge } = makeSut();

const hasPermissionSpy = mockHasPermission(true);

const badgeArray = [{ badgeName: 'asdf' }];

const findSpy = jest
.spyOn(Badge, 'find')
.mockImplementationOnce(() => Promise.resolve(badgeArray));

const response = await postBadge(mockReq, mockRes);

expect(findSpy).toHaveBeenCalledWith({
badgeName: { $regex: escapeRegex(mockReq.body.badgeName), $options: 'i' },
});

expect(hasPermissionSpy).toHaveBeenCalledWith(mockReq.body.requestor, 'createBadges');

assertResMock(
400,
{
error: `Another badge with name ${badgeArray[0].badgeName} already exists. Sorry, but badge names should be like snowflakes, no two should be the same. Please choose a different name for this badge so it can be proudly unique.`,
},
response,
mockRes,
);
});

test('Returns 500 if any error occurs when finding a badge', async () => {
const { postBadge } = makeSut();
const errorMsg = 'Error when finding badge';
const hasPermissionSpy = mockHasPermission(true);

jest.spyOn(Badge, 'find').mockImplementationOnce(() => Promise.reject(new Error(errorMsg)));

const response = await postBadge(mockReq, mockRes);

expect(hasPermissionSpy).toHaveBeenCalledWith(mockReq.body.requestor, 'createBadges');
assertResMock(500, new Error(errorMsg), response, mockRes);
});

test('Returns 500 if any error occurs when saving the new badge', async () => {
const { postBadge } = makeSut();
const errorMsg = 'Error when saving badge';
const hasPermissionSpy = mockHasPermission(true);

const findSpy = jest.spyOn(Badge, 'find').mockImplementationOnce(() => Promise.resolve([]));

jest
.spyOn(Badge.prototype, 'save')
.mockImplementationOnce(() => Promise.reject(new Error(errorMsg)));

const response = await postBadge(mockReq, mockRes);

expect(hasPermissionSpy).toHaveBeenCalledWith(mockReq.body.requestor, 'createBadges');
expect(findSpy).toHaveBeenCalledWith({
badgeName: { $regex: escapeRegex(mockReq.body.badgeName), $options: 'i' },
});
assertResMock(500, new Error(errorMsg), response, mockRes);
});

test('Returns 201 if a badge is succesfully created and no badges in cache.', async () => {
const { mockCache: getCacheMock } = makeMockCache('getCache', '');
const { postBadge } = makeSut();
const hasPermissionSpy = mockHasPermission(true);

const findSpy = jest.spyOn(Badge, 'find').mockImplementationOnce(() => Promise.resolve([]));

const newBadge = {
badgeName: mockReq.body.badgeName,
category: mockReq.body.category,
multiple: mockReq.body.multiple,
totalHrs: mockReq.body.totalHrs,
weeks: mockReq.body.weeks,
months: mockReq.body.months,
people: mockReq.body.people,
project: mockReq.body.project,
imageUrl: mockReq.body.imageUrl,
ranking: mockReq.body.ranking,
description: mockReq.body.description,
showReport: mockReq.body.showReport,
};

jest.spyOn(Badge.prototype, 'save').mockImplementationOnce(() => Promise.resolve(newBadge));

const response = await postBadge(mockReq, mockRes);

expect(getCacheMock).toHaveBeenCalledWith('allBadges');
expect(hasPermissionSpy).toHaveBeenCalledWith(mockReq.body.requestor, 'createBadges');
expect(findSpy).toHaveBeenCalledWith({
badgeName: { $regex: escapeRegex(mockReq.body.badgeName), $options: 'i' },
});
assertResMock(201, newBadge, response, mockRes);
});

test('Clears cache if all is successful and there is a badge cache', async () => {
const { mockCache: getCacheMock, cacheObject } = makeMockCache('getCache', '[{_id: 1}]');
const removeCacheMock = jest
.spyOn(cacheObject, 'removeCache')
.mockImplementationOnce(() => null);
const { postBadge } = makeSut();
const hasPermissionSpy = mockHasPermission(true);

const findSpy = jest.spyOn(Badge, 'find').mockImplementationOnce(() => Promise.resolve([]));

const newBadge = {
badgeName: mockReq.body.badgeName,
category: mockReq.body.category,
multiple: mockReq.body.multiple,
totalHrs: mockReq.body.totalHrs,
weeks: mockReq.body.weeks,
months: mockReq.body.months,
people: mockReq.body.people,
project: mockReq.body.project,
imageUrl: mockReq.body.imageUrl,
ranking: mockReq.body.ranking,
description: mockReq.body.description,
showReport: mockReq.body.showReport,
};

jest.spyOn(Badge.prototype, 'save').mockImplementationOnce(() => Promise.resolve(newBadge));

const response = await postBadge(mockReq, mockRes);

expect(getCacheMock).toHaveBeenCalledWith('allBadges');
expect(removeCacheMock).toHaveBeenCalledWith('allBadges');
expect(hasPermissionSpy).toHaveBeenCalledWith(mockReq.body.requestor, 'createBadges');
expect(findSpy).toHaveBeenCalledWith({
badgeName: { $regex: escapeRegex(mockReq.body.badgeName), $options: 'i' },
});
assertResMock(201, newBadge, response, mockRes);
});
});
});
Loading

0 comments on commit 86f1f81

Please sign in to comment.