diff --git a/app/src/controllers/submission.ts b/app/src/controllers/submission.ts index 6f0eb99f..1dbb3a96 100644 --- a/app/src/controllers/submission.ts +++ b/app/src/controllers/submission.ts @@ -245,7 +245,7 @@ const controller = { activityId: newActivityId, trackingId: x.trackingId, status: x.status, - statusLastVerified: '2024-05-03T07:00:00.000Z' + statusLastVerified: x.statusLastVerified })); } @@ -254,7 +254,7 @@ const controller = { permitTypeId: x.permitTypeId, activityId: newActivityId, needed: PERMIT_NEEDED.UNDER_INVESTIGATION, - statusLastVerified: '2024-05-03T07:00:00.000Z' + statusLastVerified: x.statusLastVerified })); } diff --git a/app/tests/unit/controllers/permit.spec.ts b/app/tests/unit/controllers/permit.spec.ts index c1ed3e22..64ed549d 100644 --- a/app/tests/unit/controllers/permit.spec.ts +++ b/app/tests/unit/controllers/permit.spec.ts @@ -65,7 +65,8 @@ describe('createPermit', () => { needed: 'true', status: 'FOO', submittedDate: now.toISOString(), - adjudicationDate: now.toISOString() + adjudicationDate: now.toISOString(), + statusLastVerified: now.toISOString() }; createSpy.mockResolvedValue(created); @@ -144,7 +145,8 @@ describe('deletePermit', () => { needed: 'true', status: 'FOO', submittedDate: now.toISOString(), - adjudicationDate: now.toISOString() + adjudicationDate: now.toISOString(), + statusLastVerified: now.toISOString() }; deleteSpy.mockResolvedValue(deleted); @@ -261,7 +263,8 @@ describe('listPermits', () => { needed: 'true', status: 'FOO', submittedDate: now.toISOString(), - adjudicationDate: now.toISOString() + adjudicationDate: now.toISOString(), + statusLastVerified: now.toISOString() } ]; @@ -335,7 +338,8 @@ describe('updatePermit', () => { needed: 'true', status: 'FOO', submittedDate: now.toISOString(), - adjudicationDate: now.toISOString() + adjudicationDate: now.toISOString(), + statusLastVerified: now.toISOString() }; updateSpy.mockResolvedValue(updated); diff --git a/app/tests/unit/controllers/submission.spec.ts b/app/tests/unit/controllers/submission.spec.ts index cc8bafac..7bd5417c 100644 --- a/app/tests/unit/controllers/submission.spec.ts +++ b/app/tests/unit/controllers/submission.spec.ts @@ -1,9 +1,9 @@ import config from 'config'; import { NIL } from 'uuid'; -import { APPLICATION_STATUS_LIST } from '../../../src/components/constants'; +import { APPLICATION_STATUS_LIST, INTAKE_STATUS_LIST } from '../../../src/components/constants'; import submissionController from '../../../src/controllers/submission'; -import { permitService, submissionService, userService } from '../../../src/services'; +import { activityService, permitService, submissionService, userService } from '../../../src/services'; import * as utils from '../../../src/components/utils'; import type { Permit, Submission } from '../../../src/types'; @@ -55,10 +55,10 @@ const FORM_EXPORT_1 = { contactApplicantRelationship: 'Agent', financiallySupported: true, intakeStatus: 'IN PROGRESS', - isBCHousingSupported: true, - isIndigenousHousingProviderSupported: true, - isNonProfitSupported: true, - isHousingCooperativeSupported: true, + isBCHousingSupported: 'Yes', + isIndigenousHousingProviderSupported: 'Yes', + isNonProfitSupported: 'Yes', + isHousingCooperativeSupported: 'Yes', parcelID: '132', latitude: -48, longitude: 160, @@ -104,10 +104,10 @@ const FORM_EXPORT_2 = { financiallySupported: true, intakeStatus: 'IN PROGRESS', - isBCHousingSupported: true, - isIndigenousHousingProviderSupported: true, - isNonProfitSupported: true, - isHousingCooperativeSupported: true, + isBCHousingSupported: 'Yes', + isIndigenousHousingProviderSupported: 'Yes', + isNonProfitSupported: 'Yes', + isHousingCooperativeSupported: 'Yes', parcelID: '132', latitude: -59, longitude: 178, @@ -147,10 +147,10 @@ const FORM_SUBMISSION_1: Partial500', - isRentalUnit: 'Yes', + hasRentalUnits: 'Yes', streetAddress: '112 Other Road', submittedAt: FORM_EXPORT_2.form.createdAt, submittedBy: 'USERABC', @@ -248,10 +248,10 @@ const SUBMISSION_1 = { bcOnlineCompleted: true, naturalDisaster: false, financiallySupported: true, - financiallySupportedBC: true, - financiallySupportedIndigenous: false, - financiallySupportedNonProfit: false, - financiallySupportedHousingCoop: false, + financiallySupportedBC: 'Yes', + financiallySupportedIndigenous: 'Yes', + financiallySupportedNonProfit: 'Yes', + financiallySupportedHousingCoop: 'Yes', aaiUpdated: true, waitingOn: null, bringForwardDate: null, @@ -273,8 +273,6 @@ describe('checkAndStoreNewSubmissions', () => { const formExportSpy = jest.spyOn(submissionService, 'getFormExport'); const searchSubmissionsSpy = jest.spyOn(submissionService, 'searchSubmissions'); const createSubmissionsFromExportSpy = jest.spyOn(submissionService, 'createSubmissionsFromExport'); - const createSubmissionSpy = jest.spyOn(submissionService, 'createSubmission'); - const getSubmissionSpy = jest.spyOn(submissionService, 'getSubmission'); it('creates submissions', async () => { (config.get as jest.Mock).mockReturnValueOnce({ @@ -317,7 +315,8 @@ describe('checkAndStoreNewSubmissions', () => { permitTypesSpy.mockResolvedValue(PERMIT_TYPES); formExportSpy.mockResolvedValueOnce([FORM_EXPORT_1, FORM_EXPORT_2]).mockResolvedValueOnce([]); - searchSubmissionsSpy.mockResolvedValue([SUBMISSION_1]); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + searchSubmissionsSpy.mockResolvedValue([SUBMISSION_1 as any]); createSubmissionsFromExportSpy.mockResolvedValue(); createPermitSpy.mockResolvedValue(null); @@ -344,23 +343,6 @@ describe('checkAndStoreNewSubmissions', () => { expect(createPermitSpy).toHaveBeenCalledTimes(1); }); - it.skip('creates submission with no UUID collision', async () => { - const req = { - body: SUBMISSION_1, - currentUser: CURRENT_USER - }; - const next = jest.fn(); - - createSubmissionSpy.mockResolvedValue({ activityId: '00000000' } as Submission); - getSubmissionSpy.mockResolvedValue(null); - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - await submissionController.createSubmission(req as any, res as any, next); - - expect(createSubmissionSpy).toHaveBeenCalledTimes(1); - expect(getSubmissionSpy).toHaveBeenCalledTimes(1); - }); - it('creates permits', async () => { (config.get as jest.Mock).mockReturnValueOnce({ form1: { @@ -394,6 +376,189 @@ describe('checkAndStoreNewSubmissions', () => { }); }); +describe('createSubmission', () => { + // Mock service calls + const createPermitSpy = jest.spyOn(permitService, 'createPermit'); + const createSubmissionSpy = jest.spyOn(submissionService, 'createSubmission'); + const getActivitySpy = jest.spyOn(activityService, 'getActivity'); + + it('should return 201 and new activity ID if all good', async () => { + const req = { + body: {}, + currentUser: CURRENT_USER + }; + const next = jest.fn(); + + getActivitySpy.mockResolvedValue(null); + createSubmissionSpy.mockResolvedValue({ activityId: '00000000' } as Submission); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + await submissionController.createSubmission(req as any, res as any, next); + + expect(getActivitySpy).toHaveBeenCalledTimes(1); + expect(createSubmissionSpy).toHaveBeenCalledTimes(1); + expect(res.status).toHaveBeenCalledWith(201); + expect(res.json).toHaveBeenCalledWith({ activityId: '00000000' }); + }); + + it('creates submission with unique activity ID', async () => { + const req = { + body: SUBMISSION_1, + currentUser: CURRENT_USER + }; + const next = jest.fn(); + + getActivitySpy.mockResolvedValue(null); + createSubmissionSpy.mockResolvedValue({ activityId: '00000000' } as Submission); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + await submissionController.createSubmission(req as any, res as any, next); + + expect(getActivitySpy).toHaveBeenCalledTimes(1); + expect(createSubmissionSpy).toHaveBeenCalledTimes(1); + }); + + it('attemps to create unique activity ID again on conflict', async () => { + const req = { + body: SUBMISSION_1, + currentUser: CURRENT_USER + }; + const next = jest.fn(); + + getActivitySpy.mockResolvedValueOnce({ activityId: '00000000', initiativeId: '123' }).mockResolvedValueOnce(null); + createSubmissionSpy.mockResolvedValue({ activityId: '00000000' } as Submission); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + await submissionController.createSubmission(req as any, res as any, next); + + expect(getActivitySpy).toHaveBeenCalledTimes(2); + expect(createSubmissionSpy).toHaveBeenCalledTimes(1); + }); + + it('populates data from body if it exists', async () => { + const isoPattern = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/; + + const req = { + body: { + applicant: { + firstName: 'Test', + lastName: 'User' + }, + basic: { + isDevelopedByCompanyOrOrg: true + }, + housing: { + projectName: 'TheProject' + }, + location: { + projectLocation: 'Some place' + }, + permits: { + hasAppliedProvincialPermits: true + } + }, + currentUser: CURRENT_USER + }; + const next = jest.fn(); + + getActivitySpy.mockResolvedValue(null); + createSubmissionSpy.mockResolvedValue({ activityId: '00000000' } as Submission); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + await submissionController.createSubmission(req as any, res as any, next); + + expect(getActivitySpy).toHaveBeenCalledTimes(1); + expect(createSubmissionSpy).toHaveBeenCalledTimes(1); + expect(createSubmissionSpy).toHaveBeenCalledWith( + expect.objectContaining({ + contactName: `${req.body.applicant.firstName} ${req.body.applicant.lastName}`, + isDevelopedByCompanyOrOrg: true, + projectName: 'TheProject', + projectLocation: 'Some place', + hasAppliedProvincialPermits: true, + submissionId: expect.any(String), + activityId: expect.any(String), + submittedAt: expect.stringMatching(isoPattern), + intakeStatus: INTAKE_STATUS_LIST.SUBMITTED, + applicationStatus: APPLICATION_STATUS_LIST.NEW + }) + ); + }); + + it('creates permits if they exist', async () => { + const now = new Date().toISOString(); + + const req = { + body: { + appliedPermits: [ + { + permitTypeId: 1, + trackingId: '123', + status: 'New', + statusLastVerified: now + }, + { + permitTypeId: 3, + trackingId: '456', + status: 'New', + statusLastVerified: now + } + ], + investigatePermits: [ + { + permitTypeId: 12, + needed: 'Under investigation', + statusLastVerified: now + } + ] + }, + currentUser: CURRENT_USER + }; + const next = jest.fn(); + + getActivitySpy.mockResolvedValue(null); + createSubmissionSpy.mockResolvedValue({ activityId: '00000000' } as Submission); + createPermitSpy.mockResolvedValue(null); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + await submissionController.createSubmission(req as any, res as any, next); + + expect(getActivitySpy).toHaveBeenCalledTimes(1); + expect(createSubmissionSpy).toHaveBeenCalledTimes(1); + + expect(createPermitSpy).toHaveBeenCalledTimes(3); + expect(createPermitSpy).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + permitTypeId: 1, + activityId: expect.any(String), + trackingId: '123', + status: 'New', + statusLastVerified: now + }) + ); + expect(createPermitSpy).toHaveBeenNthCalledWith( + 2, + expect.objectContaining({ + permitTypeId: 3, + activityId: expect.any(String), + trackingId: '456', + status: 'New', + statusLastVerified: now + }) + ); + expect(createPermitSpy).toHaveBeenNthCalledWith( + 3, + expect.objectContaining({ + permitTypeId: 12, + activityId: expect.any(String), + needed: 'Under investigation', + statusLastVerified: now + }) + ); + }); +}); + describe('getStatistics', () => { const next = jest.fn(); @@ -468,7 +633,8 @@ describe('getSubmission', () => { currentUser: CURRENT_USER }; - submissionSpy.mockResolvedValue(SUBMISSION_1); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + submissionSpy.mockResolvedValue(SUBMISSION_1 as any); // eslint-disable-next-line @typescript-eslint/no-explicit-any await submissionController.getSubmission(req as any, res as any, next); @@ -514,7 +680,8 @@ describe('getSubmissions', () => { }; checkAndStoreSpy.mockResolvedValue(); - submissionsSpy.mockResolvedValue([SUBMISSION_1]); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + submissionsSpy.mockResolvedValue([SUBMISSION_1 as any]); // eslint-disable-next-line @typescript-eslint/no-explicit-any await submissionController.getSubmissions(req as any, res as any, next); @@ -532,7 +699,8 @@ describe('getSubmissions', () => { }; checkAndStoreSpy.mockResolvedValue(); - submissionsSpy.mockResolvedValue([SUBMISSION_1]); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + submissionsSpy.mockResolvedValue([SUBMISSION_1 as any]); // eslint-disable-next-line @typescript-eslint/no-explicit-any await submissionController.getSubmissions(req as any, res as any, next); @@ -596,7 +764,8 @@ describe('updateSubmission', () => { const USR_IDENTITY = 'xxxy'; const USR_ID = 'abc-123'; - const updated = SUBMISSION_1; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const updated: any = SUBMISSION_1; updateSpy.mockResolvedValue(updated); getCurrentIdentitySpy.mockReturnValue(USR_IDENTITY); @@ -633,7 +802,8 @@ describe('updateSubmission', () => { await submissionController.updateSubmission(req as any, res as any, next); expect(updateSpy).toHaveBeenCalledTimes(1); - expect(updateSpy).toHaveBeenCalledWith({ ...(req.body as Submission), updatedBy: USR_ID }); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + expect(updateSpy).toHaveBeenCalledWith({ ...(req.body as any), updatedBy: USR_ID }); expect(res.status).toHaveBeenCalledTimes(0); expect(next).toHaveBeenCalledTimes(1); }); diff --git a/frontend/src/services/submissionService.ts b/frontend/src/services/submissionService.ts index 3a948bb3..fed764c8 100644 --- a/frontend/src/services/submissionService.ts +++ b/frontend/src/services/submissionService.ts @@ -14,7 +14,7 @@ export default { * @returns {Promise} An axios response */ getSubmissions() { - return appAxios().get('submission/'); + return appAxios().get('submission'); }, /** diff --git a/frontend/tests/unit/service/submissionService.spec.ts b/frontend/tests/unit/service/submissionService.spec.ts index f499cac0..b8da929d 100644 --- a/frontend/tests/unit/service/submissionService.spec.ts +++ b/frontend/tests/unit/service/submissionService.spec.ts @@ -7,7 +7,7 @@ vi.mock('vue-router', () => ({ }) })); -const PATH = 'submission/'; +const PATH = 'submission'; const getSpy = vi.fn(); const putSpy = vi.fn(); @@ -21,45 +21,69 @@ beforeEach(() => { vi.clearAllMocks(); }); -describe('submissionService test', () => { - it('gets submissions', async () => { - await submissionService.getSubmissions(); +describe('submissionService', () => { + describe('createSubmission', async () => { + it('calls correct endpoint', async () => { + await submissionService.createSubmission(); - expect(getSpy).toHaveBeenCalledTimes(1); - expect(getSpy).toHaveBeenCalledWith(PATH); + expect(putSpy).toHaveBeenCalledTimes(1); + expect(putSpy).toHaveBeenCalledWith(PATH, undefined); + }); + + it('passes parameters', async () => { + await submissionService.createSubmission({ foo: 'bar' }); + + expect(putSpy).toHaveBeenCalledTimes(1); + expect(putSpy).toHaveBeenCalledWith(PATH, { foo: 'bar' }); + }); }); - it('gets statistics', async () => { - const testFilter = { - dateFrom: '02/02/2022', - dateTo: '03/03/2022', - monthYear: '04/2022', - userId: 'testUserId' - }; - await submissionService.getStatistics(testFilter); - - expect(getSpy).toHaveBeenCalledTimes(1); - expect(getSpy).toHaveBeenCalledWith(`${PATH}statistics`, { params: testFilter }); + describe('getSubmissions', async () => { + it('calls correct endpoint', async () => { + await submissionService.getSubmissions(); + + expect(getSpy).toHaveBeenCalledTimes(1); + expect(getSpy).toHaveBeenCalledWith(PATH); + }); }); - it('gets a single submission', async () => { - const testActivityId = 'testActivityId'; - await submissionService.getSubmission(testActivityId); + describe('getStatistics', async () => { + it('calls correct endpoint', async () => { + const testFilter = { + dateFrom: '02/02/2022', + dateTo: '03/03/2022', + monthYear: '04/2022', + userId: 'testUserId' + }; + await submissionService.getStatistics(testFilter); - expect(getSpy).toHaveBeenCalledTimes(1); - expect(getSpy).toHaveBeenCalledWith(`${PATH}${testActivityId}`); + expect(getSpy).toHaveBeenCalledTimes(1); + expect(getSpy).toHaveBeenCalledWith(`${PATH}/statistics`, { params: testFilter }); + }); }); - it('updates a submission', async () => { - const testActivityId = 'testActivityId'; - const testObj = { - field1: 'testField1', - date1: new Date().toISOString(), - field2: 'testField2' - }; - await submissionService.updateSubmission(testActivityId, testObj); - - expect(putSpy).toHaveBeenCalledTimes(1); - expect(putSpy).toHaveBeenCalledWith(`${PATH}${testActivityId}`, testObj); + describe('getSubmission', async () => { + it('calls correct endpoint', async () => { + const testActivityId = 'testActivityId'; + await submissionService.getSubmission(testActivityId); + + expect(getSpy).toHaveBeenCalledTimes(1); + expect(getSpy).toHaveBeenCalledWith(`${PATH}/${testActivityId}`); + }); + }); + + describe('updateSubmission', async () => { + it('calls correct endpoint', async () => { + const testActivityId = 'testActivityId'; + const testObj = { + field1: 'testField1', + date1: new Date().toISOString(), + field2: 'testField2' + }; + await submissionService.updateSubmission(testActivityId, testObj); + + expect(putSpy).toHaveBeenCalledTimes(1); + expect(putSpy).toHaveBeenCalledWith(`${PATH}/${testActivityId}`, testObj); + }); }); });