Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#1724 - E2E/Units Tests - Review Queue Consumers E2E Tests #1872

Merged
merged 27 commits into from
Apr 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
267d6fd
E2E StartApplicationAssessmentProcessor
andrewsignori-aot Apr 11, 2023
540538b
Added E2E for CancelApplicationAssessment
andrewsignori-aot Apr 11, 2023
30601f4
Removed unnecessary methods from createMockedZeebeModule
andrewsignori-aot Apr 11, 2023
3152fc9
Reviewing eCert full-time test
andrewsignori-aot Apr 11, 2023
3a4887c
Fixed Sonarcloud issue
andrewsignori-aot Apr 11, 2023
1e53193
Files renaming,
andrewsignori-aot Apr 11, 2023
c9c2443
Files renaming, revert
andrewsignori-aot Apr 11, 2023
30ddc15
Merge branch 'feature/#1724-queue-conumers-e2e-tests' of https://gith…
andrewsignori-aot Apr 11, 2023
8345fb3
Fix index file conflict
andrewsignori-aot Apr 11, 2023
f8329f8
Fix tests
andrewsignori-aot Apr 11, 2023
5fcaff5
E2E fix
andrewsignori-aot Apr 11, 2023
7e4811c
E2E description change
andrewsignori-aot Apr 11, 2023
c622158
Moving E2E e-cert file
andrewsignori-aot Apr 11, 2023
80948ec
Moving file
andrewsignori-aot Apr 11, 2023
dc2d3d5
Fire renamed
andrewsignori-aot Apr 11, 2023
dffaee7
Test rename file
andrewsignori-aot Apr 11, 2023
0ac78ec
E2E e-cert restored.
andrewsignori-aot Apr 11, 2023
06f519d
Moving file
andrewsignori-aot Apr 11, 2023
07b8928
e-Cert E2E updates
andrewsignori-aot Apr 11, 2023
f72a6c3
Revert unwanted file.
andrewsignori-aot Apr 12, 2023
8eb0d26
Added some missing periods.
andrewsignori-aot Apr 12, 2023
9b2c78c
Random generated workflowInstanceId
andrewsignori-aot Apr 12, 2023
84c4f3f
Removing redis dependency
andrewsignori-aot Apr 13, 2023
552bc54
Code cleanup
andrewsignori-aot Apr 13, 2023
5869794
PR review changes
andrewsignori-aot Apr 13, 2023
742cc3c
PR comments
andrewsignori-aot Apr 13, 2023
7bb4389
PR comments
andrewsignori-aot Apr 13, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions sources/packages/backend/apps/api/src/testHelpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ export * from "./moked-http-contexts/http-context-mock";
export * from "./moked-http-contexts/reflector-mock";
export * from "./auth/token-helpers";
export * from "./mocked-providers/jwt-service-mock";
export * from "./mocked-providers/queue-module-mock";
export * from "./mocked-providers/zeebe-client-mock";
export * from "./testing-modules/testing-modules-helper";
export * from "./auth";
export * from "./fake-entities";

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,13 @@ import { DataSource } from "typeorm";
import { AppModule } from "../../app.module";
import { KeycloakConfig } from "../../auth";
import { setGlobalPipes } from "../../utilities";
import { MockedQueueModule } from "../mocked-providers/queue-module-mock";
import { createMockedZeebeModule } from "../mocked-providers/zeebe-client-mock";
import {
QueueModuleMock,
createZeebeModuleMock,
overrideImportsMetadata,
} from "@sims/test-utils";
import { QueueModule } from "@sims/services/queue";
import { ZeebeModule } from "@sims/services";

/**
* Result from a createTestingModule to support E2E tests creation.
Expand All @@ -21,9 +26,20 @@ export class CreateTestingModuleResult {
* @returns creation results with objects to support E2E tests.
*/
export async function createTestingAppModule(): Promise<CreateTestingModuleResult> {
overrideImportsMetadata(
AppModule,
{
replace: QueueModule,
by: QueueModuleMock,
},
{
replace: ZeebeModule,
by: createZeebeModuleMock(),
},
);
await KeycloakConfig.load();
const module: TestingModule = await Test.createTestingModule({
imports: [AppModule, createMockedZeebeModule(), MockedQueueModule],
imports: [AppModule],
}).compile();
const nestApplication = module.createNestApplication();
setGlobalPipes(nestApplication);
Expand Down
4 changes: 2 additions & 2 deletions sources/packages/backend/apps/api/test/auth.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
} from "../src/utilities/certificate-utils";
import { AuthTestController } from "../src/testHelpers/controllers/auth-test/auth-test.controller";
import { KeycloakService } from "../src/services/auth/keycloak/keycloak.service";
import { createMockedZeebeModule } from "../src/testHelpers/mocked-providers/zeebe-client-mock";
import { createZeebeModuleMock } from "@sims/test-utils";

