From e658c58317cde8e6d58e84170140f250032a86d3 Mon Sep 17 00:00:00 2001 From: Sebastian Webster Date: Sat, 25 Nov 2023 02:36:26 +1300 Subject: [PATCH 01/36] Added Notification schema --- models/Notification.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 models/Notification.js diff --git a/models/Notification.js b/models/Notification.js new file mode 100644 index 00000000..3401edcc --- /dev/null +++ b/models/Notification.js @@ -0,0 +1,15 @@ +const mongoose = require('mongoose'); +const Schema = mongoose.Schema; + +const NotificationSchema = new Schema({ + userId: mongoose.Types.ObjectId, + profilePublicId: String, + postId: mongoose.Types.ObjectId, + postFormat: String, + dateCreated: Date, + text: String +}) + +const Notification = mongoose.model('Notification', NotificationSchema); + +module.exports = Notification; \ No newline at end of file From 55f04f8bca56c190bf9c68b05160583670d88368 Mon Sep 17 00:00:00 2001 From: Sebastian Webster Date: Sat, 25 Nov 2023 02:55:13 +1300 Subject: [PATCH 02/36] Added temp/clearnotifications API --- controllers/Temp.js | 31 +++++++++++++++++++++++++++++++ routes/Temp.js | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/controllers/Temp.js b/controllers/Temp.js index 0f84594c..ee6377a7 100644 --- a/controllers/Temp.js +++ b/controllers/Temp.js @@ -12,6 +12,7 @@ const RefreshToken = require('../models/RefreshToken'); const Message = require('../models/Message'); const Comment = require('../models/Comment'); const CategoryMember = require('../models/CategoryMember'); +const Notification = require('../models/Notification'); const HTTPWTLibrary = require('../libraries/HTTPWT'); const CONSTANTS = require('../constants'); @@ -6493,6 +6494,32 @@ class TempController { }) } + static #clearnotifications = (userId) => { + return new Promise(resolve => { + if (typeof userId !== 'string') { + return resolve(HTTPWTHandler.badInput(`userId must be a string. Provided type: ${typeof userId}`)) + } + + if (!mongoose.isObjectIdOrHexString(userId)) { + return resolve(HTTPWTHandler.badInput('userId must be an ObjectId.')) + } + + User.findOne({_id: {$eq: userId}}).lean().then(userFound => { + if (!userFound) return resolve(HTTPWTHandler.notFound('Could not find user with provided userId.')) + + Notification.deleteMany({userId: {$eq: userId}}).then(() => { + return resolve(HTTPWTHandler.OK('Successfully deleted all notifications.')) + }).catch(error => { + console.error('An error occurred while deleting all notifications with userId:', userId, '. The error was:', error) + return resolve(HTTPWTHandler.serverError('An error occurred while deleting all notifications. Please try again.')) + }) + }).catch(error => { + console.error('An error occurred while finding one user with id:', userId, '. The error was:', error); + return resolve(HTTPWTHandler.serverError('An error occurred while finding user. Please try again.')) + }) + }) + } + static sendnotificationkey = async (userId, notificationKey, refreshTokenId) => { return await this.#sendnotificationkey(userId, notificationKey, refreshTokenId) } @@ -6816,6 +6843,10 @@ class TempController { static removevoteonpost = async (userId, postId, postFormat, voteType) => { return await this.#removevoteonpost(userId, postId, postFormat, voteType); } + + static clearnotifications = async (userId) => { + return await this.#clearnotifications(userId); + } } module.exports = TempController; diff --git a/routes/Temp.js b/routes/Temp.js index 69a5361a..8f921f2d 100644 --- a/routes/Temp.js +++ b/routes/Temp.js @@ -766,6 +766,15 @@ const rateLimiters = { message: {status: "FAILED", message: "You have removed too many votes from posts in the last minute. Please try again in 60 seconds."}, skipFailedRequests: true, keyGenerator: (req, res) => req.tokenData //Use req.tokenData (account _id in MongoDB) to identify clients and rate limit + }), + '/clearnotifications': rateLimit({ + windowMs: 1000 * 60, //1 minute + max: 5, + standardHeaders: false, + legacyHeaders: false, + message: {status: "FAILED", message: "You have cleared your notifications too many times in the last minute. Please try again in 60 seconds."}, + skipFailedRequests: true, + keyGenerator: (req, res) => req.tokenData //Use req.tokenData (account _id in MongoDB) to identify clients and rate limit }) } @@ -3149,4 +3158,33 @@ router.post('/removevoteonpost', rateLimiters['/removevoteonpost'], (req, res) = }) }); +router.post('/clearnotifications', rateLimiters['/clearnotifications'], (req, res) => { + let HTTPHeadersSent = false; + const worker = new Worker(workerPath, { + workerData: { + functionName: 'clearnotifications', + functionArgs: [req.tokenData] + } + }) + + worker.on('message', (result) => { + if (!HTTPHeadersSent) { + HTTPHeadersSent = true; + res.status(result.statusCode).json(result.data) + } else { + console.error('POST temp/clearnotifications controller function returned data to be sent to the client but HTTP headers have already been sent! Data attempted to send:', result) + } + }) + + worker.on('error', (error) => { + if (!HTTPHeadersSent) { + HTTPHeadersSent = true; + console.error('An error occurred from TempWorker for POST /clearnotifications:', error) + HTTPHandler.serverError(res, String(error)) + } else { + console.error('POST temp/clearnotifications controller function encountered an error and tried to send it to the client but HTTP headers have already been sent! Error attempted to send:', error) + } + }) +}); + module.exports = router; \ No newline at end of file From 8ed9ad4c39dc81dc7426deec686df09cdeb499cb Mon Sep 17 00:00:00 2001 From: Sebastian Webster Date: Sat, 25 Nov 2023 03:21:24 +1300 Subject: [PATCH 03/36] Added tests for temp/clearnotifications --- tests/TEST_CONSTANTS.js | 3 +- tests/temp/clearnotifications.test.js | 115 ++++++++++++++++++++++++++ 2 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 tests/temp/clearnotifications.test.js diff --git a/tests/TEST_CONSTANTS.js b/tests/TEST_CONSTANTS.js index e19f64a2..3f9d464d 100644 --- a/tests/TEST_CONSTANTS.js +++ b/tests/TEST_CONSTANTS.js @@ -11,7 +11,8 @@ const TEST_CONSTANTS = { resolve(false); }) }) - } + }, + RANDOM_OBJECTIDS: ["6560b01ec5ea35f173c645d4", "6560b0310a9b4c4ee26ce297", "6560b036dbc1f1384347ff61", "6560b047f1045829ebdb6e3b", "6560b0633f3b8652f95e4224"] } module.exports = TEST_CONSTANTS; \ No newline at end of file diff --git a/tests/temp/clearnotifications.test.js b/tests/temp/clearnotifications.test.js new file mode 100644 index 00000000..7fddc811 --- /dev/null +++ b/tests/temp/clearnotifications.test.js @@ -0,0 +1,115 @@ +const User = require('../../models/User'); +const Notification = require('../../models/Notification'); +const MockMongoDBServer = require('../../libraries/MockDBServer'); +const TEST_CONSTANTS = require('../TEST_CONSTANTS'); +const TempController = require('../../controllers/Temp'); + +const {expect, test, beforeEach, afterEach} = require('@jest/globals'); + +const DB = new MockMongoDBServer(); + +beforeEach(async () => { + await DB.startTest(); +}) + +afterEach(async () => { + await DB.stopTest(); +}) + +/* +Tests: +- Test if clearing notifications fails if userId is not a string +- Test if clearing notifications fails if userId is not an ObjectId +- Test if clearing notifications fails if user could not be found +- Test if clearing notifications succeeds with correct input +- Test if clearing notifications does not clear other users' notifications +*/ + +const userData = { + _id: '6560ae37c116f9ded444d3d7' +} + +const userNotifications = [...new Array(100)].map((item, index) => { + return { + text: `Notification ${index}`, + dateCreated: new Date(), + userId: userData._id + } +}) + +for (const notString of TEST_CONSTANTS.NOT_STRINGS) { + test(`If clearing notifications fails if userId is not a string, Testing: ${JSON.stringify(notString)}`, async () => { + expect.assertions(2); + + const returned = await TempController.clearnotifications(notString); + + expect(returned.statusCode).toBe(400); + expect(returned.data.message).toBe(`userId must be a string. Provided type: ${typeof notString}`) + }) +} + +test('If clearing notifications fails if userId is not an ObjectId', async () => { + expect.assertions(2); + + const returned = await TempController.clearnotifications('i am not an objectid'); + + expect(returned.statusCode).toBe(400); + expect(returned.data.message).toBe('userId must be an ObjectId.') +}) + +test('If clearing notifications fails if user could not be found', async () => { + expect.assertions(2); + + const returned = await TempController.clearnotifications(userData._id); + + expect(returned.statusCode).toBe(400); + expect(returned.data.message).toBe('Could not find user with provided userId.') +}) + +test('If clearing notifications succeeds with correct input', async () => { + expect.assertions(3); + + await new User(userData).save(); + + await Notification.insertMany(userNotifications); + + const beforeNotifications = await Notification.find({}).lean(); + + const returned = await TempController.clearnotifications(userData._id); + + const afterNotifications = await Notification.find({}).lean(); + + expect(returned.statusCode).toBe(200); + expect(beforeNotifications).toHaveLength(100); + expect(afterNotifications).toHaveLength(0); +}) + +test("If clearing notifications do not clear other user's notifications", async () => { + expect.assertions(4); + + await new User(userData).save(); + + const otherUserNotifications = [...new Array(900)].map((item, index) => { + return { + text: `Notification ${index}`, + dateCreated: new Date(), + userId: TEST_CONSTANTS.RANDOM_OBJECTIDS[Math.floor(Math.random() * TEST_CONSTANTS.RANDOM_OBJECTIDS.length)] + } + }) + + await Notification.insertMany(userNotifications); + await Notification.insertMany(otherUserNotifications); + + const beforeNotifications = await Notification.find({}).lean(); + const before_otherNotifications = await Notification.find({userId: {$ne: userData._id}}); + + const returned = await TempController.clearnotifications(userData._id); + + const afterNotifications = await Notification.find({}).lean(); + const after_otherNotifications = await Notification.find({userId: {$ne: userData._id}}); + + expect(returned.statusCode).toBe(200); + expect(beforeNotifications).toHaveLength(1000); + expect(afterNotifications).toHaveLength(900); + expect(before_otherNotifications).toStrictEqual(after_otherNotifications); +}) \ No newline at end of file From ef0322e08c2e3f4ce61198f46a53b2b528740fc0 Mon Sep 17 00:00:00 2001 From: Sebastian Webster Date: Sat, 25 Nov 2023 03:45:18 +1300 Subject: [PATCH 04/36] Fixed bug in tests --- tests/temp/clearnotifications.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/temp/clearnotifications.test.js b/tests/temp/clearnotifications.test.js index 7fddc811..1e3a992f 100644 --- a/tests/temp/clearnotifications.test.js +++ b/tests/temp/clearnotifications.test.js @@ -62,7 +62,7 @@ test('If clearing notifications fails if user could not be found', async () => { const returned = await TempController.clearnotifications(userData._id); - expect(returned.statusCode).toBe(400); + expect(returned.statusCode).toBe(404); expect(returned.data.message).toBe('Could not find user with provided userId.') }) From 6eddbab7006ac3aba5370d3034746df24745f628 Mon Sep 17 00:00:00 2001 From: Sebastian Webster Date: Sat, 25 Nov 2023 15:05:06 +1300 Subject: [PATCH 05/36] Changed order in test --- tests/temp/clearnotifications.test.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/temp/clearnotifications.test.js b/tests/temp/clearnotifications.test.js index 1e3a992f..94359530 100644 --- a/tests/temp/clearnotifications.test.js +++ b/tests/temp/clearnotifications.test.js @@ -90,10 +90,11 @@ test("If clearing notifications do not clear other user's notifications", async await new User(userData).save(); const otherUserNotifications = [...new Array(900)].map((item, index) => { + const index = Math.floor(Math.random() * TEST_CONSTANTS.RANDOM_OBJECTIDS.length) return { text: `Notification ${index}`, dateCreated: new Date(), - userId: TEST_CONSTANTS.RANDOM_OBJECTIDS[Math.floor(Math.random() * TEST_CONSTANTS.RANDOM_OBJECTIDS.length)] + userId: TEST_CONSTANTS.RANDOM_OBJECTIDS[index] } }) From 8048984948c16c82d0790e68947e7341809da22e Mon Sep 17 00:00:00 2001 From: Sebastian Webster Date: Sat, 25 Nov 2023 16:26:13 +1300 Subject: [PATCH 06/36] Change userId test selection process --- tests/temp/clearnotifications.test.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/temp/clearnotifications.test.js b/tests/temp/clearnotifications.test.js index 94359530..0b0d346d 100644 --- a/tests/temp/clearnotifications.test.js +++ b/tests/temp/clearnotifications.test.js @@ -90,11 +90,10 @@ test("If clearing notifications do not clear other user's notifications", async await new User(userData).save(); const otherUserNotifications = [...new Array(900)].map((item, index) => { - const index = Math.floor(Math.random() * TEST_CONSTANTS.RANDOM_OBJECTIDS.length) return { text: `Notification ${index}`, dateCreated: new Date(), - userId: TEST_CONSTANTS.RANDOM_OBJECTIDS[index] + userId: TEST_CONSTANTS.RANDOM_OBJECTIDS[index % TEST_CONSTANTS.RANDOM_OBJECTIDS.length] } }) From 21171256b89489ac253f26ee0a0f9abe0d7fce07 Mon Sep 17 00:00:00 2001 From: Sebastian Webster Date: Sat, 25 Nov 2023 18:03:04 +1300 Subject: [PATCH 07/36] Added temp/deletenotification API --- controllers/Temp.js | 36 ++++++++++++++++++++++++++++++++++++ routes/Temp.js | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/controllers/Temp.js b/controllers/Temp.js index ee6377a7..f793e14b 100644 --- a/controllers/Temp.js +++ b/controllers/Temp.js @@ -6520,6 +6520,38 @@ class TempController { }) } + static #deletenotification = (userId, notificationId) => { + return new Promise(resolve => { + if (typeof userId !== 'string') { + return resolve(HTTPWTHandler.badInput(`userId must be a string. Provided type: ${typeof userId}`)); + } + + if (!mongoose.isObjectIdOrHexString(userId)) { + return resolve(HTTPWTHandler.badInput('userId must be an ObjectId.')) + } + + User.findOne({_id: {$eq: userId}}).lean().then(userFound => { + if (!userFound) return resolve(HTTPWTHandler.notFound('Could not find user with provided userId.')) + + Notification.findOne({_id: {$eq: notificationId}}).then(notificationFound => { + if (!notificationFound) return resolve(HTTPWTHandler.notFound('Could not find notification.')) + + if (String(notificationFound.userId) !== userId) return resolve(HTTPWTHandler.forbidden('You must be the notification owner to delete this notification.')) + + Notification.deleteOne({_id: {$eq: notificationId}}).then(() => { + return resolve(HTTPWTHandler.OK('Successfully deleted notification')) + }).catch(error => { + console.error('An error occurred while deleting one notification with id:', notificationId, '. The error was:', error) + return resolve(HTTPWTHandler.serverError('An error occurred while deleting notification. Please try again.')) + }) + }) + }).catch(error => { + console.error('An error occurred while finding one user with id:', userId, '. The error was:', error) + return resolve(HTTPWTHandler.serverError('An error occurred while finding user. Please try again.')) + }) + }) + } + static sendnotificationkey = async (userId, notificationKey, refreshTokenId) => { return await this.#sendnotificationkey(userId, notificationKey, refreshTokenId) } @@ -6847,6 +6879,10 @@ class TempController { static clearnotifications = async (userId) => { return await this.#clearnotifications(userId); } + + static deletenotification = async (userId, notificationId) => { + return await this.#deletenotification(userId, notificationId); + } } module.exports = TempController; diff --git a/routes/Temp.js b/routes/Temp.js index 8f921f2d..17f3ab7c 100644 --- a/routes/Temp.js +++ b/routes/Temp.js @@ -775,6 +775,15 @@ const rateLimiters = { message: {status: "FAILED", message: "You have cleared your notifications too many times in the last minute. Please try again in 60 seconds."}, skipFailedRequests: true, keyGenerator: (req, res) => req.tokenData //Use req.tokenData (account _id in MongoDB) to identify clients and rate limit + }), + '/deletenotifications': rateLimit({ + windowMs: 1000 * 60, //1 minute + max: 60, + standardHeaders: false, + legacyHeaders: false, + message: {status: "FAILED", message: "You have deleted too many notifications in the last minute. Please try again in 60 seconds."}, + skipFailedRequests: true, + keyGenerator: (req, res) => req.tokenData //Use req.tokenData (account _id in MongoDB) to identify clients and rate limit }) } @@ -3187,4 +3196,33 @@ router.post('/clearnotifications', rateLimiters['/clearnotifications'], (req, re }) }); +router.post('/deletenotification', rateLimiters['/deletenotification'], (req, res) => { + let HTTPHeadersSent = false; + const worker = new Worker(workerPath, { + workerData: { + functionName: 'deletenotification', + functionArgs: [req.tokenData, req.body.notificationId] + } + }) + + worker.on('message', (result) => { + if (!HTTPHeadersSent) { + HTTPHeadersSent = true; + res.status(result.statusCode).json(result.data) + } else { + console.error('POST temp/deletenotification controller function returned data to be sent to the client but HTTP headers have already been sent! Data attempted to send:', result) + } + }) + + worker.on('error', (error) => { + if (!HTTPHeadersSent) { + HTTPHeadersSent = true; + console.error('An error occurred from TempWorker for POST /deletenotification:', error) + HTTPHandler.serverError(res, String(error)) + } else { + console.error('POST temp/deletenotification controller function encountered an error and tried to send it to the client but HTTP headers have already been sent! Error attempted to send:', error) + } + }) +}); + module.exports = router; \ No newline at end of file From 91ed4b7e33da9b252ddd1ece529f1618aba29812 Mon Sep 17 00:00:00 2001 From: Sebastian Webster Date: Sat, 25 Nov 2023 19:41:57 +1300 Subject: [PATCH 08/36] Added first temp/deletenotification test --- controllers/Temp.js | 8 +++++ tests/temp/deletenotification.test.js | 49 +++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 tests/temp/deletenotification.test.js diff --git a/controllers/Temp.js b/controllers/Temp.js index f793e14b..edbd27c2 100644 --- a/controllers/Temp.js +++ b/controllers/Temp.js @@ -6530,6 +6530,14 @@ class TempController { return resolve(HTTPWTHandler.badInput('userId must be an ObjectId.')) } + if (typeof notificationId !== 'string') { + return resolve(HTTPWTHandler.badInput(`notificationId must be a string. Provided type: ${typeof notificationId}`)) + } + + if (!mongoose.isObjectIdOrHexString(notificationId)) { + return resolve(HTTPWTHandler.badInput('notificationId must be an ObjectId.')) + } + User.findOne({_id: {$eq: userId}}).lean().then(userFound => { if (!userFound) return resolve(HTTPWTHandler.notFound('Could not find user with provided userId.')) diff --git a/tests/temp/deletenotification.test.js b/tests/temp/deletenotification.test.js new file mode 100644 index 00000000..b8f94748 --- /dev/null +++ b/tests/temp/deletenotification.test.js @@ -0,0 +1,49 @@ +const User = require('../../models/User'); +const Notification = require('../../models/Notification'); +const TempController = require('../../controllers/TempController'); +const MockMongoDBServer = require('../../models/MockDBServer'); +const TEST_CONSTANTS = require('../TEST_CONSTANTS'); + +const {expect, test, beforeEach, afterEach} = require('@jest/globals'); + +const DB = new MockMongoDBServer(); + +beforeEach(async () => { + await DB.startTest(); +}) + +afterEach(async () => { + await DB.stopTest(); +}) + +const userData = { + _id: '656196e317bec814f3df76d3' +} + +const notificationData = { + _id: '656196ed5d7c232a0c5f8da1' +} + +/* +TODO: +- Test if deletion fails if userId is not a string -- Done +- Test if deletion fails if userId is not an ObjectId +- Test if deletion fails if notificationId is not a string +- Test if deletion fails if notificationId is not an ObjectId +- Test if deletion fails if user could not be found +- Test if deletion fails if notification could not be found +- Test if deletion fails if the user is not the notification owner +- Test if deletion succeeds with correct inputs +- Test if deletion does not interfere with other notifications in the database +*/ + +for (const notString of TEST_CONSTANTS.NOT_STRINGS) { + test(`If deletion fails if userId is not a string. Testing: ${JSON.stringify(notString)}`, async () => { + expect.assertions(2); + + const returned = await TempController.deletenotification(notString, notificationData._id); + + expect(returned.statusCode).toBe(400); + expect(returned.data.message).toBe(`userId must be a string. Provided type: ${typeof notString}`) + }) +} \ No newline at end of file From 7689bcefc1bdfc1b9eec33ddc9af009a224d6b78 Mon Sep 17 00:00:00 2001 From: Sebastian Webster Date: Sat, 25 Nov 2023 19:45:25 +1300 Subject: [PATCH 09/36] Added temp/deletenotification type tests --- tests/temp/deletenotification.test.js | 35 ++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/tests/temp/deletenotification.test.js b/tests/temp/deletenotification.test.js index b8f94748..59a8aa0d 100644 --- a/tests/temp/deletenotification.test.js +++ b/tests/temp/deletenotification.test.js @@ -27,9 +27,9 @@ const notificationData = { /* TODO: - Test if deletion fails if userId is not a string -- Done -- Test if deletion fails if userId is not an ObjectId -- Test if deletion fails if notificationId is not a string -- Test if deletion fails if notificationId is not an ObjectId +- Test if deletion fails if userId is not an ObjectId -- Done +- Test if deletion fails if notificationId is not a string -- Done +- Test if deletion fails if notificationId is not an ObjectId -- Done - Test if deletion fails if user could not be found - Test if deletion fails if notification could not be found - Test if deletion fails if the user is not the notification owner @@ -46,4 +46,31 @@ for (const notString of TEST_CONSTANTS.NOT_STRINGS) { expect(returned.statusCode).toBe(400); expect(returned.data.message).toBe(`userId must be a string. Provided type: ${typeof notString}`) }) -} \ No newline at end of file + + test(`If deletion fails if notificationId is not a string. Testing: ${JSON.stringify(notString)}`, async () => { + expect.assertions(2); + + const returned = await TempController.deletenotification(userData._id, notString); + + expect(returned.statusCode).toBe(400); + expect(returned.data.message).toBe('notificationId must be an ObjectId.') + }) +} + +test('If deletion fails if userId is not an ObjectId', async () => { + expect.assertions(2); + + const returned = await TempController.deletenotification('i am not an ObjectId', notificationData._id); + + expect(returned.statusCode).toBe(400); + expect(returned.data.message).toBe('userId must be an ObjectId.') +}) + +test('If deletion fails if notificationId is not an ObjectId', async () => { + expect.assertions(2); + + const returned = await TempController.deletenotification(userData._id, 'i am not an ObjectId'); + + expect(returned.statusCode).toBe(400); + expect(returned.data.message).toBe('notificationId must be an ObjectId.') +}) \ No newline at end of file From cce588ce864a5c0374306441254e739fb931a79b Mon Sep 17 00:00:00 2001 From: Sebastian Webster Date: Sat, 25 Nov 2023 20:02:42 +1300 Subject: [PATCH 10/36] Added temp/deletenotification not found tests --- tests/temp/deletenotification.test.js | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/tests/temp/deletenotification.test.js b/tests/temp/deletenotification.test.js index 59a8aa0d..c46d3456 100644 --- a/tests/temp/deletenotification.test.js +++ b/tests/temp/deletenotification.test.js @@ -30,8 +30,8 @@ TODO: - Test if deletion fails if userId is not an ObjectId -- Done - Test if deletion fails if notificationId is not a string -- Done - Test if deletion fails if notificationId is not an ObjectId -- Done -- Test if deletion fails if user could not be found -- Test if deletion fails if notification could not be found +- Test if deletion fails if user could not be found -- Done +- Test if deletion fails if notification could not be found -- Done - Test if deletion fails if the user is not the notification owner - Test if deletion succeeds with correct inputs - Test if deletion does not interfere with other notifications in the database @@ -73,4 +73,24 @@ test('If deletion fails if notificationId is not an ObjectId', async () => { expect(returned.statusCode).toBe(400); expect(returned.data.message).toBe('notificationId must be an ObjectId.') +}) + +test('If deletion fails if user could not be found', async () => { + expect.assertions(2); + + const returned = await TempController.deletenotification(userData._id, notificationData._id); + + expect(returned.statusCode).toBe(404); + expect(returned.data.message).toBe('Could not find user with provided userId.') +}) + +test('If deletion fails if notification could not be found', async () => { + expect.assertions(2); + + await new User(userData).save(); + + const returned = await TempController.deletenotification(userData._id, notificationData._id); + + expect(returned.statusCode).toBe(404; + expect(returned.data.message).toBe('Could not find notification.') }) \ No newline at end of file From e55cf347ea0db00c533467cfe89e78213f886c75 Mon Sep 17 00:00:00 2001 From: Sebastian Webster Date: Sat, 25 Nov 2023 20:03:18 +1300 Subject: [PATCH 11/36] Fixed bug in tests --- tests/temp/deletenotification.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/temp/deletenotification.test.js b/tests/temp/deletenotification.test.js index c46d3456..e9fa33ba 100644 --- a/tests/temp/deletenotification.test.js +++ b/tests/temp/deletenotification.test.js @@ -1,6 +1,6 @@ const User = require('../../models/User'); const Notification = require('../../models/Notification'); -const TempController = require('../../controllers/TempController'); +const TempController = require('../../controllers/Temp'); const MockMongoDBServer = require('../../models/MockDBServer'); const TEST_CONSTANTS = require('../TEST_CONSTANTS'); From 28f639a912cb801d3000155af6488b48ced4d6ce Mon Sep 17 00:00:00 2001 From: Sebastian Webster Date: Sat, 25 Nov 2023 20:40:42 +1300 Subject: [PATCH 12/36] Fixed bug in tests --- tests/temp/deletenotification.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/temp/deletenotification.test.js b/tests/temp/deletenotification.test.js index e9fa33ba..5a696246 100644 --- a/tests/temp/deletenotification.test.js +++ b/tests/temp/deletenotification.test.js @@ -91,6 +91,6 @@ test('If deletion fails if notification could not be found', async () => { const returned = await TempController.deletenotification(userData._id, notificationData._id); - expect(returned.statusCode).toBe(404; + expect(returned.statusCode).toBe(404); expect(returned.data.message).toBe('Could not find notification.') }) \ No newline at end of file From 4ea682ac8fa6486b481f5fc9f84e976c60fc5ca8 Mon Sep 17 00:00:00 2001 From: Sebastian Webster Date: Sat, 25 Nov 2023 20:49:50 +1300 Subject: [PATCH 13/36] Fixed bug in tests --- tests/temp/deletenotification.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/temp/deletenotification.test.js b/tests/temp/deletenotification.test.js index 5a696246..ea78689a 100644 --- a/tests/temp/deletenotification.test.js +++ b/tests/temp/deletenotification.test.js @@ -1,7 +1,7 @@ const User = require('../../models/User'); const Notification = require('../../models/Notification'); const TempController = require('../../controllers/Temp'); -const MockMongoDBServer = require('../../models/MockDBServer'); +const MockMongoDBServer = require('../../libraries/MockDBServer'); const TEST_CONSTANTS = require('../TEST_CONSTANTS'); const {expect, test, beforeEach, afterEach} = require('@jest/globals'); From 28c69dd1ad8ef62a2dc4058a8db30fa210d3a1ae Mon Sep 17 00:00:00 2001 From: Sebastian Webster Date: Sat, 25 Nov 2023 21:24:33 +1300 Subject: [PATCH 14/36] Fixed bug in tests --- tests/temp/deletenotification.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/temp/deletenotification.test.js b/tests/temp/deletenotification.test.js index ea78689a..88106378 100644 --- a/tests/temp/deletenotification.test.js +++ b/tests/temp/deletenotification.test.js @@ -53,7 +53,7 @@ for (const notString of TEST_CONSTANTS.NOT_STRINGS) { const returned = await TempController.deletenotification(userData._id, notString); expect(returned.statusCode).toBe(400); - expect(returned.data.message).toBe('notificationId must be an ObjectId.') + expect(returned.data.message).toBe(`notificationId must be a string. Provided type: ${typeof notString}`) }) } From 175092cce86dadabbe1208974b7d34b0c7d551a4 Mon Sep 17 00:00:00 2001 From: Sebastian Webster <84299475+Sebastian-Webster@users.noreply.github.com> Date: Wed, 13 Dec 2023 00:58:00 +1300 Subject: [PATCH 15/36] Added skeleton getnotifications API --- controllers/Temp.js | 10 ++++++++++ routes/Temp.js | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/controllers/Temp.js b/controllers/Temp.js index edbd27c2..4ee9d857 100644 --- a/controllers/Temp.js +++ b/controllers/Temp.js @@ -6560,6 +6560,12 @@ class TempController { }) } + static #getnotifications = (userId, lastNotificationId) => { + return new Promise(resolve => { + + }) + } + static sendnotificationkey = async (userId, notificationKey, refreshTokenId) => { return await this.#sendnotificationkey(userId, notificationKey, refreshTokenId) } @@ -6891,6 +6897,10 @@ class TempController { static deletenotification = async (userId, notificationId) => { return await this.#deletenotification(userId, notificationId); } + + static getnotifications = async (userId, lastNotificationId) => { + return await this.#getnotifications(userId, lastNotificationId); + } } module.exports = TempController; diff --git a/routes/Temp.js b/routes/Temp.js index 17f3ab7c..445dfb31 100644 --- a/routes/Temp.js +++ b/routes/Temp.js @@ -784,6 +784,15 @@ const rateLimiters = { message: {status: "FAILED", message: "You have deleted too many notifications in the last minute. Please try again in 60 seconds."}, skipFailedRequests: true, keyGenerator: (req, res) => req.tokenData //Use req.tokenData (account _id in MongoDB) to identify clients and rate limit + }), + '/getnotifications': rateLimit({ + windowMs: 1000 * 60, //1 minute + max: 5, + standardHeaders: false, + legacyHeaders: false, + message: {status: "FAILED", message: "You have retrieved your notifications too many times in the last minute. Please try again in 60 seconds."}, + skipFailedRequests: true, + keyGenerator: (req, res) => req.tokenData //Use req.tokenData (account _id in MongoDB) to identify clients and rate limit }) } @@ -3225,4 +3234,33 @@ router.post('/deletenotification', rateLimiters['/deletenotification'], (req, re }) }); +router.post('/getnotifications', rateLimiters['/getnotifications'], (req, res) => { + let HTTPHeadersSent = false; + const worker = new Worker(workerPath, { + workerData: { + functionName: 'getnotifications', + functionArgs: [req.tokenData, req.body.lastNotificationId] + } + }) + + worker.on('message', (result) => { + if (!HTTPHeadersSent) { + HTTPHeadersSent = true; + res.status(result.statusCode).json(result.data) + } else { + console.error('GET temp/getnotifications controller function returned data to be sent to the client but HTTP headers have already been sent! Data attempted to send:', result) + } + }) + + worker.on('error', (error) => { + if (!HTTPHeadersSent) { + HTTPHeadersSent = true; + console.error('An error occurred from TempWorker for GET /getnotifications:', error) + HTTPHandler.serverError(res, String(error)) + } else { + console.error('GET temp/getnotifications controller function encountered an error and tried to send it to the client but HTTP headers have already been sent! Error attempted to send:', error) + } + }) +}); + module.exports = router; \ No newline at end of file From f52ac280b3002f453834f918711bb4c5e8c6cf65 Mon Sep 17 00:00:00 2001 From: Sebastian Webster <84299475+Sebastian-Webster@users.noreply.github.com> Date: Mon, 18 Dec 2023 00:03:51 +1300 Subject: [PATCH 16/36] Added more to temp/getnotifications --- controllers/Temp.js | 40 +++++++++++++++++++++++++++++++++++++- libraries/Notifications.js | 10 ++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 libraries/Notifications.js diff --git a/controllers/Temp.js b/controllers/Temp.js index 4ee9d857..005a9c31 100644 --- a/controllers/Temp.js +++ b/controllers/Temp.js @@ -6562,7 +6562,45 @@ class TempController { static #getnotifications = (userId, lastNotificationId) => { return new Promise(resolve => { - + if (typeof userId !== 'string') { + return resolve(HTTPWTHandler.badInput(`userId must be a string. Provided type: ${typeof userId}`)) + } + + if (!mongoose.isObjectIdOrHexString(userId)) { + return resolve(HTTPWTHandler.badInput('userId must be an ObjectId.')) + } + + if (typeof lastNotificationId !== 'string' && lastNotificationId !== undefined) { + return resolve(HTTPWTHandler.badInput(`lastNotificationId must be a string or undefined. Provided type: ${typeof lastNotificationId}`)) + } + + if (typeof lastNotificationId === 'string' && !mongoose.isObjectIdOrHexString(lastNotificationId)) { + return resolve(HTTPWTHandler.badInput('lastNotificationId must be an ObjectId or undefined.')) + } + + User.findOne({_id: {$eq: userId}}).lean().then(userFound => { + if (!userFound) return resolve(HTTPWTHandler.notFound('Could not find user with provided userId.')) + + const notificationQuery = { + userId: {$eq: userId} + } + + if (typeof lastNotificationId === 'string') { + notificationQuery._id = {$lt: lastNotificationId} + } + + Notification.find(notificationQuery).then(notifications => { + if (notifications.length < 1) return resolve(HTTPWTHandler.OK('No notifications could be found', {notifications: [], noMoreNotifications: true})) + + + }).catch(error => { + console.error('An error occurred while finding notifications with database query:', notificationQuery, '. The error was:', error) + return resolve(HTTPWTHandler.serverError('An error occurred while finding notifications. Please try again.')) + }) + }).catch(error => { + console.error('An error occurred while finding one user with id:', userId, '. The error was:', error) + return resolve(HTTPWTHandler.serverError('An error occurred while finding user. Please try again.')) + }) }) } diff --git a/libraries/Notifications.js b/libraries/Notifications.js new file mode 100644 index 00000000..f17955f8 --- /dev/null +++ b/libraries/Notifications.js @@ -0,0 +1,10 @@ +class Notifications { + returnNotificationDataToSend(rawNotificationData) { + return rawNotificationData.map(notification => { + delete notification.userId; + return notification; + }) + } +} + +module.exports = Notifications; \ No newline at end of file From 0dfd4fd2e93a842d3e15978c84a8d1c9892dc30d Mon Sep 17 00:00:00 2001 From: Sebastian-Webster <84299475+Sebastian-Webster@users.noreply.github.com> Date: Mon, 18 Dec 2023 00:37:33 +1300 Subject: [PATCH 17/36] Finished temp/getnotifications API (still need tests) --- constants.js | 3 ++- controllers/Temp.js | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/constants.js b/constants.js index 315fd16b..8d90ca5c 100644 --- a/constants.js +++ b/constants.js @@ -61,7 +61,8 @@ const CONSTANTS = { CATEGORY_PERMISSIONS: [ 'deletePosts' ], - MAX_ACCOUNT_FOLLOW_REQUESTS_PER_API_CALL: 10 + MAX_ACCOUNT_FOLLOW_REQUESTS_PER_API_CALL: 10, + MAX_NOTIFICATIONS_PER_API_CALL: 10 //Used in temp/getnotifications } module.exports = CONSTANTS \ No newline at end of file diff --git a/controllers/Temp.js b/controllers/Temp.js index 005a9c31..de2eb674 100644 --- a/controllers/Temp.js +++ b/controllers/Temp.js @@ -49,6 +49,9 @@ const mongooseSessionHelper = new MongooseSessionLibrary(); const CategoryLibrary = require('../libraries/Category.js'); const categoryHelper = new CategoryLibrary(); +const NotificationLibrary = require('../libraries/Notifications.js'); +const notificationHelper = new NotificationLibrary(); + const bcrypt = require('bcrypt') const mongoose = require('mongoose') @@ -6589,10 +6592,17 @@ class TempController { notificationQuery._id = {$lt: lastNotificationId} } - Notification.find(notificationQuery).then(notifications => { + Notification.find(notificationQuery).sort({_id: -1}).limit(CONSTANTS.MAX_NOTIFICATIONS_PER_API_CALL).then(notifications => { if (notifications.length < 1) return resolve(HTTPWTHandler.OK('No notifications could be found', {notifications: [], noMoreNotifications: true})) - + const notificationData = notificationHelper.returnNotificationDataToSend(notifications); + + const toSend = { + notifications: notificationData, + noMoreNotifications: notifications.length < CONSTANTS.MAX_NOTIFICATIONS_PER_API_CALL + } + + return resolve(HTTPWTHandler.OK('Found notifications', toSend)); }).catch(error => { console.error('An error occurred while finding notifications with database query:', notificationQuery, '. The error was:', error) return resolve(HTTPWTHandler.serverError('An error occurred while finding notifications. Please try again.')) From 7b3439b7498fe759e66ddfdb5cccc3ff8a8f11c9 Mon Sep 17 00:00:00 2001 From: Sebastian Webster <84299475+Sebastian-Webster@users.noreply.github.com> Date: Mon, 18 Dec 2023 01:43:25 +1300 Subject: [PATCH 18/36] Fixed server crashing issue --- routes/Temp.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/Temp.js b/routes/Temp.js index 445dfb31..0c952c89 100644 --- a/routes/Temp.js +++ b/routes/Temp.js @@ -776,7 +776,7 @@ const rateLimiters = { skipFailedRequests: true, keyGenerator: (req, res) => req.tokenData //Use req.tokenData (account _id in MongoDB) to identify clients and rate limit }), - '/deletenotifications': rateLimit({ + '/deletenotification': rateLimit({ windowMs: 1000 * 60, //1 minute max: 60, standardHeaders: false, From 5a8742f9067ce4006d3e3ad709026bea2e03509c Mon Sep 17 00:00:00 2001 From: Sebastian Webster <84299475+Sebastian-Webster@users.noreply.github.com> Date: Wed, 20 Dec 2023 00:43:05 +1300 Subject: [PATCH 19/36] Stringify notification id --- libraries/Notifications.js | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/Notifications.js b/libraries/Notifications.js index f17955f8..705514c5 100644 --- a/libraries/Notifications.js +++ b/libraries/Notifications.js @@ -2,6 +2,7 @@ class Notifications { returnNotificationDataToSend(rawNotificationData) { return rawNotificationData.map(notification => { delete notification.userId; + notification._id = String(notification._id) return notification; }) } From e8a2a2474cacdcc668dc38ffc6f2f3de6b0f362d Mon Sep 17 00:00:00 2001 From: Sebastian Webster <84299475+Sebastian-Webster@users.noreply.github.com> Date: Thu, 21 Dec 2023 23:23:41 +1300 Subject: [PATCH 20/36] Add lean option to temp/getnotifications --- controllers/Temp.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controllers/Temp.js b/controllers/Temp.js index de2eb674..5f887eb3 100644 --- a/controllers/Temp.js +++ b/controllers/Temp.js @@ -6592,7 +6592,7 @@ class TempController { notificationQuery._id = {$lt: lastNotificationId} } - Notification.find(notificationQuery).sort({_id: -1}).limit(CONSTANTS.MAX_NOTIFICATIONS_PER_API_CALL).then(notifications => { + Notification.find(notificationQuery).sort({_id: -1}).limit(CONSTANTS.MAX_NOTIFICATIONS_PER_API_CALL).lean().then(notifications => { if (notifications.length < 1) return resolve(HTTPWTHandler.OK('No notifications could be found', {notifications: [], noMoreNotifications: true})) const notificationData = notificationHelper.returnNotificationDataToSend(notifications); From e000918da6bc5471f642a7a6b4e2882eea235f64 Mon Sep 17 00:00:00 2001 From: Sebastian Webster <84299475+Sebastian-Webster@users.noreply.github.com> Date: Sat, 30 Dec 2023 00:55:08 +1300 Subject: [PATCH 21/36] Add getnotifications test skeleton file --- tests/temp/getnotifications.test.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 tests/temp/getnotifications.test.js diff --git a/tests/temp/getnotifications.test.js b/tests/temp/getnotifications.test.js new file mode 100644 index 00000000..9c023f2c --- /dev/null +++ b/tests/temp/getnotifications.test.js @@ -0,0 +1,18 @@ +const MockMongoDBServer = require('../../libraries/MockDBServer'); +const TempController = require('../../controllers/Temp'); + +const TEST_CONSTANTS = require('../TEST_CONSTANTS'); + +const {expect, beforeEach, afterEach} = require('@jest/globals') + +jest.setTimeout(20_000); + +/* +TODO: +- Test if notification retrieval fails if userId is not a string +- Test if notification retrieval fails if userId is not an ObjectId +- Test if notification retrieval fails if lastNotificationId is not a string and not undefined +- Test if notification retrieval fails if user could not be found +- Test if notification retrieval works with lastNotificationId +- Test if notification retrieval works with lastNotificationId as undefined +*/ \ No newline at end of file From 7f7177799851da89f5ea80d7f83d83a2078d5a70 Mon Sep 17 00:00:00 2001 From: Sebastian Webster <84299475+Sebastian-Webster@users.noreply.github.com> Date: Sat, 30 Dec 2023 03:42:55 +1300 Subject: [PATCH 22/36] Partially complete one delete notification test (not done yet) --- tests/temp/deletenotification.test.js | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/tests/temp/deletenotification.test.js b/tests/temp/deletenotification.test.js index 88106378..f15747be 100644 --- a/tests/temp/deletenotification.test.js +++ b/tests/temp/deletenotification.test.js @@ -21,7 +21,8 @@ const userData = { } const notificationData = { - _id: '656196ed5d7c232a0c5f8da1' + _id: '656196ed5d7c232a0c5f8da1', + userId: userData._id } /* @@ -93,4 +94,20 @@ test('If deletion fails if notification could not be found', async () => { expect(returned.statusCode).toBe(404); expect(returned.data.message).toBe('Could not find notification.') +}) + +test('If deletion fails if the user is not the notification owner', async () => { + expect.assertions(2); + + const notOwnerId = "658eb5abe9bed6afd85928b6"; + + await new User(userData).save(); + await new User({ + _id: notOwnerId, + name: "UserTwo" + }).save(); + + await new Notification(notificationData).save(); + + const returned = await TempController.deletenotification(notOwnerId, notificationData._id); }) \ No newline at end of file From bb096dbd4d36ef7221e3b9ae960fd260f31a409e Mon Sep 17 00:00:00 2001 From: Sebastian-Webster <84299475+Sebastian-Webster@users.noreply.github.com> Date: Sat, 30 Dec 2023 03:47:17 +1300 Subject: [PATCH 23/36] Added comment deletion fails if not comment owner test --- tests/temp/deletenotification.test.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/temp/deletenotification.test.js b/tests/temp/deletenotification.test.js index f15747be..d894ccdf 100644 --- a/tests/temp/deletenotification.test.js +++ b/tests/temp/deletenotification.test.js @@ -33,7 +33,7 @@ TODO: - Test if deletion fails if notificationId is not an ObjectId -- Done - Test if deletion fails if user could not be found -- Done - Test if deletion fails if notification could not be found -- Done -- Test if deletion fails if the user is not the notification owner +- Test if deletion fails if the user is not the notification owner -- Done - Test if deletion succeeds with correct inputs - Test if deletion does not interfere with other notifications in the database */ @@ -97,7 +97,7 @@ test('If deletion fails if notification could not be found', async () => { }) test('If deletion fails if the user is not the notification owner', async () => { - expect.assertions(2); + expect.assertions(3); const notOwnerId = "658eb5abe9bed6afd85928b6"; @@ -109,5 +109,13 @@ test('If deletion fails if the user is not the notification owner', async () => await new Notification(notificationData).save(); + const beforeNotifications = await Notification.find({}).lean(); + const returned = await TempController.deletenotification(notOwnerId, notificationData._id); + + const afterNotifications = await Notification.find({}).lean(); + + expect(beforeNotifications).toStrictEqual(afterNotifications); + expect(returned.statusCode).toBe(403); + expect(returned.data.message).toBe('You must be the notification owner to delete this notification.') }) \ No newline at end of file From e58f36fc36ce61f0bb806fd1a52ee95ed31a05b9 Mon Sep 17 00:00:00 2001 From: Sebastian-Webster <84299475+Sebastian-Webster@users.noreply.github.com> Date: Sat, 30 Dec 2023 03:53:53 +1300 Subject: [PATCH 24/36] Finalized temp/deletenotification tests --- tests/temp/deletenotification.test.js | 55 +++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 8 deletions(-) diff --git a/tests/temp/deletenotification.test.js b/tests/temp/deletenotification.test.js index d894ccdf..ebcdee9c 100644 --- a/tests/temp/deletenotification.test.js +++ b/tests/temp/deletenotification.test.js @@ -26,14 +26,14 @@ const notificationData = { } /* -TODO: -- Test if deletion fails if userId is not a string -- Done -- Test if deletion fails if userId is not an ObjectId -- Done -- Test if deletion fails if notificationId is not a string -- Done -- Test if deletion fails if notificationId is not an ObjectId -- Done -- Test if deletion fails if user could not be found -- Done -- Test if deletion fails if notification could not be found -- Done -- Test if deletion fails if the user is not the notification owner -- Done +Tests: +- Test if deletion fails if userId is not a string +- Test if deletion fails if userId is not an ObjectId +- Test if deletion fails if notificationId is not a string +- Test if deletion fails if notificationId is not an ObjectId +- Test if deletion fails if user could not be found +- Test if deletion fails if notification could not be found +- Test if deletion fails if the user is not the notification owner - Test if deletion succeeds with correct inputs - Test if deletion does not interfere with other notifications in the database */ @@ -118,4 +118,43 @@ test('If deletion fails if the user is not the notification owner', async () => expect(beforeNotifications).toStrictEqual(afterNotifications); expect(returned.statusCode).toBe(403); expect(returned.data.message).toBe('You must be the notification owner to delete this notification.') +}) + +test('If deletion succeeds with correct inputs', async () => { + expect.assertions(3); + + await new User(userData).save(); + + await new Notification(notificationData).save(); + + const beforeNotifications = await Notification.find({}).lean(); + + const returned = await TempController.deletenotification(userData._id, notificationData._id); + + const afterNotifications = await Notification.find({}).lean(); + + expect(beforeNotifications).toHaveLength(1); + expect(afterNotifications).toHaveLength(0); + expect(returned.statusCode).toBe(200); +}) + +test('If deletion does not interfere with other notifications in the database', async () => { + expect.assertions(2); + + await new User(userData).save(); + + await new Notification(notificationData).save(); + + await Notification.insertMany([...new Array(10)].map(() => ({ + userId: "658edc8e7902c80dfff44a8a" + }))) + + const beforeNotifications = await Notification.find({_id: {$ne: userData._id}}.lean()); + + const returned = await TempController.deletenotification(userData._id, notificationData._id); + + const afterNotifications = await Notification.find({_id: {$ne: userData._id}}).lean(); + + expect(beforeNotifications).toStrictEqual(afterNotifications); + expect(returned.statusCode).toBe(200); }) \ No newline at end of file From 22795c98d8f15162a6d5df335344b24d62bf6334 Mon Sep 17 00:00:00 2001 From: Sebastian-Webster <84299475+Sebastian-Webster@users.noreply.github.com> Date: Sat, 30 Dec 2023 03:56:36 +1300 Subject: [PATCH 25/36] Added first temp/getnotifications test --- tests/temp/getnotifications.test.js | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/tests/temp/getnotifications.test.js b/tests/temp/getnotifications.test.js index 9c023f2c..6697e164 100644 --- a/tests/temp/getnotifications.test.js +++ b/tests/temp/getnotifications.test.js @@ -9,10 +9,31 @@ jest.setTimeout(20_000); /* TODO: -- Test if notification retrieval fails if userId is not a string +- Test if notification retrieval fails if userId is not a string -- Done - Test if notification retrieval fails if userId is not an ObjectId - Test if notification retrieval fails if lastNotificationId is not a string and not undefined - Test if notification retrieval fails if user could not be found - Test if notification retrieval works with lastNotificationId - Test if notification retrieval works with lastNotificationId as undefined -*/ \ No newline at end of file +*/ + +const DB = new MockMongoDBServer(); + +beforeEach(async () => { + await DB.startTest() +}) + +afterEach(async () => { + await DB.stopTest() +}) + +for (const notString of TEST_CONSTANTS.NOT_STRINGS) { + test(`If retrieval fails if userId is not a string. Testing: ${JSON.stringify(notString)}`, async () => { + expect.assertions(2); + + const returned = await TempController.getnotifications(notString, undefined); + + expect(returned.statusCode).toBe(400); + expect(returned.data.message).toBe(`userId must be a string. Provided type: ${typeof notString}`) + }) +} \ No newline at end of file From b3a03a78d02aa6c2e3ba52ebba66ffcefa8b30aa Mon Sep 17 00:00:00 2001 From: Sebastian-Webster <84299475+Sebastian-Webster@users.noreply.github.com> Date: Sat, 30 Dec 2023 04:05:26 +1300 Subject: [PATCH 26/36] Added all data type tests for temp/getnotifications --- tests/temp/getnotifications.test.js | 53 ++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 4 deletions(-) diff --git a/tests/temp/getnotifications.test.js b/tests/temp/getnotifications.test.js index 6697e164..5be99c21 100644 --- a/tests/temp/getnotifications.test.js +++ b/tests/temp/getnotifications.test.js @@ -2,6 +2,7 @@ const MockMongoDBServer = require('../../libraries/MockDBServer'); const TempController = require('../../controllers/Temp'); const TEST_CONSTANTS = require('../TEST_CONSTANTS'); +const CONSTANTS = require('../../constants'); const {expect, beforeEach, afterEach} = require('@jest/globals') @@ -10,13 +11,18 @@ jest.setTimeout(20_000); /* TODO: - Test if notification retrieval fails if userId is not a string -- Done -- Test if notification retrieval fails if userId is not an ObjectId -- Test if notification retrieval fails if lastNotificationId is not a string and not undefined -- Test if notification retrieval fails if user could not be found +- Test if notification retrieval fails if userId is not an ObjectId -- Done +- Test if notification retrieval fails if lastNotificationId is not a string and not undefined -- Done +- Test if notification retrieval fails if lastNotificationid is not an ObjectId and not undefined -- Done +- Test if notification retrieval fails if user could not be found -- Done - Test if notification retrieval works with lastNotificationId - Test if notification retrieval works with lastNotificationId as undefined */ +const userData = { + _id: "658ede853d49c73b7571ab76" +} + const DB = new MockMongoDBServer(); beforeEach(async () => { @@ -36,4 +42,43 @@ for (const notString of TEST_CONSTANTS.NOT_STRINGS) { expect(returned.statusCode).toBe(400); expect(returned.data.message).toBe(`userId must be a string. Provided type: ${typeof notString}`) }) -} \ No newline at end of file + + if (notString !== undefined) { + test(`If retrieval fails if lastNotificationId is not a string or undefined. Testing: ${JSON.stringify(notString)}`, async () => { + expect.assertions(2); + + const returned = await TempController.getnotifications(userData._id, notString) + + expect(returned.statusCode).toBe(400); + expect(returned.data.message).toBe(`lastNotificationId must be a string or undefined. Provided type: ${typeof notString}`) + }) + } +} + +test('If retrieval fails if userId is not an ObjectId', async () => { + expect.assertions(2); + + const returned = await TempController.getnotifications('i am not an objectid', undefined); + + expect(returned.statusCode).toBe(400); + expect(returned.data.message).toBe('userId must be an ObjectId.') +}) + +test('If retrieval fails if lastNotificationId is not an ObjectId and not undefined', async () => { + expect.assertions(2); + + const returned = await TempController.getnotifications(userData._id, 'i am not an objectid') + + expect(returned.statusCode).toBe(400); + expect(returned.data.message).toBe('lastNotificationId must be an ObjectId or undefined.') +}) + +test('If retrieval fails if user could not be found', async () => { + expect.assertions(2); + + const returned = await TempController.getnotifications(userData._id, undefined); + + expect(returned.statusCode).toBe(404); + expect(returned.data.message).toBe('Could not find user with provided userId.') + expect(returned.data.data).toBe(undefined) +}) \ No newline at end of file From 69c970b9dfebf89dcbf9cb102fc707f5eb641f9b Mon Sep 17 00:00:00 2001 From: Sebastian-Webster <84299475+Sebastian-Webster@users.noreply.github.com> Date: Sat, 30 Dec 2023 04:16:03 +1300 Subject: [PATCH 27/36] Fixed bug in temp/deletenotification test --- tests/temp/deletenotification.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/temp/deletenotification.test.js b/tests/temp/deletenotification.test.js index ebcdee9c..9d5f47eb 100644 --- a/tests/temp/deletenotification.test.js +++ b/tests/temp/deletenotification.test.js @@ -149,7 +149,7 @@ test('If deletion does not interfere with other notifications in the database', userId: "658edc8e7902c80dfff44a8a" }))) - const beforeNotifications = await Notification.find({_id: {$ne: userData._id}}.lean()); + const beforeNotifications = await Notification.find({_id: {$ne: userData._id}}).lean(); const returned = await TempController.deletenotification(userData._id, notificationData._id); From 3fac6c70849a9e9a42d5a49bc189b24b7d9af908 Mon Sep 17 00:00:00 2001 From: Sebastian-Webster <84299475+Sebastian-Webster@users.noreply.github.com> Date: Sat, 30 Dec 2023 04:21:45 +1300 Subject: [PATCH 28/36] Fixed bug in temp/getnotifications test --- tests/temp/getnotifications.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/temp/getnotifications.test.js b/tests/temp/getnotifications.test.js index 5be99c21..105ba773 100644 --- a/tests/temp/getnotifications.test.js +++ b/tests/temp/getnotifications.test.js @@ -74,7 +74,7 @@ test('If retrieval fails if lastNotificationId is not an ObjectId and not undefi }) test('If retrieval fails if user could not be found', async () => { - expect.assertions(2); + expect.assertions(3); const returned = await TempController.getnotifications(userData._id, undefined); From f2d985f19fef7962739865e9088b5b693138fedb Mon Sep 17 00:00:00 2001 From: Sebastian-Webster <84299475+Sebastian-Webster@users.noreply.github.com> Date: Sat, 30 Dec 2023 04:28:01 +1300 Subject: [PATCH 29/36] Added test to see if retrieval works with lastNotificationId --- tests/temp/getnotifications.test.js | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/temp/getnotifications.test.js b/tests/temp/getnotifications.test.js index 105ba773..7d549953 100644 --- a/tests/temp/getnotifications.test.js +++ b/tests/temp/getnotifications.test.js @@ -1,9 +1,13 @@ const MockMongoDBServer = require('../../libraries/MockDBServer'); const TempController = require('../../controllers/Temp'); +const Notification = require('../../models/Notification'); const TEST_CONSTANTS = require('../TEST_CONSTANTS'); const CONSTANTS = require('../../constants'); +const NotificationLibrary = require('../../libraries/Notifications'); +const notificationHelper = new NotificationLibrary(); + const {expect, beforeEach, afterEach} = require('@jest/globals') jest.setTimeout(20_000); @@ -81,4 +85,27 @@ test('If retrieval fails if user could not be found', async () => { expect(returned.statusCode).toBe(404); expect(returned.data.message).toBe('Could not find user with provided userId.') expect(returned.data.data).toBe(undefined) +}) + +test('If retrieval works with lastNotificationId', async () => { + expect.assertions(2); + + await new User(userData).save(); + + await Notification.insertMany([...new Array(1000)].map((item, index) => { + return { + text: index + } + })) + + const notifications = await Notification.find({}).lean(); + + const lastNotificationId = notifications[4]._id; + + const processedNotifications = notificationHelper.returnNotificationDataToSend(notifications.splice(5, CONSTANTS.MAX_NOTIFICATIONS_PER_API_CALL)) + + const returned = await TempController.getnotifications(userData._id, lastNotificationId); + + expect(returned.statusCode).toBe(200); + expect(returned.data.data).toStrictEqual(processedNotifications); }) \ No newline at end of file From d07541f0444cee040c5df882c81eb49f88efa735 Mon Sep 17 00:00:00 2001 From: Sebastian-Webster <84299475+Sebastian-Webster@users.noreply.github.com> Date: Sat, 30 Dec 2023 04:29:08 +1300 Subject: [PATCH 30/36] Finalized tests for temp/getnotifications --- tests/temp/getnotifications.test.js | 33 +++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/tests/temp/getnotifications.test.js b/tests/temp/getnotifications.test.js index 7d549953..cadb0dc2 100644 --- a/tests/temp/getnotifications.test.js +++ b/tests/temp/getnotifications.test.js @@ -13,12 +13,12 @@ const {expect, beforeEach, afterEach} = require('@jest/globals') jest.setTimeout(20_000); /* -TODO: -- Test if notification retrieval fails if userId is not a string -- Done -- Test if notification retrieval fails if userId is not an ObjectId -- Done -- Test if notification retrieval fails if lastNotificationId is not a string and not undefined -- Done -- Test if notification retrieval fails if lastNotificationid is not an ObjectId and not undefined -- Done -- Test if notification retrieval fails if user could not be found -- Done +Tests: +- Test if notification retrieval fails if userId is not a string +- Test if notification retrieval fails if userId is not an ObjectId +- Test if notification retrieval fails if lastNotificationId is not a string and not undefined +- Test if notification retrieval fails if lastNotificationid is not an ObjectId and not undefined +- Test if notification retrieval fails if user could not be found - Test if notification retrieval works with lastNotificationId - Test if notification retrieval works with lastNotificationId as undefined */ @@ -106,6 +106,27 @@ test('If retrieval works with lastNotificationId', async () => { const returned = await TempController.getnotifications(userData._id, lastNotificationId); + expect(returned.statusCode).toBe(200); + expect(returned.data.data).toStrictEqual(processedNotifications); +}) + +test('If retrieval works with lastNotificationId', async () => { + expect.assertions(2); + + await new User(userData).save(); + + await Notification.insertMany([...new Array(1000)].map((item, index) => { + return { + text: index + } + })) + + const notifications = await Notification.find({}).lean(); + + const processedNotifications = notificationHelper.returnNotificationDataToSend(notifications.splice(0, CONSTANTS.MAX_NOTIFICATIONS_PER_API_CALL)) + + const returned = await TempController.getnotifications(userData._id, undefined); + expect(returned.statusCode).toBe(200); expect(returned.data.data).toStrictEqual(processedNotifications); }) \ No newline at end of file From 1dd900edd3eb0eb4da288ac73adf435929609a7e Mon Sep 17 00:00:00 2001 From: Sebastian-Webster <84299475+Sebastian-Webster@users.noreply.github.com> Date: Sat, 30 Dec 2023 04:37:22 +1300 Subject: [PATCH 31/36] Fixed bug in temp/deletenotification --- tests/temp/deletenotification.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/temp/deletenotification.test.js b/tests/temp/deletenotification.test.js index 9d5f47eb..3b07b02c 100644 --- a/tests/temp/deletenotification.test.js +++ b/tests/temp/deletenotification.test.js @@ -149,11 +149,11 @@ test('If deletion does not interfere with other notifications in the database', userId: "658edc8e7902c80dfff44a8a" }))) - const beforeNotifications = await Notification.find({_id: {$ne: userData._id}}).lean(); + const beforeNotifications = await Notification.find({_id: {$ne: notificationData._id}}).lean(); const returned = await TempController.deletenotification(userData._id, notificationData._id); - const afterNotifications = await Notification.find({_id: {$ne: userData._id}}).lean(); + const afterNotifications = await Notification.find({_id: {$ne: notificationData._id}}).lean(); expect(beforeNotifications).toStrictEqual(afterNotifications); expect(returned.statusCode).toBe(200); From 31ae52957362a9330670be0b324a0700bfcfef25 Mon Sep 17 00:00:00 2001 From: Sebastian-Webster <84299475+Sebastian-Webster@users.noreply.github.com> Date: Sat, 30 Dec 2023 04:43:15 +1300 Subject: [PATCH 32/36] Fixed bug in temp/getnotifications test --- tests/temp/getnotifications.test.js | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/temp/getnotifications.test.js b/tests/temp/getnotifications.test.js index cadb0dc2..d10e6fa4 100644 --- a/tests/temp/getnotifications.test.js +++ b/tests/temp/getnotifications.test.js @@ -1,6 +1,7 @@ const MockMongoDBServer = require('../../libraries/MockDBServer'); const TempController = require('../../controllers/Temp'); const Notification = require('../../models/Notification'); +const User = require('../../models/User'); const TEST_CONSTANTS = require('../TEST_CONSTANTS'); const CONSTANTS = require('../../constants'); From d7e69ede07cecbbad35f59bf5330fdeee462e43e Mon Sep 17 00:00:00 2001 From: Sebastian-Webster <84299475+Sebastian-Webster@users.noreply.github.com> Date: Sat, 30 Dec 2023 04:57:52 +1300 Subject: [PATCH 33/36] Changed name of test --- tests/temp/getnotifications.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/temp/getnotifications.test.js b/tests/temp/getnotifications.test.js index d10e6fa4..ed853140 100644 --- a/tests/temp/getnotifications.test.js +++ b/tests/temp/getnotifications.test.js @@ -111,7 +111,7 @@ test('If retrieval works with lastNotificationId', async () => { expect(returned.data.data).toStrictEqual(processedNotifications); }) -test('If retrieval works with lastNotificationId', async () => { +test('If retrieval works with lastNotificationId as undefined', async () => { expect.assertions(2); await new User(userData).save(); From fd19326ba36c0349f8a656ee4b71bedbf21e7232 Mon Sep 17 00:00:00 2001 From: Sebastian-Webster <84299475+Sebastian-Webster@users.noreply.github.com> Date: Sun, 31 Dec 2023 03:10:18 +1300 Subject: [PATCH 34/36] Fixed bugs in temp/getnotifications test --- tests/temp/getnotifications.test.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/temp/getnotifications.test.js b/tests/temp/getnotifications.test.js index ed853140..0e84b022 100644 --- a/tests/temp/getnotifications.test.js +++ b/tests/temp/getnotifications.test.js @@ -95,7 +95,8 @@ test('If retrieval works with lastNotificationId', async () => { await Notification.insertMany([...new Array(1000)].map((item, index) => { return { - text: index + text: index, + userId: userData._id } })) @@ -108,7 +109,7 @@ test('If retrieval works with lastNotificationId', async () => { const returned = await TempController.getnotifications(userData._id, lastNotificationId); expect(returned.statusCode).toBe(200); - expect(returned.data.data).toStrictEqual(processedNotifications); + expect(returned.data.data.notifications).toStrictEqual(processedNotifications); }) test('If retrieval works with lastNotificationId as undefined', async () => { @@ -118,7 +119,8 @@ test('If retrieval works with lastNotificationId as undefined', async () => { await Notification.insertMany([...new Array(1000)].map((item, index) => { return { - text: index + text: index, + userId: userData._id } })) @@ -129,5 +131,5 @@ test('If retrieval works with lastNotificationId as undefined', async () => { const returned = await TempController.getnotifications(userData._id, undefined); expect(returned.statusCode).toBe(200); - expect(returned.data.data).toStrictEqual(processedNotifications); + expect(returned.data.data.notifications).toStrictEqual(processedNotifications); }) \ No newline at end of file From b376822fc2df8309b48500059528b4076fa86e3b Mon Sep 17 00:00:00 2001 From: Sebastian-Webster <84299475+Sebastian-Webster@users.noreply.github.com> Date: Sun, 31 Dec 2023 18:01:40 +1300 Subject: [PATCH 35/36] Add sort by id to temp/getnotifications test --- tests/temp/getnotifications.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/temp/getnotifications.test.js b/tests/temp/getnotifications.test.js index 0e84b022..de54cda0 100644 --- a/tests/temp/getnotifications.test.js +++ b/tests/temp/getnotifications.test.js @@ -100,7 +100,7 @@ test('If retrieval works with lastNotificationId', async () => { } })) - const notifications = await Notification.find({}).lean(); + const notifications = await Notification.find({}).sort({_id: -1}).lean(); const lastNotificationId = notifications[4]._id; @@ -124,7 +124,7 @@ test('If retrieval works with lastNotificationId as undefined', async () => { } })) - const notifications = await Notification.find({}).lean(); + const notifications = await Notification.find({}).sort({_id: -1}).lean(); const processedNotifications = notificationHelper.returnNotificationDataToSend(notifications.splice(0, CONSTANTS.MAX_NOTIFICATIONS_PER_API_CALL)) From 3e6ef8bc66b84fb37d34d8561c632be9c5e8f2c6 Mon Sep 17 00:00:00 2001 From: Sebastian-Webster <84299475+Sebastian-Webster@users.noreply.github.com> Date: Sun, 31 Dec 2023 18:48:35 +1300 Subject: [PATCH 36/36] Fixed bug in tests --- tests/temp/getnotifications.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/temp/getnotifications.test.js b/tests/temp/getnotifications.test.js index de54cda0..5c80c836 100644 --- a/tests/temp/getnotifications.test.js +++ b/tests/temp/getnotifications.test.js @@ -102,7 +102,7 @@ test('If retrieval works with lastNotificationId', async () => { const notifications = await Notification.find({}).sort({_id: -1}).lean(); - const lastNotificationId = notifications[4]._id; + const lastNotificationId = String(notifications[4]._id); const processedNotifications = notificationHelper.returnNotificationDataToSend(notifications.splice(5, CONSTANTS.MAX_NOTIFICATIONS_PER_API_CALL))