From 4fdfa5b2aeb9bdbf096615958d8238855fad7652 Mon Sep 17 00:00:00 2001 From: Andre Pestana <78114138+andrepestana-aot@users.noreply.github.com> Date: Thu, 27 Apr 2023 14:14:52 -0700 Subject: [PATCH] #1835 - E2E Student Authentication (#1902) * initial commit * changed to use discovery service * test to fix heap out of memory * test to fix heap out of memory * test to fix heap out of memory * test to fix heap out of memory * test to fix heap out of memory * removed unnecessary unknown type * review issues * review issues * review issues * review issues --- ...controller.getOverawardBalance.e2e-spec.ts | 75 +++++++++++------ ...troller.getOverawardsByStudent.e2e-spec.ts | 83 ++++++++++++------- ...s.controller.getStudentProfile.e2e-spec.ts | 74 +++++++++++++++++ .../apps/api/src/testHelpers/auth/index.ts | 1 + .../testHelpers/auth/student-user-helpers.ts | 27 ++++++ .../testing-modules/testing-modules-helper.ts | 3 +- .../test-utils/src/modules/modules-utils.ts | 29 +++++++ sources/packages/backend/package.json | 4 +- 8 files changed, 235 insertions(+), 61 deletions(-) create mode 100644 sources/packages/backend/apps/api/src/route-controllers/student/_tests_/e2e/student.students.controller.getStudentProfile.e2e-spec.ts create mode 100644 sources/packages/backend/apps/api/src/testHelpers/auth/student-user-helpers.ts diff --git a/sources/packages/backend/apps/api/src/route-controllers/overaward/_tests_/e2e/overaward.students.controller.getOverawardBalance.e2e-spec.ts b/sources/packages/backend/apps/api/src/route-controllers/overaward/_tests_/e2e/overaward.students.controller.getOverawardBalance.e2e-spec.ts index c125857cf7..24cb258387 100644 --- a/sources/packages/backend/apps/api/src/route-controllers/overaward/_tests_/e2e/overaward.students.controller.getOverawardBalance.e2e-spec.ts +++ b/sources/packages/backend/apps/api/src/route-controllers/overaward/_tests_/e2e/overaward.students.controller.getOverawardBalance.e2e-spec.ts @@ -1,64 +1,85 @@ import { HttpStatus, INestApplication } from "@nestjs/common"; import * as request from "supertest"; -import { createFakeDisbursementOveraward } from "@sims/test-utils"; -import { Repository } from "typeorm"; +import { + createFakeDisbursementOveraward, + saveFakeStudent, +} from "@sims/test-utils"; +import { DataSource, Repository } from "typeorm"; import { DisbursementOveraward, DisbursementOverawardOriginType, - Student, } from "@sims/sims-db"; import { BEARER_AUTH_TYPE, createTestingAppModule, FakeStudentUsersTypes, - getStudentByFakeStudentUserType, getStudentToken, + mockUserLoginInfo, } from "../../../../testHelpers"; +import { TestingModule } from "@nestjs/testing"; describe("OverawardStudentsController(e2e)-getOverawardBalance", () => { let app: INestApplication; - let student: Student; + let appDataSource: DataSource; + let appModule: TestingModule; let disbursementOverawardRepo: Repository; beforeAll(async () => { - const { nestApplication, dataSource } = await createTestingAppModule(); + const { nestApplication, module, dataSource } = + await createTestingAppModule(); app = nestApplication; - student = await getStudentByFakeStudentUserType( - FakeStudentUsersTypes.FakeStudentUserType1, - dataSource, - ); + appDataSource = dataSource; + appModule = module; disbursementOverawardRepo = dataSource.getRepository(DisbursementOveraward); }); - it.skip("Should return correct value for overaward balance when available.", async () => { + it("Should return correct value for overaward balance when available.", async () => { // Arrange + const student = await saveFakeStudent(appDataSource); + + // Mock user service to return the saved student. + await mockUserLoginInfo(appModule, student); + + // Get any student user token. + const studentToken = await getStudentToken( + FakeStudentUsersTypes.FakeStudentUserType1, + ); + // Create an overaward. - const legacyOveraward = createFakeDisbursementOveraward({ student }); - legacyOveraward.disbursementValueCode = "CSLF"; - legacyOveraward.overawardValue = 500; - legacyOveraward.originType = + const legacyOverawardCSLF = createFakeDisbursementOveraward({ student }); + legacyOverawardCSLF.disbursementValueCode = "CSLF"; + legacyOverawardCSLF.overawardValue = 500; + legacyOverawardCSLF.originType = DisbursementOverawardOriginType.LegacyOveraward; + + // Create a manual overaward deduction. + const manualOverawardCSLF = createFakeDisbursementOveraward({ student }); + manualOverawardCSLF.disbursementValueCode = "CSLF"; + manualOverawardCSLF.overawardValue = -100; + manualOverawardCSLF.originType = + DisbursementOverawardOriginType.ManualRecord; + // Create a manual overaward deduction. - const manualOveraward = createFakeDisbursementOveraward({ student }); - manualOveraward.disbursementValueCode = "CSLF"; - manualOveraward.overawardValue = -100; - manualOveraward.originType = DisbursementOverawardOriginType.ManualRecord; + const manualOverawardCSLP = createFakeDisbursementOveraward({ student }); + manualOverawardCSLP.disbursementValueCode = "CSLP"; + manualOverawardCSLP.overawardValue = 99; + manualOverawardCSLP.originType = + DisbursementOverawardOriginType.ManualRecord; + // Persist the overawards. - await disbursementOverawardRepo.save([legacyOveraward, manualOveraward]); + await disbursementOverawardRepo.save([ + legacyOverawardCSLF, + manualOverawardCSLF, + manualOverawardCSLP, + ]); const endpoint = "/students/overaward/balance"; - const studentToken = await getStudentToken( - FakeStudentUsersTypes.FakeStudentUserType1, - ); // Act/Assert await request(app.getHttpServer()) .get(endpoint) .auth(studentToken, BEARER_AUTH_TYPE) .expect(HttpStatus.OK) - .then((response) => { - // TODO change the mock student token. - expect(response.body.overawardBalanceValues.CSLF).toBe("400.00"); - }); + .expect({ overawardBalanceValues: { CSLF: "400.00", CSLP: "99.00" } }); }); afterAll(async () => { diff --git a/sources/packages/backend/apps/api/src/route-controllers/overaward/_tests_/e2e/overaward.students.controller.getOverawardsByStudent.e2e-spec.ts b/sources/packages/backend/apps/api/src/route-controllers/overaward/_tests_/e2e/overaward.students.controller.getOverawardsByStudent.e2e-spec.ts index c2cfdb08dc..3412f7f18e 100644 --- a/sources/packages/backend/apps/api/src/route-controllers/overaward/_tests_/e2e/overaward.students.controller.getOverawardsByStudent.e2e-spec.ts +++ b/sources/packages/backend/apps/api/src/route-controllers/overaward/_tests_/e2e/overaward.students.controller.getOverawardsByStudent.e2e-spec.ts @@ -4,45 +4,55 @@ import { createFakeDisbursementOveraward, createFakeStudentAssessment, createFakeApplication, + saveFakeStudent, } from "@sims/test-utils"; -import { Repository } from "typeorm"; +import { DataSource, Repository } from "typeorm"; import { Application, DisbursementOveraward, DisbursementOverawardOriginType, - Student, StudentAssessment, } from "@sims/sims-db"; import { BEARER_AUTH_TYPE, createTestingAppModule, - getStudentToken, FakeStudentUsersTypes, - getStudentByFakeStudentUserType, + getStudentToken, + mockUserLoginInfo, } from "../../../../testHelpers"; -import { StudentsOverawardAPIOutDTO } from "../../models/overaward.dto"; +import { TestingModule } from "@nestjs/testing"; describe("OverawardStudentsController(e2e)-getOverawardsByStudent", () => { let app: INestApplication; + let appDataSource: DataSource; + let appModule: TestingModule; let assessmentRepo: Repository; let applicationRepo: Repository; let disbursementOverawardRepo: Repository; - let student: Student; beforeAll(async () => { - const { nestApplication, dataSource } = await createTestingAppModule(); + const { nestApplication, module, dataSource } = + await createTestingAppModule(); app = nestApplication; assessmentRepo = dataSource.getRepository(StudentAssessment); applicationRepo = dataSource.getRepository(Application); disbursementOverawardRepo = dataSource.getRepository(DisbursementOveraward); - student = await getStudentByFakeStudentUserType( - FakeStudentUsersTypes.FakeStudentUserType1, - dataSource, - ); + appDataSource = dataSource; + appModule = module; }); it("Should return student overawards when available.", async () => { // Arrange + const student = await saveFakeStudent(appDataSource); + + // Mock user service to return the saved student. + await mockUserLoginInfo(appModule, student); + + // Get any student user token. + const studentToken = await getStudentToken( + FakeStudentUsersTypes.FakeStudentUserType1, + ); + // Prepare the student assessment to create overaward. const application = await applicationRepo.save( createFakeApplication({ @@ -62,33 +72,44 @@ describe("OverawardStudentsController(e2e)-getOverawardsByStudent", () => { reassessmentOveraward.originType = DisbursementOverawardOriginType.ReassessmentOveraward; reassessmentOveraward.addedDate = new Date(); - await disbursementOverawardRepo.save(reassessmentOveraward); - const endpoint = "/students/overaward"; - const studentToken = await getStudentToken( - FakeStudentUsersTypes.FakeStudentUserType1, + const savedReassessmentOveraward = await disbursementOverawardRepo.save( + reassessmentOveraward, + ); + // Create a manual overaward deduction. + const manualOveraward = createFakeDisbursementOveraward({ student }); + manualOveraward.disbursementValueCode = "CSLP"; + manualOveraward.overawardValue = -123; + manualOveraward.originType = DisbursementOverawardOriginType.ManualRecord; + manualOveraward.addedDate = new Date(); + const savedManualOveraward = await disbursementOverawardRepo.save( + manualOveraward, ); + const endpoint = "/students/overaward"; + // Act/Assert await request(app.getHttpServer()) .get(endpoint) .auth(studentToken, BEARER_AUTH_TYPE) .expect(HttpStatus.OK) - .then((response) => { - expect(response.body.length).toBeGreaterThan(0); - const overawards = response.body as StudentsOverawardAPIOutDTO[]; - // TODO change the expect.arrayContaining, when we create the mock student token. - expect(overawards).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - applicationNumber: - reassessmentOveraward.studentAssessment.application - .applicationNumber, - awardValueCode: reassessmentOveraward.disbursementValueCode, - overawardValue: reassessmentOveraward.overawardValue, - }), - ]), - ); - }); + .expect([ + { + dateAdded: savedManualOveraward.addedDate.toISOString(), + createdAt: savedManualOveraward.createdAt.toISOString(), + overawardOrigin: savedManualOveraward.originType, + awardValueCode: savedManualOveraward.disbursementValueCode, + overawardValue: savedManualOveraward.overawardValue, + }, + { + dateAdded: savedReassessmentOveraward.addedDate.toISOString(), + createdAt: savedReassessmentOveraward.createdAt.toISOString(), + overawardOrigin: savedReassessmentOveraward.originType, + awardValueCode: savedReassessmentOveraward.disbursementValueCode, + overawardValue: savedReassessmentOveraward.overawardValue, + applicationNumber: application.applicationNumber, + assessmentTriggerType: studentAssessment.triggerType, + }, + ]); }); afterAll(async () => { diff --git a/sources/packages/backend/apps/api/src/route-controllers/student/_tests_/e2e/student.students.controller.getStudentProfile.e2e-spec.ts b/sources/packages/backend/apps/api/src/route-controllers/student/_tests_/e2e/student.students.controller.getStudentProfile.e2e-spec.ts new file mode 100644 index 0000000000..4440bcbf0b --- /dev/null +++ b/sources/packages/backend/apps/api/src/route-controllers/student/_tests_/e2e/student.students.controller.getStudentProfile.e2e-spec.ts @@ -0,0 +1,74 @@ +import { HttpStatus, INestApplication } from "@nestjs/common"; +import * as request from "supertest"; +import { DataSource } from "typeorm"; +import { + BEARER_AUTH_TYPE, + createTestingAppModule, + FakeStudentUsersTypes, + getStudentToken, + mockUserLoginInfo, +} from "../../../../testHelpers"; +import { saveFakeStudent } from "@sims/test-utils"; +import { determinePDStatus, getUserFullName } from "../../../../utilities"; +import { TestingModule } from "@nestjs/testing"; + +describe("StudentInstitutionsController(e2e)-getStudentProfile", () => { + let app: INestApplication; + let appDataSource: DataSource; + let appModule: TestingModule; + const endpoint = "/students/student"; + + beforeAll(async () => { + const { nestApplication, module, dataSource } = + await createTestingAppModule(); + app = nestApplication; + appDataSource = dataSource; + appModule = module; + }); + + it("Should get the student profile when a student account exists.", async () => { + // Arrange + const student = await saveFakeStudent(appDataSource); + + // Mock user service to return the saved student. + await mockUserLoginInfo(appModule, student); + + // Get any student user token. + const studentToken = await getStudentToken( + FakeStudentUsersTypes.FakeStudentUserType1, + ); + + // Act/Assert + await request(app.getHttpServer()) + .get(endpoint) + .auth(studentToken, BEARER_AUTH_TYPE) + .expect(HttpStatus.OK) + .expect({ + firstName: student.user.firstName, + lastName: student.user.lastName, + fullName: getUserFullName(student.user), + email: student.user.email, + gender: student.gender, + dateOfBirth: student.birthDate, + contact: { + address: { + addressLine1: student.contactInfo.address.addressLine1, + provinceState: student.contactInfo.address.provinceState, + country: student.contactInfo.address.country, + city: student.contactInfo.address.city, + postalCode: student.contactInfo.address.postalCode, + canadaPostalCode: student.contactInfo.address.postalCode, + selectedCountry: student.contactInfo.address.selectedCountry, + }, + phone: student.contactInfo.phone, + }, + pdStatus: determinePDStatus(student), + validSin: student.sinValidation.isValidSIN, + sin: student.sinValidation.sin, + }); + }); + + afterAll(async () => { + await app?.close(); + }); +}); diff --git a/sources/packages/backend/apps/api/src/testHelpers/auth/index.ts b/sources/packages/backend/apps/api/src/testHelpers/auth/index.ts index f0b089c5d7..66ce177e3c 100644 --- a/sources/packages/backend/apps/api/src/testHelpers/auth/index.ts +++ b/sources/packages/backend/apps/api/src/testHelpers/auth/index.ts @@ -4,3 +4,4 @@ export * from "./aest-auth-helpers"; export * from "./institution-token-helpers"; export * from "./institution-auth-helpers"; export * from "./student-token-helpers"; +export * from "./student-user-helpers"; diff --git a/sources/packages/backend/apps/api/src/testHelpers/auth/student-user-helpers.ts b/sources/packages/backend/apps/api/src/testHelpers/auth/student-user-helpers.ts new file mode 100644 index 0000000000..01383e4007 --- /dev/null +++ b/sources/packages/backend/apps/api/src/testHelpers/auth/student-user-helpers.ts @@ -0,0 +1,27 @@ +import { TestingModule } from "@nestjs/testing"; +import { IdentityProviders, Student } from "@sims/sims-db"; +import { getProviderInstanceForModule } from "@sims/test-utils"; +import { AuthModule } from "../../auth/auth.module"; +import { UserService } from "../../services"; + +/** + * Mocks user log info from student service to return the passed student. + * @param testingModule nest testing module. + * @param student a persisted student object. + */ +export async function mockUserLoginInfo( + testingModule: TestingModule, + student: Student, +) { + const userService = await getProviderInstanceForModule( + testingModule, + AuthModule, + UserService, + ); + userService.getUserLoginInfo = jest.fn().mockResolvedValue({ + id: student.user.id, + isActive: true, + studentId: student.id, + identityProviderType: IdentityProviders.BCSC, + }); +} diff --git a/sources/packages/backend/apps/api/src/testHelpers/testing-modules/testing-modules-helper.ts b/sources/packages/backend/apps/api/src/testHelpers/testing-modules/testing-modules-helper.ts index 9b12596dad..0a2bfbd2aa 100644 --- a/sources/packages/backend/apps/api/src/testHelpers/testing-modules/testing-modules-helper.ts +++ b/sources/packages/backend/apps/api/src/testHelpers/testing-modules/testing-modules-helper.ts @@ -11,6 +11,7 @@ import { } from "@sims/test-utils"; import { QueueModule } from "@sims/services/queue"; import { ZeebeModule } from "@sims/services"; +import { DiscoveryModule } from "@golevelup/nestjs-discovery"; /** * Result from a createTestingModule to support E2E tests creation. @@ -39,7 +40,7 @@ export async function createTestingAppModule(): Promise( + testingModule: TestingModule, + module: Type, + provider: Type, +): Promise { + const discovery = testingModule.get(DiscoveryService); + const providerResults = await discovery.providers((aProviderWith) => { + return ( + aProviderWith.parentModule.instance instanceof module && + aProviderWith.instance instanceof provider + ); + }); + const [providerResult] = providerResults; + return providerResult.instance as T; +} diff --git a/sources/packages/backend/package.json b/sources/packages/backend/package.json index 9aadcd2cea..092b38228e 100644 --- a/sources/packages/backend/package.json +++ b/sources/packages/backend/package.json @@ -22,7 +22,7 @@ "test:watch": "jest --watch ", "test:cov": "cross-env ENVIRONMENT=test jest --coverage --forceExit", "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e:api": "npm run db:seed:test filter=CreateInstitutionsAndAuthenticationUsers,CreateAESTUsers,CreateStudentUsers && cross-env ENVIRONMENT=test jest --collect-coverage --verbose --config ./apps/api/test/jest-e2e.json --forceExit", + "test:e2e:api": "npm run db:seed:test filter=CreateInstitutionsAndAuthenticationUsers,CreateAESTUsers,CreateStudentUsers && cross-env NODE_OPTIONS=--max_old_space_size=8192 ENVIRONMENT=test jest --collect-coverage --verbose --config ./apps/api/test/jest-e2e.json --forceExit", "test:e2e:api:local": "cross-env ENVIRONMENT=test jest --config ./apps/api/test/jest-e2e.json --forceExit", "test:e2e:workers": "npm run migration:run && cross-env ENVIRONMENT=test jest --collect-coverage --verbose --config ./apps/workers/test/jest-e2e.json --forceExit", "test:e2e:workflow:local": "cross-env ENVIRONMENT=test jest --verbose --config ./workflow/test/jest-e2e.json --forceExit", @@ -157,4 +157,4 @@ "^@sims/integrations(|/.*)$": "/libs/integrations/src/$1" } } -} +} \ No newline at end of file