From 0c33df1cd648dd08fb8f428a7a534133af481ce9 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Thu, 10 Apr 2025 19:15:34 +1200 Subject: [PATCH] Add tests for lock API functions --- .../lockNotification/lockNotification.jsx | 2 +- server/admin.api.js | 4 +- server/admin.api.spec.js | 588 ++++++++++++++++++ 3 files changed, 591 insertions(+), 3 deletions(-) diff --git a/client/homebrew/pages/editPage/lockNotification/lockNotification.jsx b/client/homebrew/pages/editPage/lockNotification/lockNotification.jsx index 0750a599c..34102bdc6 100644 --- a/client/homebrew/pages/editPage/lockNotification/lockNotification.jsx +++ b/client/homebrew/pages/editPage/lockNotification/lockNotification.jsx @@ -16,7 +16,7 @@ function LockNotification(props) { const [reviewState, setReviewState] = React.useState(props.reviewRequested); const removeLock = async ()=>{ - await request.put(`/admin/lock/review/request/${props.shareId}`) + await request.put(`/api/lock/review/request/${props.shareId}`) .then(()=>{ setReviewState(true); }); diff --git a/server/admin.api.js b/server/admin.api.js index ee859a3e0..a3d7622f1 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -232,7 +232,7 @@ router.post('/api/lock/:id', mw.adminOnly, asyncHandler(async (req, res)=>{ await brew.save() .catch((error)=>{ - throw { name: 'Already Locked', message: 'Unable to set lock', shareId: req.params.id, status: 500, HBErrorCode: '62', error }; + throw { name: 'Lock Error', message: 'Unable to set lock', shareId: req.params.id, status: 500, HBErrorCode: '62', error }; }); return res.json({ name: 'LOCKED', message: `Lock applied to brew ID ${brew.shareId} - ${brew.title}`, ...lock }); @@ -289,7 +289,7 @@ router.get('/api/lock/reviews', mw.adminOnly, asyncHandler(async (req, res)=>{ })); -router.put('/admin/lock/review/request/:id', asyncHandler(async (req, res)=>{ +router.put('/api/lock/review/request/:id', asyncHandler(async (req, res)=>{ // === This route is NOT Admin only === // Any user can request a review of their document const filter = { diff --git a/server/admin.api.spec.js b/server/admin.api.spec.js index 036c238c9..e156c6c8f 100644 --- a/server/admin.api.spec.js +++ b/server/admin.api.spec.js @@ -1,6 +1,8 @@ +/*eslint max-lines: ["warn", {"max": 1000, "skipBlankLines": true, "skipComments": true}]*/ import supertest from 'supertest'; import HBApp from './app.js'; import { model as NotificationModel } from './notifications.model.js'; +import { model as HomebrewModel } from './homebrew.model.js'; // Mimic https responses to avoid being redirected all the time @@ -114,4 +116,590 @@ describe('Tests for admin api', ()=>{ expect(response.body).toEqual({ message: 'Notification not found' }); }); }); + + describe('Locks', ()=>{ + describe('Count', ()=>{ + it('Count of all locked documents', async ()=>{ + const testNumber = 16777216; // 8^8, because why not + + jest.spyOn(HomebrewModel, 'countDocuments') + .mockImplementationOnce(()=>{ + return Promise.resolve(testNumber); + }); + + const response = await app + .set('Authorization', `Basic ${Buffer.from('admin:password3').toString('base64')}`) + .get('/api/lock/count'); + + expect(response.status).toBe(200); + expect(response.body).toEqual({ count: testNumber }); + }); + + it('Handle error while fetching count of locked documents', async ()=>{ + jest.spyOn(HomebrewModel, 'countDocuments') + .mockImplementationOnce(()=>{ + return Promise.reject(); + }); + + const response = await app + .set('Authorization', `Basic ${Buffer.from('admin:password3').toString('base64')}`) + .get('/api/lock/count'); + + expect(response.status).toBe(500); + expect(response.body).toEqual({ + HBErrorCode : '61', + message : 'Unable to get lock count', + name : 'Lock Count Error', + originalUrl : '/api/lock/count', + status : 500, + }); + }); + }); + + describe('Lists', ()=>{ + it('Get list of all locked documents', async ()=>{ + const testLocks = ['a', 'b']; + + jest.spyOn(HomebrewModel, 'aggregate') + .mockImplementationOnce(()=>{ + return Promise.resolve(testLocks); + }); + + const response = await app + .set('Authorization', `Basic ${Buffer.from('admin:password3').toString('base64')}`) + .get('/api/locks'); + + expect(response.status).toBe(200); + expect(response.body).toEqual({ lockedDocuments: testLocks }); + }); + + it('Handle error while fetching list of all locked documents', async ()=>{ + jest.spyOn(HomebrewModel, 'aggregate') + .mockImplementationOnce(()=>{ + return Promise.reject(); + }); + + const response = await app + .set('Authorization', `Basic ${Buffer.from('admin:password3').toString('base64')}`) + .get('/api/locks'); + + expect(response.status).toBe(500); + expect(response.body).toEqual({ + HBErrorCode : '68', + message : 'Unable to get locked brew collection', + name : 'Can Not Get Locked Brews', + originalUrl : '/api/locks', + status : 500 + }); + }); + + it('Get list of all locked documents with pending review requests', async ()=>{ + const testLocks = ['a', 'b']; + + jest.spyOn(HomebrewModel, 'aggregate') + .mockImplementationOnce(()=>{ + return Promise.resolve(testLocks); + }); + + const response = await app + .set('Authorization', `Basic ${Buffer.from('admin:password3').toString('base64')}`) + .get('/api/lock/reviews'); + + expect(response.status).toBe(200); + expect(response.body).toEqual({ reviewDocuments: testLocks }); + }); + + it('Handle error while fetching list of all locked documents with pending review requests', async ()=>{ + jest.spyOn(HomebrewModel, 'aggregate') + .mockImplementationOnce(()=>{ + return Promise.reject(); + }); + + const response = await app + .set('Authorization', `Basic ${Buffer.from('admin:password3').toString('base64')}`) + .get('/api/lock/reviews'); + + expect(response.status).toBe(500); + expect(response.body).toEqual({ + HBErrorCode : '68', + message : 'Unable to get review collection', + name : 'Can Not Get Reviews', + originalUrl : '/api/lock/reviews', + status : 500 + }); + }); + }); + + describe('Lock', ()=>{ + it('Lock a brew', async ()=>{ + const testBrew = { + shareId : 'shareId', + title : 'title', + markModified : ()=>{ return true; }, + save : ()=>{ return Promise.resolve(); } + }; + + const testLock = { + code : 999, + editMessage : 'edit', + shareMessage : 'share' + }; + + jest.spyOn(HomebrewModel, 'findOne') + .mockImplementationOnce(()=>{ + return Promise.resolve(testBrew); + }); + + const response = await app + .set('Authorization', `Basic ${Buffer.from('admin:password3').toString('base64')}`) + .post(`/api/lock/${testBrew.shareId}`) + .send(testLock); + + expect(response.status).toBe(200); + expect(response.body).toMatchObject({ + applied : expect.any(String), + code : testLock.code, + editMessage : testLock.editMessage, + shareMessage : testLock.shareMessage, + name : 'LOCKED', + message : `Lock applied to brew ID ${testBrew.shareId} - ${testBrew.title}` + }); + }); + + it('Overwrite lock on a locked brew', async ()=>{ + const testLock = { + code : 999, + editMessage : 'newEdit', + shareMessage : 'newShare', + overwrite : true + }; + + const testBrew = { + shareId : 'shareId', + title : 'title', + markModified : ()=>{ return true; }, + save : ()=>{ return Promise.resolve(); }, + lock : { + code : 1, + editMessage : 'oldEdit', + shareMessage : 'oldShare', + } + }; + + jest.spyOn(HomebrewModel, 'findOne') + .mockImplementationOnce(()=>{ + return Promise.resolve(testBrew); + }); + + const response = await app + .set('Authorization', `Basic ${Buffer.from('admin:password3').toString('base64')}`) + .post(`/api/lock/${testBrew.shareId}`) + .send(testLock); + + expect(response.status).toBe(200); + expect(response.body).toMatchObject({ + applied : expect.any(String), + code : testLock.code, + editMessage : testLock.editMessage, + shareMessage : testLock.shareMessage, + name : 'LOCKED', + message : `Lock applied to brew ID ${testBrew.shareId} - ${testBrew.title}` + }); + }); + + it('Error when locking a locked brew', async ()=>{ + const testLock = { + code : 999, + editMessage : 'newEdit', + shareMessage : 'newShare' + }; + + const testBrew = { + shareId : 'shareId', + title : 'title', + markModified : ()=>{ return true; }, + save : ()=>{ return Promise.resolve(); }, + lock : { + code : 1, + editMessage : 'oldEdit', + shareMessage : 'oldShare', + } + }; + + jest.spyOn(HomebrewModel, 'findOne') + .mockImplementationOnce(()=>{ + return Promise.resolve(testBrew); + }); + + const response = await app + .set('Authorization', `Basic ${Buffer.from('admin:password3').toString('base64')}`) + .post(`/api/lock/${testBrew.shareId}`) + .send(testLock); + + expect(response.status).toBe(500); + expect(response.body).toEqual({ + HBErrorCode : '64', + message : 'Lock already exists on brew', + name : 'Already Locked', + originalUrl : `/api/lock/${testBrew.shareId}`, + shareId : testBrew.shareId, + status : 500, + title : 'title' + }); + }); + + it('Handle save error while locking a brew', async ()=>{ + const testBrew = { + shareId : 'shareId', + title : 'title', + markModified : ()=>{ return true; }, + save : ()=>{ return Promise.reject(); } + }; + + const testLock = { + code : 999, + editMessage : 'edit', + shareMessage : 'share' + }; + + jest.spyOn(HomebrewModel, 'findOne') + .mockImplementationOnce(()=>{ + return Promise.resolve(testBrew); + }); + + const response = await app + .set('Authorization', `Basic ${Buffer.from('admin:password3').toString('base64')}`) + .post(`/api/lock/${testBrew.shareId}`) + .send(testLock); + + expect(response.status).toBe(500); + expect(response.body).toEqual({ + HBErrorCode : '62', + message : 'Unable to set lock', + name : 'Lock Error', + originalUrl : `/api/lock/${testBrew.shareId}`, + shareId : testBrew.shareId, + status : 500 + }); + }); + }); + + describe('Unlock', ()=>{ + it('Unlock a brew', async ()=>{ + const testLock = { + applied : 'YES', + code : 999, + editMessage : 'edit', + shareMessage : 'share' + }; + + const testBrew = { + shareId : 'shareId', + title : 'title', + markModified : ()=>{ return true; }, + save : ()=>{ return Promise.resolve(); }, + lock : testLock + }; + + jest.spyOn(HomebrewModel, 'findOne') + .mockImplementationOnce(()=>{ + return Promise.resolve(testBrew); + }); + + const response = await app + .set('Authorization', `Basic ${Buffer.from('admin:password3').toString('base64')}`) + .put(`/api/unlock/${testBrew.shareId}`); + + expect(response.status).toBe(200); + expect(response.body).toEqual({ + name : 'Unlocked', + message : `Lock removed from brew ID ${testBrew.shareId}` + }); + }); + + it('Error when unlocking a brew with no lock', async ()=>{ + const testBrew = { + shareId : 'shareId', + title : 'title', + markModified : ()=>{ return true; }, + save : ()=>{ return Promise.resolve(); }, + }; + + jest.spyOn(HomebrewModel, 'findOne') + .mockImplementationOnce(()=>{ + return Promise.resolve(testBrew); + }); + + const response = await app + .set('Authorization', `Basic ${Buffer.from('admin:password3').toString('base64')}`) + .put(`/api/unlock/${testBrew.shareId}`); + + expect(response.status).toBe(500); + expect(response.body).toEqual({ + HBErrorCode : '67', + message : 'Cannot unlock as brew is not locked', + name : 'Not Locked', + originalUrl : `/api/unlock/${testBrew.shareId}`, + shareId : testBrew.shareId, + status : 500, + }); + }); + + it('Handle error while unlocking a brew', async ()=>{ + const testLock = { + applied : 'YES', + code : 999, + editMessage : 'edit', + shareMessage : 'share' + }; + + const testBrew = { + shareId : 'shareId', + title : 'title', + markModified : ()=>{ return true; }, + save : ()=>{ return Promise.reject(); }, + lock : testLock + }; + + jest.spyOn(HomebrewModel, 'findOne') + .mockImplementationOnce(()=>{ + return Promise.resolve(testBrew); + }); + + const response = await app + .set('Authorization', `Basic ${Buffer.from('admin:password3').toString('base64')}`) + .put(`/api/unlock/${testBrew.shareId}`); + + expect(response.status).toBe(500); + expect(response.body).toEqual({ + HBErrorCode : '65', + message : 'Unable to clear lock', + name : 'Cannot Unlock', + originalUrl : `/api/unlock/${testBrew.shareId}`, + shareId : testBrew.shareId, + status : 500 + }); + }); + }); + + describe('Reviews', ()=>{ + it('Add review request to a locked brew', async ()=>{ + const testLock = { + applied : 'YES', + code : 999, + editMessage : 'edit', + shareMessage : 'share' + }; + + const testBrew = { + shareId : 'shareId', + title : 'title', + markModified : ()=>{ return true; }, + save : ()=>{ return Promise.resolve(); }, + lock : testLock + }; + + jest.spyOn(HomebrewModel, 'findOne') + .mockImplementationOnce(()=>{ + return Promise.resolve(testBrew); + }); + + const response = await app + .put(`/api/lock/review/request/${testBrew.shareId}`); + + expect(response.status).toBe(200); + expect(response.body).toEqual({ + message : `Review requested on brew ID ${testBrew.shareId} - ${testBrew.title}`, + name : 'Review Requested', + }); + }); + + it('Error when cannot find a locked brew', async ()=>{ + const testBrew = { + shareId : 'shareId' + }; + + jest.spyOn(HomebrewModel, 'findOne') + .mockImplementationOnce(()=>{ + return Promise.resolve(false); + }); + + + const response = await app + .put(`/api/lock/review/request/${testBrew.shareId}`) + .catch((err)=>{return err;}); + + expect(response.status).toBe(500); + expect(response.body).toEqual({ + message : `Cannot find a locked brew with ID ${testBrew.shareId}`, + name : 'Brew Not Found', + HBErrorCode : '70', + code : 500, + originalUrl : `/api/lock/review/request/${testBrew.shareId}` + }); + }); + + it('Error when review is already requested', async ()=>{ + const testLock = { + applied : 'YES', + code : 999, + editMessage : 'edit', + shareMessage : 'share', + reviewRequested : 'YES' + }; + + const testBrew = { + shareId : 'shareId', + title : 'title', + markModified : ()=>{ return true; }, + save : ()=>{ return Promise.resolve(); }, + lock : testLock + }; + + jest.spyOn(HomebrewModel, 'findOne') + .mockImplementationOnce(()=>{ + return Promise.resolve(false); + }); + + + const response = await app + .put(`/api/lock/review/request/${testBrew.shareId}`) + .catch((err)=>{return err;}); + + expect(response.status).toBe(500); + expect(response.body).toEqual({ + HBErrorCode : '70', + code : 500, + message : `Cannot find a locked brew with ID ${testBrew.shareId}`, + name : 'Brew Not Found', + originalUrl : `/api/lock/review/request/${testBrew.shareId}` + }); + }); + + it('Handle error while adding review request to a locked brew', async ()=>{ + const testLock = { + applied : 'YES', + code : 999, + editMessage : 'edit', + shareMessage : 'share' + }; + + const testBrew = { + shareId : 'shareId', + title : 'title', + markModified : ()=>{ return true; }, + save : ()=>{ return Promise.reject(); }, + lock : testLock + }; + + jest.spyOn(HomebrewModel, 'findOne') + .mockImplementationOnce(()=>{ + return Promise.resolve(testBrew); + }); + + const response = await app + .put(`/api/lock/review/request/${testBrew.shareId}`); + + expect(response.status).toBe(500); + expect(response.body).toEqual({ + HBErrorCode : '69', + code : 500, + message : `Unable to set request for review on brew ID ${testBrew.shareId}`, + name : 'Can Not Set Review Request', + originalUrl : `/api/lock/review/request/${testBrew.shareId}` + }); + }); + + it('Clear review request from a locked brew', async ()=>{ + const testLock = { + applied : 'YES', + code : 999, + editMessage : 'edit', + shareMessage : 'share', + reviewRequested : 'YES' + }; + + const testBrew = { + shareId : 'shareId', + title : 'title', + markModified : ()=>{ return true; }, + save : ()=>{ return Promise.resolve(); }, + lock : testLock + }; + + jest.spyOn(HomebrewModel, 'findOne') + .mockImplementationOnce(()=>{ + return Promise.resolve(testBrew); + }); + + const response = await app + .set('Authorization', `Basic ${Buffer.from('admin:password3').toString('base64')}`) + .put(`/api/lock/review/remove/${testBrew.shareId}`); + + expect(response.status).toBe(200); + expect(response.body).toEqual({ + message : `Review request removed for brew ID ${testBrew.shareId} - ${testBrew.title}`, + name : 'Review Request Cleared' + }); + }); + + it('Error when clearing review request from a brew with no review request', async ()=>{ + const testBrew = { + shareId : 'shareId', + }; + + jest.spyOn(HomebrewModel, 'findOne') + .mockImplementationOnce(()=>{ + return Promise.resolve(false); + }); + + const response = await app + .set('Authorization', `Basic ${Buffer.from('admin:password3').toString('base64')}`) + .put(`/api/lock/review/remove/${testBrew.shareId}`); + + expect(response.status).toBe(500); + expect(response.body).toEqual({ + HBErrorCode : '73', + message : `Brew ID ${testBrew.shareId} does not have a review pending!`, + name : 'Can Not Clear Review Request', + originalUrl : `/api/lock/review/remove/${testBrew.shareId}` + }); + }); + + it('Handle error while clearing review request from a locked brew', async ()=>{ + const testLock = { + applied : 'YES', + code : 999, + editMessage : 'edit', + shareMessage : 'share', + reviewRequested : 'YES' + }; + + const testBrew = { + shareId : 'shareId', + title : 'title', + markModified : ()=>{ return true; }, + save : ()=>{ return Promise.reject(); }, + lock : testLock + }; + + jest.spyOn(HomebrewModel, 'findOne') + .mockImplementationOnce(()=>{ + return Promise.resolve(testBrew); + }); + + const response = await app + .set('Authorization', `Basic ${Buffer.from('admin:password3').toString('base64')}`) + .put(`/api/lock/review/remove/${testBrew.shareId}`); + + expect(response.status).toBe(500); + expect(response.body).toEqual({ + HBErrorCode : '72', + message : `Unable to remove request for review on brew ID ${testBrew.shareId}`, + name : 'Can Not Clear Review Request', + originalUrl : `/api/lock/review/remove/${testBrew.shareId}` + }); + }); + }); + }); });