From cba950b952e09537d629c6c870f86a7f56d7dfd0 Mon Sep 17 00:00:00 2001 From: Matei-Alexandru Gardus Date: Sun, 16 May 2021 01:07:53 -0700 Subject: [PATCH 01/24] Add tests for "/bonus" and "/emails" routes Squashed set of commits for 6f51db8...c70e444. Merge conflict was on package-lock, which was hard to fix, so I just redid the commits. --- tests/admin.test.ts | 81 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/tests/admin.test.ts b/tests/admin.test.ts index 6c53f572..b8ad8fd6 100644 --- a/tests/admin.test.ts +++ b/tests/admin.test.ts @@ -1,3 +1,4 @@ +import { CreateBonusRequest } from 'api/validators/AdminControllerRequests'; import { ActivityScope, ActivityType, SubmitAttendanceForUsersRequest, UserAccessType } from '../types'; import { ControllerFactory } from './controllers'; import { DatabaseConnection, EventFactory, UserFactory, PortalState } from './data'; @@ -117,3 +118,83 @@ describe('retroactive attendance submission', () => { expect(staffActivityResponse.activity[1].type).toEqual(ActivityType.ATTEND_EVENT_AS_STAFF); }); }); + +describe('email retrieval', () => { + test('gets all the emails of stored users', async () => { + const conn = await DatabaseConnection.get(); + const users = UserFactory.create(5); + const emails = users.map((user) => user.email.toLowerCase()); + const [proxyUser] = UserFactory.with({ accessType: UserAccessType.ADMIN }); + + await new PortalState() + .createUsers([...users, proxyUser]) + .write(); + + const adminController = ControllerFactory.admin(conn); + const response = await adminController.getAllEmails(proxyUser); + expect([...emails, proxyUser.email].sort()).toEqual(response.emails.sort()); + }); + + test('no other emails present asides from registered users', async () => { + const conn = await DatabaseConnection.get(); + const users = UserFactory.create(5); + const [proxyUser] = UserFactory.with({ accessType: UserAccessType.ADMIN }); + const [extraneousUser] = UserFactory.create(1); + + await new PortalState() + .createUsers([...users, proxyUser]) + .write(); + + const adminController = ControllerFactory.admin(conn); + const response = await adminController.getAllEmails(proxyUser); + expect(response.emails).not.toContain(extraneousUser.email.toLowerCase()); + }); +}); + +describe('bonus points submission', () => { + test('updates points and activity to the users in the bonus request', async () => { + const conn = await DatabaseConnection.get(); + const users = UserFactory.create(5); + const emails = users.map((user) => user.email.toLowerCase()); + const [proxyUser] = UserFactory.with({ accessType: UserAccessType.ADMIN }); + + await new PortalState() + .createUsers([...users, proxyUser]) + .write(); + + const adminController = ControllerFactory.admin(conn); + const userController = ControllerFactory.user(conn); + const request: CreateBonusRequest = { bonus: { description: 'Test addition of bonus points', + users: emails, + points: 200 } }; + const bonusResponse = await adminController.addBonus(request, proxyUser); + const userResponse = await userController.getUser({ uuid: users[0].uuid }, proxyUser); + + expect(userResponse.user.points).toEqual(200); + expect(bonusResponse.emails.sort()).toEqual(emails.sort()); + }); + + test("Does not update points and activity to the users who aren't in the bonus request", async () => { + const conn = await DatabaseConnection.get(); + const users = UserFactory.create(5); + const emails = users.map((user) => user.email.toLowerCase()); + const [proxyUser] = UserFactory.with({ accessType: UserAccessType.ADMIN }); + const [extraneousUser] = UserFactory.create(1); + + await new PortalState() + .createUsers([...users, extraneousUser, proxyUser]) + .write(); + + const adminController = ControllerFactory.admin(conn); + const userController = ControllerFactory.user(conn); + const request: CreateBonusRequest = { bonus: { description: 'Test addition of bonus points', + users: emails, + points: 200 } }; + + await adminController.addBonus(request, proxyUser); + + const userResponse = await userController.getUser({ uuid: extraneousUser.uuid }, proxyUser); + + expect(userResponse.user.points).toEqual(0); + }); +}); From 11c362c8232681b98839d1f8cc6dc3a131df34b5 Mon Sep 17 00:00:00 2001 From: Matei-Alexandru Gardus Date: Thu, 27 May 2021 14:25:25 -0700 Subject: [PATCH 02/24] nit: "proxyUser" -> "adminUser" for admin tests --- tests/admin.test.ts | 58 ++++++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/tests/admin.test.ts b/tests/admin.test.ts index b8ad8fd6..48b8ffb4 100644 --- a/tests/admin.test.ts +++ b/tests/admin.test.ts @@ -21,11 +21,11 @@ describe('retroactive attendance submission', () => { const conn = await DatabaseConnection.get(); const users = UserFactory.create(3); const emails = users.map((user) => user.email); - const [proxyUser] = UserFactory.with({ accessType: UserAccessType.ADMIN }); + const [adminUser] = UserFactory.with({ accessType: UserAccessType.ADMIN }); const [event] = EventFactory.create(1); await new PortalState() - .createUsers([...users, proxyUser]) + .createUsers([...users, adminUser]) .createEvents([event]) .write(); @@ -33,11 +33,11 @@ describe('retroactive attendance submission', () => { const adminController = ControllerFactory.admin(conn); const attendanceController = ControllerFactory.attendance(conn); - await adminController.submitAttendanceForUsers({ users: emails, event: event.uuid }, proxyUser); + await adminController.submitAttendanceForUsers({ users: emails, event: event.uuid }, adminUser); for (let u = 0; u < users.length; u += 1) { const user = users[u]; - const userResponse = await userController.getUser({ uuid: user.uuid }, proxyUser); + const userResponse = await userController.getUser({ uuid: user.uuid }, adminUser); expect(userResponse.user.points).toEqual(user.points + event.pointValue); @@ -45,7 +45,7 @@ describe('retroactive attendance submission', () => { expect(attendanceResponse.attendances).toHaveLength(1); expect(attendanceResponse.attendances[0].event).toStrictEqual(event.getPublicEvent()); - const activityResponse = await userController.getUserActivityStream({ uuid: user.uuid }, proxyUser); + const activityResponse = await userController.getUserActivityStream({ uuid: user.uuid }, adminUser); expect(activityResponse.activity).toHaveLength(2); expect(activityResponse.activity[1].pointsEarned).toEqual(event.pointValue); @@ -57,11 +57,11 @@ describe('retroactive attendance submission', () => { test('does not log activity, attendance, and points for users who already attended', async () => { const conn = await DatabaseConnection.get(); const [user] = UserFactory.create(1); - const [proxyUser] = UserFactory.with({ accessType: UserAccessType.ADMIN }); + const [adminUser] = UserFactory.with({ accessType: UserAccessType.ADMIN }); const [event] = EventFactory.create(1); await new PortalState() - .createUsers([user, proxyUser]) + .createUsers([user, adminUser]) .createEvents([event]) .attendEvents([user], [event]) .write(); @@ -72,10 +72,10 @@ describe('retroactive attendance submission', () => { await adminController.submitAttendanceForUsers( { users: [user.email], event: event.uuid }, - proxyUser, + adminUser, ); - const userResponse = await userController.getUser({ uuid: user.uuid }, proxyUser); + const userResponse = await userController.getUser({ uuid: user.uuid }, adminUser); const attendanceResponse = await attendanceController.getAttendancesForCurrentUser(user); const activityResponse = await userController.getCurrentUserActivityStream(user); @@ -89,11 +89,11 @@ describe('retroactive attendance submission', () => { const conn = await DatabaseConnection.get(); const [user] = UserFactory.create(1); const [staffUser] = UserFactory.with({ accessType: UserAccessType.STAFF }); - const [proxyUser] = UserFactory.with({ accessType: UserAccessType.ADMIN }); + const [adminUser] = UserFactory.with({ accessType: UserAccessType.ADMIN }); const [event] = EventFactory.with({ requiresStaff: true, staffPointBonus: 10 }); await new PortalState() - .createUsers([user, staffUser, proxyUser]) + .createUsers([user, staffUser, adminUser]) .createEvents([event]) .write(); @@ -105,10 +105,10 @@ describe('retroactive attendance submission', () => { asStaff: true, }; - await adminController.submitAttendanceForUsers(request, proxyUser); + await adminController.submitAttendanceForUsers(request, adminUser); - const userResponse = await userController.getUser({ uuid: user.uuid }, proxyUser); - const staffUserResponse = await userController.getUser({ uuid: staffUser.uuid }, proxyUser); + const userResponse = await userController.getUser({ uuid: user.uuid }, adminUser); + const staffUserResponse = await userController.getUser({ uuid: staffUser.uuid }, adminUser); const activityResponse = await userController.getCurrentUserActivityStream(user); const staffActivityResponse = await userController.getCurrentUserActivityStream(staffUser); @@ -124,29 +124,29 @@ describe('email retrieval', () => { const conn = await DatabaseConnection.get(); const users = UserFactory.create(5); const emails = users.map((user) => user.email.toLowerCase()); - const [proxyUser] = UserFactory.with({ accessType: UserAccessType.ADMIN }); + const [adminUser] = UserFactory.with({ accessType: UserAccessType.ADMIN }); await new PortalState() - .createUsers([...users, proxyUser]) + .createUsers([...users, adminUser]) .write(); const adminController = ControllerFactory.admin(conn); - const response = await adminController.getAllEmails(proxyUser); - expect([...emails, proxyUser.email].sort()).toEqual(response.emails.sort()); + const response = await adminController.getAllEmails(adminUser); + expect([...emails, adminUser.email].sort()).toEqual(response.emails.sort()); }); test('no other emails present asides from registered users', async () => { const conn = await DatabaseConnection.get(); const users = UserFactory.create(5); - const [proxyUser] = UserFactory.with({ accessType: UserAccessType.ADMIN }); + const [adminUser] = UserFactory.with({ accessType: UserAccessType.ADMIN }); const [extraneousUser] = UserFactory.create(1); await new PortalState() - .createUsers([...users, proxyUser]) + .createUsers([...users, adminUser]) .write(); const adminController = ControllerFactory.admin(conn); - const response = await adminController.getAllEmails(proxyUser); + const response = await adminController.getAllEmails(adminUser); expect(response.emails).not.toContain(extraneousUser.email.toLowerCase()); }); }); @@ -156,10 +156,10 @@ describe('bonus points submission', () => { const conn = await DatabaseConnection.get(); const users = UserFactory.create(5); const emails = users.map((user) => user.email.toLowerCase()); - const [proxyUser] = UserFactory.with({ accessType: UserAccessType.ADMIN }); + const [adminUser] = UserFactory.with({ accessType: UserAccessType.ADMIN }); await new PortalState() - .createUsers([...users, proxyUser]) + .createUsers([...users, adminUser]) .write(); const adminController = ControllerFactory.admin(conn); @@ -167,8 +167,8 @@ describe('bonus points submission', () => { const request: CreateBonusRequest = { bonus: { description: 'Test addition of bonus points', users: emails, points: 200 } }; - const bonusResponse = await adminController.addBonus(request, proxyUser); - const userResponse = await userController.getUser({ uuid: users[0].uuid }, proxyUser); + const bonusResponse = await adminController.addBonus(request, adminUser); + const userResponse = await userController.getUser({ uuid: users[0].uuid }, adminUser); expect(userResponse.user.points).toEqual(200); expect(bonusResponse.emails.sort()).toEqual(emails.sort()); @@ -178,11 +178,11 @@ describe('bonus points submission', () => { const conn = await DatabaseConnection.get(); const users = UserFactory.create(5); const emails = users.map((user) => user.email.toLowerCase()); - const [proxyUser] = UserFactory.with({ accessType: UserAccessType.ADMIN }); + const [adminUser] = UserFactory.with({ accessType: UserAccessType.ADMIN }); const [extraneousUser] = UserFactory.create(1); await new PortalState() - .createUsers([...users, extraneousUser, proxyUser]) + .createUsers([...users, extraneousUser, adminUser]) .write(); const adminController = ControllerFactory.admin(conn); @@ -191,9 +191,9 @@ describe('bonus points submission', () => { users: emails, points: 200 } }; - await adminController.addBonus(request, proxyUser); + await adminController.addBonus(request, adminUser); - const userResponse = await userController.getUser({ uuid: extraneousUser.uuid }, proxyUser); + const userResponse = await userController.getUser({ uuid: extraneousUser.uuid }, adminUser); expect(userResponse.user.points).toEqual(0); }); From fdbbd0843a0b45f3e87150e2ac3cc89b99ecf108 Mon Sep 17 00:00:00 2001 From: Matei-Alexandru Gardus Date: Thu, 27 May 2021 14:28:36 -0700 Subject: [PATCH 03/24] Adjust array equality checks on admin tests --- tests/admin.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/admin.test.ts b/tests/admin.test.ts index 48b8ffb4..f7df0ef1 100644 --- a/tests/admin.test.ts +++ b/tests/admin.test.ts @@ -132,7 +132,7 @@ describe('email retrieval', () => { const adminController = ControllerFactory.admin(conn); const response = await adminController.getAllEmails(adminUser); - expect([...emails, adminUser.email].sort()).toEqual(response.emails.sort()); + expect([...emails, adminUser.email]).toEqual(expect.arrayContaining(response.emails)); }); test('no other emails present asides from registered users', async () => { @@ -171,7 +171,7 @@ describe('bonus points submission', () => { const userResponse = await userController.getUser({ uuid: users[0].uuid }, adminUser); expect(userResponse.user.points).toEqual(200); - expect(bonusResponse.emails.sort()).toEqual(emails.sort()); + expect(bonusResponse.emails).toEqual(expect.arrayContaining(emails)); }); test("Does not update points and activity to the users who aren't in the bonus request", async () => { From 3a27e7f81c14e0ac296cfc3e581e5661e2a71fc2 Mon Sep 17 00:00:00 2001 From: Matei-Alexandru Gardus Date: Thu, 27 May 2021 14:33:57 -0700 Subject: [PATCH 04/24] Remove test for non-present emails Test is not necessary due to lack of filtering in original controller. --- tests/admin.test.ts | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/tests/admin.test.ts b/tests/admin.test.ts index f7df0ef1..a32d7056 100644 --- a/tests/admin.test.ts +++ b/tests/admin.test.ts @@ -134,21 +134,6 @@ describe('email retrieval', () => { const response = await adminController.getAllEmails(adminUser); expect([...emails, adminUser.email]).toEqual(expect.arrayContaining(response.emails)); }); - - test('no other emails present asides from registered users', async () => { - const conn = await DatabaseConnection.get(); - const users = UserFactory.create(5); - const [adminUser] = UserFactory.with({ accessType: UserAccessType.ADMIN }); - const [extraneousUser] = UserFactory.create(1); - - await new PortalState() - .createUsers([...users, adminUser]) - .write(); - - const adminController = ControllerFactory.admin(conn); - const response = await adminController.getAllEmails(adminUser); - expect(response.emails).not.toContain(extraneousUser.email.toLowerCase()); - }); }); describe('bonus points submission', () => { From 5fc6a9aadf21dd44d1db75ac411e527058db4562 Mon Sep 17 00:00:00 2001 From: Matei-Alexandru Gardus Date: Thu, 27 May 2021 14:54:01 -0700 Subject: [PATCH 05/24] Inline functions and variables in admin tests --- tests/admin.test.ts | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/tests/admin.test.ts b/tests/admin.test.ts index a32d7056..0109e08e 100644 --- a/tests/admin.test.ts +++ b/tests/admin.test.ts @@ -1,7 +1,6 @@ -import { CreateBonusRequest } from 'api/validators/AdminControllerRequests'; -import { ActivityScope, ActivityType, SubmitAttendanceForUsersRequest, UserAccessType } from '../types'; -import { ControllerFactory } from './controllers'; -import { DatabaseConnection, EventFactory, UserFactory, PortalState } from './data'; +import {ActivityScope, ActivityType, SubmitAttendanceForUsersRequest, UserAccessType} from '../types'; +import {ControllerFactory} from './controllers'; +import {DatabaseConnection, EventFactory, PortalState, UserFactory} from './data'; beforeAll(async () => { await DatabaseConnection.connect(); @@ -147,16 +146,17 @@ describe('bonus points submission', () => { .createUsers([...users, adminUser]) .write(); - const adminController = ControllerFactory.admin(conn); - const userController = ControllerFactory.user(conn); - const request: CreateBonusRequest = { bonus: { description: 'Test addition of bonus points', + const bonus = { + description: 'Test addition of bonus points', users: emails, - points: 200 } }; - const bonusResponse = await adminController.addBonus(request, adminUser); - const userResponse = await userController.getUser({ uuid: users[0].uuid }, adminUser); + points: 200, + }; + + const createBonusResponse = await ControllerFactory.admin(conn).addBonus({ bonus }, adminUser); + const getUserResponse = await ControllerFactory.user(conn).getUser({ uuid: users[0].uuid }, adminUser); - expect(userResponse.user.points).toEqual(200); - expect(bonusResponse.emails).toEqual(expect.arrayContaining(emails)); + expect(getUserResponse.user.points).toEqual(200); + expect(createBonusResponse.emails).toEqual(expect.arrayContaining(emails)); }); test("Does not update points and activity to the users who aren't in the bonus request", async () => { @@ -170,15 +170,14 @@ describe('bonus points submission', () => { .createUsers([...users, extraneousUser, adminUser]) .write(); - const adminController = ControllerFactory.admin(conn); - const userController = ControllerFactory.user(conn); - const request: CreateBonusRequest = { bonus: { description: 'Test addition of bonus points', + const bonus = { + description: 'Test addition of bonus points', users: emails, - points: 200 } }; - - await adminController.addBonus(request, adminUser); + points: 200, + }; - const userResponse = await userController.getUser({ uuid: extraneousUser.uuid }, adminUser); + await ControllerFactory.admin(conn).addBonus({ bonus }, adminUser); + const userResponse = await ControllerFactory.user(conn).getUser({ uuid: extraneousUser.uuid }, adminUser); expect(userResponse.user.points).toEqual(0); }); From 5fe3084b8e4453754f81d33d83ac3a94417fbab9 Mon Sep 17 00:00:00 2001 From: Matei-Alexandru Gardus Date: Thu, 27 May 2021 14:57:36 -0700 Subject: [PATCH 06/24] Modify test to check bonus points for all users --- tests/admin.test.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/admin.test.ts b/tests/admin.test.ts index 0109e08e..69a72528 100644 --- a/tests/admin.test.ts +++ b/tests/admin.test.ts @@ -153,10 +153,13 @@ describe('bonus points submission', () => { }; const createBonusResponse = await ControllerFactory.admin(conn).addBonus({ bonus }, adminUser); - const getUserResponse = await ControllerFactory.user(conn).getUser({ uuid: users[0].uuid }, adminUser); - - expect(getUserResponse.user.points).toEqual(200); expect(createBonusResponse.emails).toEqual(expect.arrayContaining(emails)); + + for (let u = 0; u < users.length; u += 1) { + const user = users[u]; + const getUserResponse = await ControllerFactory.user(conn).getUser({ uuid: user.uuid }, adminUser); + expect(getUserResponse.user.points).toEqual(200); + } }); test("Does not update points and activity to the users who aren't in the bonus request", async () => { From 1e37dd250310efb180906edc352e8e48c8497533 Mon Sep 17 00:00:00 2001 From: Matei-Alexandru Gardus Date: Thu, 27 May 2021 15:01:19 -0700 Subject: [PATCH 07/24] Combine both bonus points tests into one Tests had basically the same boilerplate. Combine them to check both conditions at once. The test already checked if we were adding bonus point to _specifically_ the emails we passed in, so it's best to also check if an extra user wasn't included at the same time. --- tests/admin.test.ts | 33 +++++++-------------------------- 1 file changed, 7 insertions(+), 26 deletions(-) diff --git a/tests/admin.test.ts b/tests/admin.test.ts index 69a72528..ec6a29f4 100644 --- a/tests/admin.test.ts +++ b/tests/admin.test.ts @@ -1,6 +1,6 @@ -import {ActivityScope, ActivityType, SubmitAttendanceForUsersRequest, UserAccessType} from '../types'; -import {ControllerFactory} from './controllers'; -import {DatabaseConnection, EventFactory, PortalState, UserFactory} from './data'; +import { ActivityScope, ActivityType, SubmitAttendanceForUsersRequest, UserAccessType } from '../types'; +import { ControllerFactory } from './controllers'; +import { DatabaseConnection, EventFactory, PortalState, UserFactory } from './data'; beforeAll(async () => { await DatabaseConnection.connect(); @@ -141,9 +141,10 @@ describe('bonus points submission', () => { const users = UserFactory.create(5); const emails = users.map((user) => user.email.toLowerCase()); const [adminUser] = UserFactory.with({ accessType: UserAccessType.ADMIN }); + const [extraneousUser] = UserFactory.create(1); await new PortalState() - .createUsers([...users, adminUser]) + .createUsers([...users, extraneousUser, adminUser]) .write(); const bonus = { @@ -160,28 +161,8 @@ describe('bonus points submission', () => { const getUserResponse = await ControllerFactory.user(conn).getUser({ uuid: user.uuid }, adminUser); expect(getUserResponse.user.points).toEqual(200); } - }); - - test("Does not update points and activity to the users who aren't in the bonus request", async () => { - const conn = await DatabaseConnection.get(); - const users = UserFactory.create(5); - const emails = users.map((user) => user.email.toLowerCase()); - const [adminUser] = UserFactory.with({ accessType: UserAccessType.ADMIN }); - const [extraneousUser] = UserFactory.create(1); - - await new PortalState() - .createUsers([...users, extraneousUser, adminUser]) - .write(); - - const bonus = { - description: 'Test addition of bonus points', - users: emails, - points: 200, - }; - - await ControllerFactory.admin(conn).addBonus({ bonus }, adminUser); - const userResponse = await ControllerFactory.user(conn).getUser({ uuid: extraneousUser.uuid }, adminUser); - expect(userResponse.user.points).toEqual(0); + const getExtraUserResponse = await ControllerFactory.user(conn).getUser({ uuid: extraneousUser.uuid }, adminUser); + expect(getExtraUserResponse.user.points).toEqual(0); }); }); From 5ad4381df91ecf08990e4ba66b4067aa0557e367 Mon Sep 17 00:00:00 2001 From: Matei-Alexandru Gardus Date: Thu, 27 May 2021 15:07:39 -0700 Subject: [PATCH 08/24] Rearrange asserts to suffix response calls --- tests/admin.test.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/tests/admin.test.ts b/tests/admin.test.ts index ec6a29f4..bea440cb 100644 --- a/tests/admin.test.ts +++ b/tests/admin.test.ts @@ -75,11 +75,12 @@ describe('retroactive attendance submission', () => { ); const userResponse = await userController.getUser({ uuid: user.uuid }, adminUser); - const attendanceResponse = await attendanceController.getAttendancesForCurrentUser(user); - const activityResponse = await userController.getCurrentUserActivityStream(user); - expect(userResponse.user.points).toEqual(user.points); + + const attendanceResponse = await attendanceController.getAttendancesForCurrentUser(user); expect(attendanceResponse.attendances).toHaveLength(1); + + const activityResponse = await userController.getCurrentUserActivityStream(user); expect(activityResponse.activity).toHaveLength(2); expect(activityResponse.activity[1].description).toBeNull(); }); @@ -107,13 +108,15 @@ describe('retroactive attendance submission', () => { await adminController.submitAttendanceForUsers(request, adminUser); const userResponse = await userController.getUser({ uuid: user.uuid }, adminUser); - const staffUserResponse = await userController.getUser({ uuid: staffUser.uuid }, adminUser); - const activityResponse = await userController.getCurrentUserActivityStream(user); - const staffActivityResponse = await userController.getCurrentUserActivityStream(staffUser); - expect(userResponse.user.points).toEqual(event.pointValue); + + const staffUserResponse = await userController.getUser({ uuid: staffUser.uuid }, adminUser); expect(staffUserResponse.user.points).toEqual(event.pointValue + event.staffPointBonus); + + const activityResponse = await userController.getCurrentUserActivityStream(user); expect(activityResponse.activity[1].type).toEqual(ActivityType.ATTEND_EVENT); + + const staffActivityResponse = await userController.getCurrentUserActivityStream(staffUser); expect(staffActivityResponse.activity[1].type).toEqual(ActivityType.ATTEND_EVENT_AS_STAFF); }); }); From df1a901e015b15095b6f0c0fa8f0561ff9af95ca Mon Sep 17 00:00:00 2001 From: Matei-Alexandru Gardus Date: Tue, 8 Jun 2021 23:44:31 -0700 Subject: [PATCH 09/24] Rename adminUser to admin in admin test suite --- tests/admin.test.ts | 46 ++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/tests/admin.test.ts b/tests/admin.test.ts index bea440cb..0ab1d3f6 100644 --- a/tests/admin.test.ts +++ b/tests/admin.test.ts @@ -20,11 +20,11 @@ describe('retroactive attendance submission', () => { const conn = await DatabaseConnection.get(); const users = UserFactory.create(3); const emails = users.map((user) => user.email); - const [adminUser] = UserFactory.with({ accessType: UserAccessType.ADMIN }); + const [admin] = UserFactory.with({ accessType: UserAccessType.ADMIN }); const [event] = EventFactory.create(1); await new PortalState() - .createUsers([...users, adminUser]) + .createUsers([...users, admin]) .createEvents([event]) .write(); @@ -32,11 +32,11 @@ describe('retroactive attendance submission', () => { const adminController = ControllerFactory.admin(conn); const attendanceController = ControllerFactory.attendance(conn); - await adminController.submitAttendanceForUsers({ users: emails, event: event.uuid }, adminUser); + await adminController.submitAttendanceForUsers({ users: emails, event: event.uuid }, admin); for (let u = 0; u < users.length; u += 1) { const user = users[u]; - const userResponse = await userController.getUser({ uuid: user.uuid }, adminUser); + const userResponse = await userController.getUser({ uuid: user.uuid }, admin); expect(userResponse.user.points).toEqual(user.points + event.pointValue); @@ -44,7 +44,7 @@ describe('retroactive attendance submission', () => { expect(attendanceResponse.attendances).toHaveLength(1); expect(attendanceResponse.attendances[0].event).toStrictEqual(event.getPublicEvent()); - const activityResponse = await userController.getUserActivityStream({ uuid: user.uuid }, adminUser); + const activityResponse = await userController.getUserActivityStream({ uuid: user.uuid }, admin); expect(activityResponse.activity).toHaveLength(2); expect(activityResponse.activity[1].pointsEarned).toEqual(event.pointValue); @@ -56,11 +56,11 @@ describe('retroactive attendance submission', () => { test('does not log activity, attendance, and points for users who already attended', async () => { const conn = await DatabaseConnection.get(); const [user] = UserFactory.create(1); - const [adminUser] = UserFactory.with({ accessType: UserAccessType.ADMIN }); + const [admin] = UserFactory.with({ accessType: UserAccessType.ADMIN }); const [event] = EventFactory.create(1); await new PortalState() - .createUsers([user, adminUser]) + .createUsers([user, admin]) .createEvents([event]) .attendEvents([user], [event]) .write(); @@ -71,10 +71,10 @@ describe('retroactive attendance submission', () => { await adminController.submitAttendanceForUsers( { users: [user.email], event: event.uuid }, - adminUser, + admin, ); - const userResponse = await userController.getUser({ uuid: user.uuid }, adminUser); + const userResponse = await userController.getUser({ uuid: user.uuid }, admin); expect(userResponse.user.points).toEqual(user.points); const attendanceResponse = await attendanceController.getAttendancesForCurrentUser(user); @@ -89,11 +89,11 @@ describe('retroactive attendance submission', () => { const conn = await DatabaseConnection.get(); const [user] = UserFactory.create(1); const [staffUser] = UserFactory.with({ accessType: UserAccessType.STAFF }); - const [adminUser] = UserFactory.with({ accessType: UserAccessType.ADMIN }); + const [admin] = UserFactory.with({ accessType: UserAccessType.ADMIN }); const [event] = EventFactory.with({ requiresStaff: true, staffPointBonus: 10 }); await new PortalState() - .createUsers([user, staffUser, adminUser]) + .createUsers([user, staffUser, admin]) .createEvents([event]) .write(); @@ -105,12 +105,12 @@ describe('retroactive attendance submission', () => { asStaff: true, }; - await adminController.submitAttendanceForUsers(request, adminUser); + await adminController.submitAttendanceForUsers(request, admin); - const userResponse = await userController.getUser({ uuid: user.uuid }, adminUser); + const userResponse = await userController.getUser({ uuid: user.uuid }, admin); expect(userResponse.user.points).toEqual(event.pointValue); - const staffUserResponse = await userController.getUser({ uuid: staffUser.uuid }, adminUser); + const staffUserResponse = await userController.getUser({ uuid: staffUser.uuid }, admin); expect(staffUserResponse.user.points).toEqual(event.pointValue + event.staffPointBonus); const activityResponse = await userController.getCurrentUserActivityStream(user); @@ -126,15 +126,15 @@ describe('email retrieval', () => { const conn = await DatabaseConnection.get(); const users = UserFactory.create(5); const emails = users.map((user) => user.email.toLowerCase()); - const [adminUser] = UserFactory.with({ accessType: UserAccessType.ADMIN }); + const [admin] = UserFactory.with({ accessType: UserAccessType.ADMIN }); await new PortalState() - .createUsers([...users, adminUser]) + .createUsers([...users, admin]) .write(); const adminController = ControllerFactory.admin(conn); - const response = await adminController.getAllEmails(adminUser); - expect([...emails, adminUser.email]).toEqual(expect.arrayContaining(response.emails)); + const response = await adminController.getAllEmails(admin); + expect([...emails, admin.email]).toEqual(expect.arrayContaining(response.emails)); }); }); @@ -143,11 +143,11 @@ describe('bonus points submission', () => { const conn = await DatabaseConnection.get(); const users = UserFactory.create(5); const emails = users.map((user) => user.email.toLowerCase()); - const [adminUser] = UserFactory.with({ accessType: UserAccessType.ADMIN }); + const [admin] = UserFactory.with({ accessType: UserAccessType.ADMIN }); const [extraneousUser] = UserFactory.create(1); await new PortalState() - .createUsers([...users, extraneousUser, adminUser]) + .createUsers([...users, extraneousUser, admin]) .write(); const bonus = { @@ -156,16 +156,16 @@ describe('bonus points submission', () => { points: 200, }; - const createBonusResponse = await ControllerFactory.admin(conn).addBonus({ bonus }, adminUser); + const createBonusResponse = await ControllerFactory.admin(conn).addBonus({ bonus }, admin); expect(createBonusResponse.emails).toEqual(expect.arrayContaining(emails)); for (let u = 0; u < users.length; u += 1) { const user = users[u]; - const getUserResponse = await ControllerFactory.user(conn).getUser({ uuid: user.uuid }, adminUser); + const getUserResponse = await ControllerFactory.user(conn).getUser({ uuid: user.uuid }, admin); expect(getUserResponse.user.points).toEqual(200); } - const getExtraUserResponse = await ControllerFactory.user(conn).getUser({ uuid: extraneousUser.uuid }, adminUser); + const getExtraUserResponse = await ControllerFactory.user(conn).getUser({ uuid: extraneousUser.uuid }, admin); expect(getExtraUserResponse.user.points).toEqual(0); }); }); From aca6574d24ecab76cadc5edc5879def68d7c1554 Mon Sep 17 00:00:00 2001 From: Matei-Alexandru Gardus Date: Tue, 8 Jun 2021 23:48:29 -0700 Subject: [PATCH 10/24] Adjust order of assertions in admin test --- tests/admin.test.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/admin.test.ts b/tests/admin.test.ts index 0ab1d3f6..cf9f0c0d 100644 --- a/tests/admin.test.ts +++ b/tests/admin.test.ts @@ -132,9 +132,8 @@ describe('email retrieval', () => { .createUsers([...users, admin]) .write(); - const adminController = ControllerFactory.admin(conn); - const response = await adminController.getAllEmails(admin); - expect([...emails, admin.email]).toEqual(expect.arrayContaining(response.emails)); + const response = await ControllerFactory.admin(conn).getAllEmails(admin); + expect(expect.arrayContaining(response.emails)).toEqual([...emails, admin.email]); }); }); From 2afef57f3aee3e6f0d87b71c41469a685c80e016 Mon Sep 17 00:00:00 2001 From: Matei-Alexandru Gardus Date: Tue, 8 Jun 2021 23:49:04 -0700 Subject: [PATCH 11/24] Adjust name of extraneous user in admin test --- tests/admin.test.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tests/admin.test.ts b/tests/admin.test.ts index cf9f0c0d..0a808378 100644 --- a/tests/admin.test.ts +++ b/tests/admin.test.ts @@ -140,13 +140,12 @@ describe('email retrieval', () => { describe('bonus points submission', () => { test('updates points and activity to the users in the bonus request', async () => { const conn = await DatabaseConnection.get(); - const users = UserFactory.create(5); + const [userNotGettingBonus, ...users] = UserFactory.create(5); const emails = users.map((user) => user.email.toLowerCase()); const [admin] = UserFactory.with({ accessType: UserAccessType.ADMIN }); - const [extraneousUser] = UserFactory.create(1); await new PortalState() - .createUsers([...users, extraneousUser, admin]) + .createUsers([...users, userNotGettingBonus, admin]) .write(); const bonus = { @@ -164,7 +163,7 @@ describe('bonus points submission', () => { expect(getUserResponse.user.points).toEqual(200); } - const getExtraUserResponse = await ControllerFactory.user(conn).getUser({ uuid: extraneousUser.uuid }, admin); - expect(getExtraUserResponse.user.points).toEqual(0); + const getNoBonusUserResponse = await ControllerFactory.user(conn).getUser({ uuid: userNotGettingBonus.uuid }, admin); + expect(getNoBonusUserResponse.user.points).toEqual(0); }); }); From acdf94ac5f0b43ca62362831c1600abda64f37b5 Mon Sep 17 00:00:00 2001 From: Matei-Alexandru Gardus Date: Tue, 8 Jun 2021 23:51:05 -0700 Subject: [PATCH 12/24] Make variable for UserController in admin test Inlining UserController made one line too long. Since it is used multiple times in the test, extract variable to make its usage less verbose. --- tests/admin.test.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/admin.test.ts b/tests/admin.test.ts index 0a808378..badae372 100644 --- a/tests/admin.test.ts +++ b/tests/admin.test.ts @@ -154,16 +154,18 @@ describe('bonus points submission', () => { points: 200, }; + const userController = ControllerFactory.user(conn); + const createBonusResponse = await ControllerFactory.admin(conn).addBonus({ bonus }, admin); expect(createBonusResponse.emails).toEqual(expect.arrayContaining(emails)); for (let u = 0; u < users.length; u += 1) { const user = users[u]; - const getUserResponse = await ControllerFactory.user(conn).getUser({ uuid: user.uuid }, admin); + const getUserResponse = await userController.getUser({ uuid: user.uuid }, admin); expect(getUserResponse.user.points).toEqual(200); } - const getNoBonusUserResponse = await ControllerFactory.user(conn).getUser({ uuid: userNotGettingBonus.uuid }, admin); + const getNoBonusUserResponse = await userController.getUser({ uuid: userNotGettingBonus.uuid }, admin); expect(getNoBonusUserResponse.user.points).toEqual(0); }); }); From 2c591a531be4c135e0c08163dbf0209da69f7ab3 Mon Sep 17 00:00:00 2001 From: Matei-Alexandru Gardus Date: Fri, 2 Jul 2021 19:48:42 -0700 Subject: [PATCH 13/24] Fix nits and check attendance response details --- tests/admin.test.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/admin.test.ts b/tests/admin.test.ts index badae372..0b664028 100644 --- a/tests/admin.test.ts +++ b/tests/admin.test.ts @@ -79,6 +79,8 @@ describe('retroactive attendance submission', () => { const attendanceResponse = await attendanceController.getAttendancesForCurrentUser(user); expect(attendanceResponse.attendances).toHaveLength(1); + expect(attendanceResponse.attendances[0].event.uuid).toEqual(event.uuid); + expect(attendanceResponse.attendances[0].asStaff).toEqual(false); const activityResponse = await userController.getCurrentUserActivityStream(user); expect(activityResponse.activity).toHaveLength(2); @@ -154,11 +156,11 @@ describe('bonus points submission', () => { points: 200, }; - const userController = ControllerFactory.user(conn); - const createBonusResponse = await ControllerFactory.admin(conn).addBonus({ bonus }, admin); expect(createBonusResponse.emails).toEqual(expect.arrayContaining(emails)); + const userController = ControllerFactory.user(conn); + for (let u = 0; u < users.length; u += 1) { const user = users[u]; const getUserResponse = await userController.getUser({ uuid: user.uuid }, admin); From 6688e90a9384b690b4b1a96305caf7ea2bdc0160 Mon Sep 17 00:00:00 2001 From: Matei-Alexandru Gardus Date: Fri, 2 Jul 2021 19:50:55 -0700 Subject: [PATCH 14/24] Check for correct activities in admin test suite --- tests/admin.test.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/admin.test.ts b/tests/admin.test.ts index 0b664028..f8b7f6fb 100644 --- a/tests/admin.test.ts +++ b/tests/admin.test.ts @@ -85,6 +85,7 @@ describe('retroactive attendance submission', () => { const activityResponse = await userController.getCurrentUserActivityStream(user); expect(activityResponse.activity).toHaveLength(2); expect(activityResponse.activity[1].description).toBeNull(); + expect(activityResponse.activity[1].type).toEqual(ActivityType.ATTEND_EVENT); }); test('logs proper activity and point rewards for staff attendance', async () => { @@ -165,6 +166,12 @@ describe('bonus points submission', () => { const user = users[u]; const getUserResponse = await userController.getUser({ uuid: user.uuid }, admin); expect(getUserResponse.user.points).toEqual(200); + + const activityResponse = await userController.getCurrentUserActivityStream(user); + expect(activityResponse.activity).toHaveLength(2); + expect(activityResponse.activity[1].description).toEqual(bonus.description); + expect(activityResponse.activity[1].type).toEqual(ActivityType.BONUS_POINTS); + expect(activityResponse.activity[1].pointsEarned).toEqual(bonus.points); } const getNoBonusUserResponse = await userController.getUser({ uuid: userNotGettingBonus.uuid }, admin); From da3210a008a20794b30afaa38bc0e1637ca2a8e0 Mon Sep 17 00:00:00 2001 From: Matei-Alexandru Gardus Date: Sat, 3 Jul 2021 12:58:24 -0700 Subject: [PATCH 15/24] Add points field of ActivityModel in test suite ActivityModel is missing the `pointsEarned` field whenever creating an Activity after attending an event. This only happens in our mock tester. Add a check for this field's presence, along with fixing the inherent bug within the mock state. --- tests/admin.test.ts | 1 + tests/data/PortalState.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/tests/admin.test.ts b/tests/admin.test.ts index f8b7f6fb..0f3d00bd 100644 --- a/tests/admin.test.ts +++ b/tests/admin.test.ts @@ -86,6 +86,7 @@ describe('retroactive attendance submission', () => { expect(activityResponse.activity).toHaveLength(2); expect(activityResponse.activity[1].description).toBeNull(); expect(activityResponse.activity[1].type).toEqual(ActivityType.ATTEND_EVENT); + expect(activityResponse.activity[1].pointsEarned).toEqual(event.pointValue); }); test('logs proper activity and point rewards for staff attendance', async () => { diff --git a/tests/data/PortalState.ts b/tests/data/PortalState.ts index 2ae7e93a..91570f37 100644 --- a/tests/data/PortalState.ts +++ b/tests/data/PortalState.ts @@ -97,6 +97,7 @@ export class PortalState { type: asStaff ? ActivityType.ATTEND_EVENT_AS_STAFF : ActivityType.ATTEND_EVENT, timestamp, scope: ActivityScope.PUBLIC, + pointsEarned, })); } } From 747b44d4ec0c4dee7ba74ab481243fbef929dcbe Mon Sep 17 00:00:00 2001 From: Matei-Alexandru Gardus Date: Fri, 16 Jul 2021 16:52:00 -0700 Subject: [PATCH 16/24] Port EventFactory code from #181 Code from #181 adds ability to fake events in the past, future or present. Taken verbatim from commit 18a952c. --- tests/data/EventFactory.ts | 59 +++++++++++++++++++++++++++++++------- 1 file changed, 48 insertions(+), 11 deletions(-) diff --git a/tests/data/EventFactory.ts b/tests/data/EventFactory.ts index d17a4c59..3a13c80e 100644 --- a/tests/data/EventFactory.ts +++ b/tests/data/EventFactory.ts @@ -22,9 +22,9 @@ export class EventFactory { return substitutes.map((sub) => EventModel.merge(EventFactory.fake(), sub)); } - public static fake(substitute?: Partial): EventModel { + public static fake(): EventModel { const [start, end] = EventFactory.randomTime(); - const fake = EventModel.create({ + return EventModel.create({ uuid: uuid(), organization: FactoryUtils.pickRandomValue(EventFactory.ORGS), title: faker.datatype.hexaDecimal(10), @@ -37,19 +37,56 @@ export class EventFactory { requiresStaff: FactoryUtils.getRandomBoolean(), staffPointBonus: EventFactory.randomPointValue(), }); - return EventModel.merge(fake, substitute); + } + + public static fakePastEvent(daysAgo = 1): EventModel { + const [start, end] = EventFactory.randomPastTime(daysAgo); + return EventFactory.with({ start, end })[0]; + } + + public static fakeOngoingEvent(): EventModel { + const [start, end] = EventFactory.randomOngoingTime(); + return EventFactory.with({ start, end })[0]; + } + + public static fakeFutureEvent(daysAhead = 1): EventModel { + const [start, end] = EventFactory.randomFutureTime(daysAhead); + return EventFactory.with({ start, end })[0]; } private static randomTime(): [Date, Date] { - // between last and next week - const days = FactoryUtils.getRandomNumber(-7, 7); - // between 8 AM and 6 PM - const hour = FactoryUtils.getRandomNumber(9, 19); - // between 0.5 and 2.5 hours long, rounded to the half hour - const duration = FactoryUtils.getRandomNumber(30, 150, 30); - const start = moment().subtract(days, 'days').hour(hour); + // random day between last and next week + const day = FactoryUtils.getRandomNumber(-7, 7); + return EventFactory.randomIntervalInDay(day); + } + + private static randomPastTime(daysAgo: number): [Date, Date] { + // random day between daysAgo and a week before daysAgo + const day = FactoryUtils.getRandomNumber(-daysAgo - 7, -daysAgo); + return EventFactory.randomIntervalInDay(day); + } + + private static randomOngoingTime(): [Date, Date] { + // 0, 30 mins, or 1 hour before now + const currentHour = moment().hour(); + const hour = FactoryUtils.getRandomNumber(currentHour - 1, currentHour, 0.5); + return EventFactory.randomIntervalInDay(0, hour); + } + + private static randomFutureTime(daysAhead: number): [Date, Date] { + // random day between daysAhead and a week after daysAhead + const day = FactoryUtils.getRandomNumber(daysAhead, daysAhead + 7); + return EventFactory.randomIntervalInDay(day); + } + + private static randomIntervalInDay(day: number, hour?: number): [Date, Date] { + // default between 8 AM and 6 PM + if (!hour) hour = FactoryUtils.getRandomNumber(9, 19); + // between 1 and 2.5 hours long, rounded to the half hour + const duration = FactoryUtils.getRandomNumber(60, 150, 30); + const start = moment().add(day, 'days').hour(hour); const end = moment(start.valueOf()).add(duration, 'minutes'); - return [new Date(start.valueOf()), new Date(end.valueOf())]; + return [start.toDate(), end.toDate()]; } private static randomPointValue(): number { From a9834d4d2b19e3889423ffdae9c2f11c23041bf8 Mon Sep 17 00:00:00 2001 From: Matei-Alexandru Gardus Date: Fri, 16 Jul 2021 16:56:28 -0700 Subject: [PATCH 17/24] Adjust test suite to account for factory bug Account creation can occur after event attendance, causing activities to be out of order chronologically. Guarantee order in activities by introducing retroactive attendance in a future event. Bug in factory is not a reproducible issue in production, since account creation will always occur before event attendance in real time. --- tests/admin.test.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/admin.test.ts b/tests/admin.test.ts index 0f3d00bd..df322c9f 100644 --- a/tests/admin.test.ts +++ b/tests/admin.test.ts @@ -57,7 +57,11 @@ describe('retroactive attendance submission', () => { const conn = await DatabaseConnection.get(); const [user] = UserFactory.create(1); const [admin] = UserFactory.with({ accessType: UserAccessType.ADMIN }); - const [event] = EventFactory.create(1); + // Create event in the future to guarantee account creation does not + // have a later timestamp than any possible event attendance. + // + // Not reproducible in production. + const event = EventFactory.fakeFutureEvent(); await new PortalState() .createUsers([user, admin]) From 94e1113333c593d799f18ea32700d5e90f878998 Mon Sep 17 00:00:00 2001 From: Matei-Alexandru Gardus Date: Wed, 21 Jul 2021 22:04:33 -0700 Subject: [PATCH 18/24] Revert event factory code changes --- tests/admin.test.ts | 6 +--- tests/data/EventFactory.ts | 59 +++++++------------------------------- 2 files changed, 12 insertions(+), 53 deletions(-) diff --git a/tests/admin.test.ts b/tests/admin.test.ts index df322c9f..0f3d00bd 100644 --- a/tests/admin.test.ts +++ b/tests/admin.test.ts @@ -57,11 +57,7 @@ describe('retroactive attendance submission', () => { const conn = await DatabaseConnection.get(); const [user] = UserFactory.create(1); const [admin] = UserFactory.with({ accessType: UserAccessType.ADMIN }); - // Create event in the future to guarantee account creation does not - // have a later timestamp than any possible event attendance. - // - // Not reproducible in production. - const event = EventFactory.fakeFutureEvent(); + const [event] = EventFactory.create(1); await new PortalState() .createUsers([user, admin]) diff --git a/tests/data/EventFactory.ts b/tests/data/EventFactory.ts index 3a13c80e..d17a4c59 100644 --- a/tests/data/EventFactory.ts +++ b/tests/data/EventFactory.ts @@ -22,9 +22,9 @@ export class EventFactory { return substitutes.map((sub) => EventModel.merge(EventFactory.fake(), sub)); } - public static fake(): EventModel { + public static fake(substitute?: Partial): EventModel { const [start, end] = EventFactory.randomTime(); - return EventModel.create({ + const fake = EventModel.create({ uuid: uuid(), organization: FactoryUtils.pickRandomValue(EventFactory.ORGS), title: faker.datatype.hexaDecimal(10), @@ -37,56 +37,19 @@ export class EventFactory { requiresStaff: FactoryUtils.getRandomBoolean(), staffPointBonus: EventFactory.randomPointValue(), }); - } - - public static fakePastEvent(daysAgo = 1): EventModel { - const [start, end] = EventFactory.randomPastTime(daysAgo); - return EventFactory.with({ start, end })[0]; - } - - public static fakeOngoingEvent(): EventModel { - const [start, end] = EventFactory.randomOngoingTime(); - return EventFactory.with({ start, end })[0]; - } - - public static fakeFutureEvent(daysAhead = 1): EventModel { - const [start, end] = EventFactory.randomFutureTime(daysAhead); - return EventFactory.with({ start, end })[0]; + return EventModel.merge(fake, substitute); } private static randomTime(): [Date, Date] { - // random day between last and next week - const day = FactoryUtils.getRandomNumber(-7, 7); - return EventFactory.randomIntervalInDay(day); - } - - private static randomPastTime(daysAgo: number): [Date, Date] { - // random day between daysAgo and a week before daysAgo - const day = FactoryUtils.getRandomNumber(-daysAgo - 7, -daysAgo); - return EventFactory.randomIntervalInDay(day); - } - - private static randomOngoingTime(): [Date, Date] { - // 0, 30 mins, or 1 hour before now - const currentHour = moment().hour(); - const hour = FactoryUtils.getRandomNumber(currentHour - 1, currentHour, 0.5); - return EventFactory.randomIntervalInDay(0, hour); - } - - private static randomFutureTime(daysAhead: number): [Date, Date] { - // random day between daysAhead and a week after daysAhead - const day = FactoryUtils.getRandomNumber(daysAhead, daysAhead + 7); - return EventFactory.randomIntervalInDay(day); - } - - private static randomIntervalInDay(day: number, hour?: number): [Date, Date] { - // default between 8 AM and 6 PM - if (!hour) hour = FactoryUtils.getRandomNumber(9, 19); - // between 1 and 2.5 hours long, rounded to the half hour - const duration = FactoryUtils.getRandomNumber(60, 150, 30); - const start = moment().add(day, 'days').hour(hour); + // between last and next week + const days = FactoryUtils.getRandomNumber(-7, 7); + // between 8 AM and 6 PM + const hour = FactoryUtils.getRandomNumber(9, 19); + // between 0.5 and 2.5 hours long, rounded to the half hour + const duration = FactoryUtils.getRandomNumber(30, 150, 30); + const start = moment().subtract(days, 'days').hour(hour); const end = moment(start.valueOf()).add(duration, 'minutes'); - return [start.toDate(), end.toDate()]; + return [new Date(start.valueOf()), new Date(end.valueOf())]; } private static randomPointValue(): number { From b5f03dcc4bce5d22890a8f13ac56af8b39a2e426 Mon Sep 17 00:00:00 2001 From: Matei-Alexandru Gardus Date: Wed, 21 Jul 2021 22:11:11 -0700 Subject: [PATCH 19/24] Adjust transaction order when saving factory state Mock factory `write` function incorrectly saves attendance before any possible activities, typically causing event attendances to be stored in the database prior to activities being stored. Adjust order of flushing to database in order to consistently set smaller timestamps for account-related activities, such as account creation. --- tests/data/PortalState.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/data/PortalState.ts b/tests/data/PortalState.ts index 91570f37..e444c91c 100644 --- a/tests/data/PortalState.ts +++ b/tests/data/PortalState.ts @@ -44,8 +44,8 @@ export class PortalState { await conn.transaction(async (txn) => { this.users = await txn.save(this.users); this.events = await txn.save(this.events); - this.attendances = await txn.save(this.attendances); this.activities = await txn.save(this.activities); + this.attendances = await txn.save(this.attendances); this.merch = await txn.save(this.merch); this.orders = await txn.save(this.orders); this.feedback = await txn.save(this.feedback); From 54c65b8107bbf474a686d064cd33063b042cc543 Mon Sep 17 00:00:00 2001 From: Matei-Alexandru Gardus Date: Wed, 21 Jul 2021 22:23:17 -0700 Subject: [PATCH 20/24] Revert transaction order adjustment This reverts commit b5f03dcc4bce5d22890a8f13ac56af8b39a2e426. Tests continue to be non-deterministic despite this change. My assumption was wrong. --- tests/data/PortalState.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/data/PortalState.ts b/tests/data/PortalState.ts index e444c91c..91570f37 100644 --- a/tests/data/PortalState.ts +++ b/tests/data/PortalState.ts @@ -44,8 +44,8 @@ export class PortalState { await conn.transaction(async (txn) => { this.users = await txn.save(this.users); this.events = await txn.save(this.events); - this.activities = await txn.save(this.activities); this.attendances = await txn.save(this.attendances); + this.activities = await txn.save(this.activities); this.merch = await txn.save(this.merch); this.orders = await txn.save(this.orders); this.feedback = await txn.save(this.feedback); From fe8f65b655ecf22e7d8c9b0a587ccf8274f1fef4 Mon Sep 17 00:00:00 2001 From: Matei-Alexandru Gardus Date: Wed, 21 Jul 2021 22:24:08 -0700 Subject: [PATCH 21/24] Guarantee correct activity order in failed tests Create users at beginning of the Unix epoch to ensure guarantee of event attendance activity being second is fulfilled. Since Activities are sorted by timestamp, we need to guarantee account creation does not occur before attendance of event. Fix issue with above workaround, providing explicit method whenever necessary. --- tests/admin.test.ts | 5 ++++- tests/data/PortalState.ts | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/tests/admin.test.ts b/tests/admin.test.ts index 0f3d00bd..0acc3507 100644 --- a/tests/admin.test.ts +++ b/tests/admin.test.ts @@ -60,7 +60,10 @@ describe('retroactive attendance submission', () => { const [event] = EventFactory.create(1); await new PortalState() - .createUsers([user, admin]) + // Create users at beginning of time to ensure that account creation time does not + // end up after event attendance time, causing the order guarantee for activities + // below to fail. + .createUsersAtBeginningOfTime([user, admin]) .createEvents([event]) .attendEvents([user], [event]) .write(); diff --git a/tests/data/PortalState.ts b/tests/data/PortalState.ts index 91570f37..9fa6a2c8 100644 --- a/tests/data/PortalState.ts +++ b/tests/data/PortalState.ts @@ -66,6 +66,22 @@ export class PortalState { return this; } + public createUsersAtBeginningOfTime(users: UserModel[]): PortalState { + for (let u = 0; u < users.length; u += 1) { + const user = users[u]; + user.email = user.email.toLowerCase(); + this.users.push(user); + const beginningOfTime = new Date(0); + this.activities.push(ActivityModel.create({ + user, + type: ActivityType.ACCOUNT_CREATE, + scope: ActivityScope.PUBLIC, + timestamp: beginningOfTime, + })); + } + return this; + } + public createEvents(events: EventModel[]): PortalState { this.events = this.events.concat(events); return this; From b41e0da36c695e7d502fd93379c3bb2e0a76a797 Mon Sep 17 00:00:00 2001 From: Matei-Alexandru Gardus Date: Wed, 21 Jul 2021 22:36:50 -0700 Subject: [PATCH 22/24] Merge user creation methods to have optional args Rather than having two explicit methods, merge methods and use optional timestamp argument to allow for custom creation of users at explicit times. --- tests/admin.test.ts | 2 +- tests/data/PortalState.ts | 19 ++----------------- 2 files changed, 3 insertions(+), 18 deletions(-) diff --git a/tests/admin.test.ts b/tests/admin.test.ts index 0acc3507..3a58dd06 100644 --- a/tests/admin.test.ts +++ b/tests/admin.test.ts @@ -63,7 +63,7 @@ describe('retroactive attendance submission', () => { // Create users at beginning of time to ensure that account creation time does not // end up after event attendance time, causing the order guarantee for activities // below to fail. - .createUsersAtBeginningOfTime([user, admin]) + .createUsers([user, admin], new Date(0)) .createEvents([event]) .attendEvents([user], [event]) .write(); diff --git a/tests/data/PortalState.ts b/tests/data/PortalState.ts index 9fa6a2c8..b6ce909a 100644 --- a/tests/data/PortalState.ts +++ b/tests/data/PortalState.ts @@ -52,7 +52,7 @@ export class PortalState { }); } - public createUsers(users: UserModel[]): PortalState { + public createUsers(users: UserModel[], timestamp?: Date): PortalState { for (let u = 0; u < users.length; u += 1) { const user = users[u]; user.email = user.email.toLowerCase(); @@ -61,22 +61,7 @@ export class PortalState { user, type: ActivityType.ACCOUNT_CREATE, scope: ActivityScope.PUBLIC, - })); - } - return this; - } - - public createUsersAtBeginningOfTime(users: UserModel[]): PortalState { - for (let u = 0; u < users.length; u += 1) { - const user = users[u]; - user.email = user.email.toLowerCase(); - this.users.push(user); - const beginningOfTime = new Date(0); - this.activities.push(ActivityModel.create({ - user, - type: ActivityType.ACCOUNT_CREATE, - scope: ActivityScope.PUBLIC, - timestamp: beginningOfTime, + timestamp: timestamp || new Date(), })); } return this; From 4c3f0fcaa4df3c0c1ee61f0a55fee6ebd56cfe78 Mon Sep 17 00:00:00 2001 From: Matei-Alexandru Gardus Date: Wed, 21 Jul 2021 22:52:51 -0700 Subject: [PATCH 23/24] Adjust user creation method to have set timestamp Use specified timestamp of 1 month before current time to place account creation in time before most activities for testing. --- tests/data/PortalState.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/data/PortalState.ts b/tests/data/PortalState.ts index b6ce909a..e4307d2d 100644 --- a/tests/data/PortalState.ts +++ b/tests/data/PortalState.ts @@ -1,5 +1,6 @@ import * as rfdc from 'rfdc'; import { flatten } from 'underscore'; +import * as moment from 'moment'; import { AttendanceModel } from '../../models/AttendanceModel'; import { EventModel } from '../../models/EventModel'; import { MerchandiseCollectionModel } from '../../models/MerchandiseCollectionModel'; @@ -52,7 +53,7 @@ export class PortalState { }); } - public createUsers(users: UserModel[], timestamp?: Date): PortalState { + public createUsers(users: UserModel[]): PortalState { for (let u = 0; u < users.length; u += 1) { const user = users[u]; user.email = user.email.toLowerCase(); @@ -61,7 +62,7 @@ export class PortalState { user, type: ActivityType.ACCOUNT_CREATE, scope: ActivityScope.PUBLIC, - timestamp: timestamp || new Date(), + timestamp: moment().subtract(1, 'months'), })); } return this; From b7ab2957753968e83ae0dcfa11e6bfe545251862 Mon Sep 17 00:00:00 2001 From: Matei-Alexandru Gardus Date: Wed, 21 Jul 2021 23:35:26 -0700 Subject: [PATCH 24/24] Fix argument call for creating users in test suite --- tests/admin.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/admin.test.ts b/tests/admin.test.ts index 3a58dd06..0ea9fda3 100644 --- a/tests/admin.test.ts +++ b/tests/admin.test.ts @@ -63,7 +63,7 @@ describe('retroactive attendance submission', () => { // Create users at beginning of time to ensure that account creation time does not // end up after event attendance time, causing the order guarantee for activities // below to fail. - .createUsers([user, admin], new Date(0)) + .createUsers([user, admin]) .createEvents([event]) .attendEvents([user], [event]) .write();