describe("Authentication (e2e)", () => {
// Nest application to be shared for all e2e tests
Expand Down Expand Up @@ -41,7 +41,7 @@ describe("Authentication (e2e)", () => {
aestAccessToken = aestToken.access_token;

const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule, createMockedZeebeModule()],
imports: [AppModule, createZeebeModuleMock()],
// AuthTestController is used only for e2e tests and could be
// changed as needed to implement more test scenarios.
controllers: [AuthTestController],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
import { ZBClient } from "zeebe-node";
import { Job } from "bull";
import { createMock } from "@golevelup/ts-jest";
import { INestApplication } from "@nestjs/common";
import { CancelAssessmentQueueInDTO } from "@sims/services/queue";
import { QueueNames } from "@sims/utilities";
import {
createTestingAppModule,
describeProcessorRootTest,
} from "../../../../test/helpers";
import { CancelApplicationAssessmentProcessor } from "../cancel-application-assessment.processor";
import { DataSource, Repository } from "typeorm";
import {
createFakeDisbursementOveraward,
saveFakeApplicationDisbursements,
} from "@sims/test-utils";
import {
ApplicationStatus,
DisbursementOveraward,
DisbursementSchedule,
DisbursementScheduleStatus,
StudentAssessment,
} from "@sims/sims-db";
import * as faker from "faker";

describe(
describeProcessorRootTest(QueueNames.CancelApplicationAssessment),
() => {
let app: INestApplication;
let processor: CancelApplicationAssessmentProcessor;
let zbClientMock: ZBClient;
let appDataSource: DataSource;
let studentAssessmentRepo: Repository<StudentAssessment>;
let disbursementOverawardRepo: Repository<DisbursementOveraward>;
let disbursementScheduleRepo: Repository<DisbursementSchedule>;

beforeAll(async () => {
const { nestApplication, dataSource, zbClient } =
await createTestingAppModule();
app = nestApplication;
zbClientMock = zbClient;
appDataSource = dataSource;
studentAssessmentRepo = dataSource.getRepository(StudentAssessment);
disbursementOverawardRepo = dataSource.getRepository(
DisbursementOveraward,
);
disbursementScheduleRepo = dataSource.getRepository(DisbursementSchedule);
// Processor under test.
processor = app.get(CancelApplicationAssessmentProcessor);
});

beforeEach(() => {
jest.resetAllMocks();
});

it("Should cancel the assessment pending disbursements and rollback overawards when the cancelled application has overawards and also one sent and one pending disbursements.", async () => {
// Arrange
const workflowInstanceId = faker.random
.number({
min: 1000000000,
max: 9999999999,
})
.toString();
// Application and disbursements.
const application = await saveFakeApplicationDisbursements(
appDataSource,
null,
{
applicationStatus: ApplicationStatus.Cancelled,
ann-aot marked this conversation as resolved.
Show resolved Hide resolved
createSecondDisbursement: true,
},
);
// Adjust assessment.
const studentAssessment = application.currentAssessment;
studentAssessment.assessmentWorkflowId = workflowInstanceId;
await studentAssessmentRepo.save(application.currentAssessment);
dheepak-aot marked this conversation as resolved.
Show resolved Hide resolved
// Adjust disbursements.
const [firstDisbursement, secondDisbursement] =
studentAssessment.disbursementSchedules;
firstDisbursement.disbursementScheduleStatus =
DisbursementScheduleStatus.Sent;
await disbursementScheduleRepo.save(firstDisbursement);
// Add a student overaward.
const overaward = createFakeDisbursementOveraward({
student: application.student,
studentAssessment,
});
await disbursementOverawardRepo.save(overaward);
// Queued job.
const job = createMock<Job<CancelAssessmentQueueInDTO>>({
data: { assessmentId: studentAssessment.id },
});

// Act
const result = await processor.cancelAssessment(job);

// Assert
expect(zbClientMock.cancelProcessInstance).toBeCalledWith(
workflowInstanceId,
);
expect(result.summary).toStrictEqual([
`Cancelling application assessment id ${studentAssessment.id}`,
`Found workflow id ${workflowInstanceId}.`,
"Workflow instance successfully cancelled.",
"Rolling back overawards, if any.",
"Assessment cancelled with success.",
]);

// Assert that overawards were soft deleted.
const updatedOveraward = await disbursementOverawardRepo.findOne({
where: { id: overaward.id },
withDeleted: true,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

});
expect(updatedOveraward.deletedAt).toBeDefined();

// Assert sent disbursement was not affected.
const firstDisbursementUpdated = await disbursementScheduleRepo.findOneBy(
{ id: firstDisbursement.id },
);
expect(firstDisbursementUpdated.disbursementScheduleStatus).toBe(
DisbursementScheduleStatus.Sent,
);

// Assert pending disbursement was cancelled.
const secondDisbursementUpdated =
await disbursementScheduleRepo.findOneBy({ id: secondDisbursement.id });
expect(secondDisbursementUpdated.disbursementScheduleStatus).toBe(
DisbursementScheduleStatus.Cancelled,
);
});

it("Should cancel the assessment and log a warning when the workflowInstanceId is not present.", async () => {
// Arrange
const application = await saveFakeApplicationDisbursements(
appDataSource,
null,
{ applicationStatus: ApplicationStatus.Overwritten },
);
const studentAssessment = application.currentAssessment;
// Queued job.
const job = createMock<Job<CancelAssessmentQueueInDTO>>({
data: { assessmentId: studentAssessment.id },
});

// Act
const result = await processor.cancelAssessment(job);

// Assert
expect(zbClientMock.cancelProcessInstance).not.toHaveBeenCalled();
expect(result.warnings).toContain(
"Assessment was queued to be cancelled but there is no workflow id associated with.",
);
expect(result.summary).toContain("Assessment cancelled with success.");
});

it("Should throw an error and call job.discard when the application is not in the expected status.", async () => {
// Arrange
const errorMessage = `Application must be in the ${ApplicationStatus.Cancelled} or ${ApplicationStatus.Overwritten} state to have the assessment cancelled.`;
const expectedError = new Error(errorMessage);
const application = await saveFakeApplicationDisbursements(
appDataSource,
null,
{ applicationStatus: ApplicationStatus.Completed },
);
const studentAssessment = application.currentAssessment;
// Queued job.
const job = createMock<Job<CancelAssessmentQueueInDTO>>({
data: { assessmentId: studentAssessment.id },
});

// Act and Assert
await expect(processor.cancelAssessment(job)).rejects.toStrictEqual(
expectedError,
);
expect(job.discard).toBeCalled();
});

it("Should throw an error and call job.discard when the assessment id was not found.", async () => {
// Arrange
const assessmentId = 9999;
const errorMessage = `Assessment id ${assessmentId} was not found.`;
const error = new Error(errorMessage);
// Queued job.
const job = createMock<Job<CancelAssessmentQueueInDTO>>({
dheepak-aot marked this conversation as resolved.
Show resolved Hide resolved
data: { assessmentId },
});

// Act and Assert
await expect(processor.cancelAssessment(job)).rejects.toStrictEqual(
error,
);
expect(job.discard).toBeCalled();
});
},
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { ZBClient } from "zeebe-node";
import { Job } from "bull";
import { createMock } from "@golevelup/ts-jest";
import { INestApplication } from "@nestjs/common";
import { StartApplicationAssessmentProcessor } from "../start-application-assessment.processor";
import { StartAssessmentQueueInDTO } from "@sims/services/queue";
import { QueueNames } from "@sims/utilities";
import {
createTestingAppModule,
describeProcessorRootTest,
} from "../../../../test/helpers";

describe(
describeProcessorRootTest(QueueNames.StartApplicationAssessment),
() => {
let app: INestApplication;
let processor: StartApplicationAssessmentProcessor;
let zbClientMock: ZBClient;

beforeAll(async () => {
const { nestApplication, zbClient } = await createTestingAppModule();
app = nestApplication;
zbClientMock = zbClient;
// Processor under test.
processor = app.get(StartApplicationAssessmentProcessor);
});

beforeEach(() => {
jest.resetAllMocks();
});

it("Should throw an error when the workflow createProcessInstance method throws an error.", async () => {
// Arrange
const dummyException = new Error("Dummy error");
const job = createMock<Job<StartAssessmentQueueInDTO>>();
zbClientMock.createProcessInstance = jest.fn().mockImplementation(() => {
throw dummyException;
});

// Act and Assert.
await expect(processor.startAssessment(job)).rejects.toBe(dummyException);
ann-aot marked this conversation as resolved.
Show resolved Hide resolved
});

it("Should invoke the workflow create instance method with the received job parameters.", async () => {
// Arrange
const workflowName = "dummy workflow name";
const assessmentId = 999;
const variables = { assessmentId };
// Queued job.
const job = createMock<Job<StartAssessmentQueueInDTO>>({
data: {
workflowName,
assessmentId,
},
});

// Act
await processor.startAssessment(job);

// Assert
expect(zbClientMock.createProcessInstance).toHaveBeenCalledWith(
workflowName,
variables,
);
});
},
);
Loading