diff --git a/src/Pages/AuditLog/AuditLog.js b/src/Pages/AuditLog/AuditLog.js index b115998ee..339a36b9f 100644 --- a/src/Pages/AuditLog/AuditLog.js +++ b/src/Pages/AuditLog/AuditLog.js @@ -1,4 +1,4 @@ -import { useState, useEffect } from 'react'; +import React, { useState, useEffect } from 'react'; import { getAllLogs, createAuditLogEventSource } from '../../APIFunctions/AuditLog'; import Pagination from './Components/Pagination'; import { useSCE } from '../../Components/context/SceContext'; diff --git a/test/api/AuditLog.js b/test/api/AuditLog.js new file mode 100644 index 000000000..fa914041b --- /dev/null +++ b/test/api/AuditLog.js @@ -0,0 +1,158 @@ +process.env.NODE_ENV = 'test'; + +const User = require('../../api/main_endpoints/models/User.js'); +const AuditLog = require('../../api/main_endpoints/models/AuditLog.js'); + +const chai = require('chai'); + +const chaiHttp = require('chai-http'); +const { + UNAUTHORIZED, +} = require('../../api/util/constants.js').STATUS_CODES; +const SceApiTester = require('../util/tools/SceApiTester.js'); + +let app = null; +let test = null; +const expect = chai.expect; +const tools = require('../util/tools/tools.js'); +const { + setTokenStatus, + resetTokenMock, + restoreTokenMock, + initializeTokenMock +} = require('../util/mocks/TokenValidFunctions.js'); +const { + setDiscordAPIStatus, + resetDiscordAPIMock, + restoreDiscordAPIMock, + initializeDiscordAPIMock +} = require('../util/mocks/DiscordApiFunction.js'); +const { MEMBERSHIP_STATE } = require('../../api/util/constants.js'); +const AuditLogActions = require('../../api/main_endpoints/util/auditLogActions.js'); + +chai.should(); +chai.use(chaiHttp); + +describe('AuditLog', () => { + before(done => { + initializeTokenMock(); + initializeDiscordAPIMock(); + app = tools.initializeServer([ + __dirname + '/../../api/main_endpoints/routes/AuditLog.js' + ]); + test = new SceApiTester(app); + + tools.emptySchema(User); + tools.emptySchema(AuditLog); + const testUser = new User({ + email: 'audit@b.c', + password: 'Passw0rd', + firstName: 'firstName', + lastName: 'lastName', + major: 'Software Engineering', + }); + testUser.save(); + const testLog = new AuditLog({ + userId: testUser._id, + action: AuditLogActions.LOG_IN, + documentId: testUser._id, + details: {email: testUser.email }, + }); + testLog.save(); + done(); + }); + + after(done => { + resetTokenMock(); + restoreDiscordAPIMock(); + tools.terminateServer(done); + }); + + beforeEach(() => { + setTokenStatus(false); + setDiscordAPIStatus(false); + }); + + afterEach(async () => { + restoreTokenMock(); + resetTokenMock(); + restoreDiscordAPIMock(); + resetDiscordAPIMock(); + }); + + const token = ''; + + describe('GET /getAuditLogs', () => { + const url = '/api/AuditLog/getAuditLogs/'; + + it('Should return status code 401 if no token is passed through', async () => { + setTokenStatus(false); + const result = await test.sendGetRequest(url); + expect(result).to.have.status(UNAUTHORIZED); + }); + + it('Should return status code 401 if access level is invalid', async () => { + setTokenStatus(false, { accessLevel: MEMBERSHIP_STATE.MEMBER }); + const result = await test.sendGetRequestWithToken(token, url); + expect(result).to.have.status(UNAUTHORIZED); + }); + + it('Should return at most 50 records when query is an empty string and access level is valid', async () => { + setTokenStatus(true, { accessLevel: MEMBERSHIP_STATE.OFFICER}); + + before(async () => { + const newUser = new User({ + email: 'auditLog@b.c', + password: 'Passw0rd', + firstName: 'first name', + lastName: 'last name', + major: 'Software Engineering', + }); + newUser.save(); + + for (let i = 0; i < 3; i++) { + await AuditLog.create({ + userId: newUser._id, + action: AuditLogActions.RESET_PW, + documentId: newUser._id, + details: {email: newUser.email }, + }); + } + + for (let i = 0; i < 60; i++) { + await AuditLog.create({ + userId: newUser._id, + action: AuditLogActions.EMAIL_SENT, + documentId: newUser._id, + details: {email: newUser.email }, + }); + } + }); + const result = await test.sendGetRequestWithToken(token, url); + expect(result.body.items).that.is.an('array'); + expect(result.body.items.length).at.most(50); + }); + + it('Should return the testUser when query is "audit@b.c" and access level is OFFICER', async () => { + setTokenStatus(true, { accessLevel: MEMBERSHIP_STATE.OFFICER}); + const search = 'audit@b.c'; + const fullUrl = `/api/AuditLog/getAuditLogs?search=${encodeURIComponent(search)}`; + const result = await test.sendGetRequestWithToken(token, fullUrl); + expect(result.body.items).that.is.an('array').to.have.lengthOf(1); + expect(result.body.items[0].userId.email).to.eql('audit@b.c'); + }); + + it('Should return an empty array when the query matches no record: "randome@e.f" and access level is ADMIN', async () => { + setTokenStatus(true, { accessLevel: MEMBERSHIP_STATE.ADMIN}); + const search = 'randome@e.f'; + const fullUrl = `/api/AuditLog/getAuditLogs?search=${encodeURIComponent(search)}`; + const result = await test.sendGetRequestWithToken(token, fullUrl); + expect(result.body.items).that.is.an('array').that.is.empty; + }); + + after(async () => { + await User.deleteMany({}); + await AuditLog.deleteMany({}); + }); + }); +}); diff --git a/test/frontend/Routing.test.js b/test/frontend/Routing.test.js index 12e1e7ebe..7268bbf2a 100644 --- a/test/frontend/Routing.test.js +++ b/test/frontend/Routing.test.js @@ -15,6 +15,7 @@ import EditUserInfo from '../../src/Pages/UserManager/EditUserInfo'; import URLShortenerPage from '../../src/Pages/URLShortener/URLShortener'; import sendUnsubscribeEmail from '../../src/Pages/Profile/admin/SendUnsubscribeEmail'; import NotFoundPage from '../../src/Pages/NotFoundPage/NotFoundPage'; +import AuditLogPage from '../../src/Pages/AuditLog/AuditLog'; import { membershipState } from '../../src/Enums'; import { MemoryRouter } from 'react-router-dom'; @@ -59,6 +60,17 @@ function getComponentFromRoute(route, props = adminAppProps, user = mockUser) { describe(' with ', () => { describe('Renders correct components for Admin user', () => { + // Prevent ReferenceError: EventSource is not defined + if (typeof global.EventSource === 'undefined') { + global.EventSource = class { + constructor() {} + close() {} + addEventListener() {} + removeEventListener() {} + onmessage() {} + onerror() {} + }; + } it('Should render a component with the / endpoint', () => { const wrapper = getComponentFromRoute('/'); expect(wrapper.find(Home)).to.have.lengthOf(1); @@ -150,5 +162,13 @@ describe(' with ', () => { expect(wrapper.find(NotFoundPage)).to.have.lengthOf(1); } ); + it( + 'Should render a component with with the /audit-logs ' + + 'endpoint', + () => { + const wrapper = getComponentFromRoute('/audit-logs'); + expect(wrapper.find(AuditLogPage)).to.have.lengthOf(1); + } + ); }); });