diff --git a/src/functions/updateTechRecordStatus.ts b/src/functions/updateTechRecordStatus.ts index e3250a95..0ad6cedd 100644 --- a/src/functions/updateTechRecordStatus.ts +++ b/src/functions/updateTechRecordStatus.ts @@ -1,27 +1,31 @@ import TechRecordsDAO from "../models/TechRecordsDAO"; import TechRecordsService from "../services/TechRecordsService"; import HTTPResponse from "../models/HTTPResponse"; -import {STATUS, HTTPRESPONSE} from "../assets/Enums"; -import {TechRecordStatusHandler} from "../handlers/TechRecordStatusHandler"; +import { STATUS, HTTPRESPONSE } from "../assets/Enums"; +import { TechRecordStatusHandler } from "../handlers/TechRecordStatusHandler"; export async function updateTechRecordStatus(event: any) { - const techRecordsService = new TechRecordsService(new TechRecordsDAO()); + const techRecordsService = new TechRecordsService(new TechRecordsDAO()); - const systemNumber: string = event.pathParameters.systemNumber; - const testStatus: string = event.queryStringParameters!.testStatus; - const testResult: string = event.queryStringParameters!.testResult; - const testTypeId: string = event.queryStringParameters!.testTypeId; - const newStatus = event.queryStringParameters ? STATUS[event.queryStringParameters.newStatus as keyof typeof STATUS] : undefined; - const createdById: string = event.queryStringParameters!.createdById; - const createdByName: string = event.queryStringParameters!.createdByName; + const systemNumber: string = event.pathParameters.systemNumber; + const testStatus: string = event.queryStringParameters!.testStatus; + const testResult: string = event.queryStringParameters!.testResult; + const testTypeId: string = event.queryStringParameters!.testTypeId; + const newStatus = event.queryStringParameters + ? STATUS[event.queryStringParameters.newStatus as keyof typeof STATUS] + : undefined; + const createdById: string = event.queryStringParameters!.createdById; + const createdByName: string = event.queryStringParameters!.createdByName; - if (!TechRecordStatusHandler.isStatusUpdateRequired(testStatus, testResult, testTypeId)) { - return new HTTPResponse(200, HTTPRESPONSE.NO_STATUS_UPDATE_REQUIRED); - } - try { - const updatedTechRec = await techRecordsService.updateTechRecordStatusCode(systemNumber, newStatus, createdById, createdByName); - return new HTTPResponse(200, updatedTechRec); - } catch (error) { - return new HTTPResponse(error.statusCode, error.body); - } + try { + const updatedTechRec = await techRecordsService.updateTechRecordStatusCode( + systemNumber, + newStatus, + createdById, + createdByName + ); + return new HTTPResponse(200, updatedTechRec); + } catch (error) { + return new HTTPResponse(error.statusCode, error.body); + } } diff --git a/src/handlers/TechRecordStatusHandler.ts b/src/handlers/TechRecordStatusHandler.ts index 38d17e39..d349a75e 100644 --- a/src/handlers/TechRecordStatusHandler.ts +++ b/src/handlers/TechRecordStatusHandler.ts @@ -5,7 +5,6 @@ import * as handlers from "./index"; import { Vehicle } from "../../@Types/TechRecords"; import { VehicleProcessor } from "../domain/Processors"; - export class TechRecordStatusHandler { private techRecordsListHandler: handlers.TechRecordsListHandler; private readonly auditHandler: handlers.AuditDetailsHandler; @@ -15,11 +14,31 @@ export class TechRecordStatusHandler { this.auditHandler = new handlers.AuditDetailsHandler(); } - public async prepareTechRecordForStatusUpdate(systemNumber: string, newStatus: STATUS = STATUS.CURRENT, createdById: string, createdByName: string): Promise { - const techRecordWrapper = await this.techRecordsListHandler.getFormattedTechRecordsList(systemNumber, STATUS.ALL, SEARCHCRITERIA.SYSTEM_NUMBER, false); - const uniqueRecord = VehicleProcessor.getTechRecordToUpdate(techRecordWrapper, (techRecord) => techRecord.statusCode === STATUS.PROVISIONAL || techRecord.statusCode === STATUS.CURRENT) as T; - const provisionalTechRecords = uniqueRecord.techRecord.filter((techRecord) => techRecord.statusCode === STATUS.PROVISIONAL); - const currentTechRecords = uniqueRecord.techRecord.filter((techRecord) => techRecord.statusCode === STATUS.CURRENT); + public async prepareTechRecordForStatusUpdate( + systemNumber: string, + newStatus: STATUS = STATUS.CURRENT, + createdById: string, + createdByName: string + ): Promise { + const techRecordWrapper = + await this.techRecordsListHandler.getFormattedTechRecordsList( + systemNumber, + STATUS.ALL, + SEARCHCRITERIA.SYSTEM_NUMBER, + false + ); + const uniqueRecord = VehicleProcessor.getTechRecordToUpdate( + techRecordWrapper, + (techRecord) => + techRecord.statusCode === STATUS.PROVISIONAL || + techRecord.statusCode === STATUS.CURRENT + ) as T; + const provisionalTechRecords = uniqueRecord.techRecord.filter( + (techRecord) => techRecord.statusCode === STATUS.PROVISIONAL + ); + const currentTechRecords = uniqueRecord.techRecord.filter( + (techRecord) => techRecord.statusCode === STATUS.CURRENT + ); let newTechRecord; if (provisionalTechRecords.length === 1) { provisionalTechRecords[0].statusCode = STATUS.ARCHIVED; @@ -27,8 +46,18 @@ export class TechRecordStatusHandler { newTechRecord.statusCode = newStatus; const date = new Date().toISOString(); - this.auditHandler.setCreatedAuditDetails(newTechRecord, createdByName, createdById, date); - this.auditHandler.setLastUpdatedAuditDetails(provisionalTechRecords[0], createdByName, createdById, date); + this.auditHandler.setCreatedAuditDetails( + newTechRecord, + createdByName, + createdById, + date + ); + this.auditHandler.setLastUpdatedAuditDetails( + provisionalTechRecords[0], + createdByName, + createdById, + date + ); provisionalTechRecords[0].updateType = UPDATE_TYPE.TECH_RECORD_UPDATE; } if (currentTechRecords.length === 1) { @@ -37,37 +66,30 @@ export class TechRecordStatusHandler { if (!newTechRecord) { newTechRecord = cloneDeep(currentTechRecords[0]); newTechRecord.statusCode = newStatus; - this.auditHandler.setCreatedAuditDetails(newTechRecord, createdByName, createdById, date); + this.auditHandler.setCreatedAuditDetails( + newTechRecord, + createdByName, + createdById, + date + ); } - this.auditHandler.setLastUpdatedAuditDetails(currentTechRecords[0], createdByName, createdById, date); + this.auditHandler.setLastUpdatedAuditDetails( + currentTechRecords[0], + createdByName, + createdById, + date + ); currentTechRecords[0].updateType = UPDATE_TYPE.TECH_RECORD_UPDATE; } // if newTechRecord is undefined that means there multiple or no current/provisional records were found if (!newTechRecord) { - throw new HTTPError(400, "The tech record status cannot be updated to " + newStatus); + throw new HTTPError( + 400, + "The tech record status cannot be updated to " + newStatus + ); } uniqueRecord.techRecord.push(newTechRecord); return uniqueRecord; } - - public static isStatusUpdateRequired(testStatus: string, testResult: string, testTypeId: string): boolean { - return testStatus === "submitted" && (testResult === "pass" || testResult === "prs") && - (this.isTestTypeFirstTest(testTypeId) || this.isTestTypeNotifiableAlteration(testTypeId) || this.isTestTypeCOIF(testTypeId)); - } - - private static isTestTypeFirstTest(testTypeId: string): boolean { - const firstTestIds = ["41", "95", "65", "66", "67", "103", "104", "82", "83", "119", "120", "186", "188", "192", "194"]; - return firstTestIds.includes(testTypeId); - } - - private static isTestTypeNotifiableAlteration(testTypeId: string): boolean { - const notifiableAlterationIds = ["38", "47", "48"]; - return notifiableAlterationIds.includes(testTypeId); - } - - private static isTestTypeCOIF(testTypeId: string): boolean { - const coifIds = ["142", "143", "175", "176"]; - return coifIds.includes(testTypeId); - } } diff --git a/tests/integration/updateTechRecordStatus.intTest.ts b/tests/integration/updateTechRecordStatus.intTest.ts index b082faee..03bf0846 100644 --- a/tests/integration/updateTechRecordStatus.intTest.ts +++ b/tests/integration/updateTechRecordStatus.intTest.ts @@ -23,29 +23,6 @@ describe("UpdateTechRecordStatus", () => { }); context("when trying to update a vehicle", () => { - it("should not update the status if the test type does not require a tech record update", async () => { - const systemNumber: string = "11000001"; - expect.assertions(2); - await LambdaTester(updateTechRecordStatus) - .event({ - path: "/vehicles/update-status/" + systemNumber, - pathParameters: { - systemNumber, - }, - queryStringParameters: { - testStatus: "submitted", - testResult: "pass", - testTypeId: "1", - }, - httpMethod: "PUT", - resource: "/vehicles/update-status/{systemNumber}" - }) - .expectResolve((result: any) => { - expect(result.statusCode).toBe(200); - expect(JSON.parse(result.body)).toBe(HTTPRESPONSE.NO_STATUS_UPDATE_REQUIRED); - }); - }); - it("should return 200 and the updated vehicle if it has a provisional techRecord", async () => { const systemNumber: string = "11000027"; expect.assertions(4); diff --git a/tests/unit/handler.unitTest.ts b/tests/unit/handler.unitTest.ts index 16204507..9336be42 100644 --- a/tests/unit/handler.unitTest.ts +++ b/tests/unit/handler.unitTest.ts @@ -1,12 +1,12 @@ -import {handler} from "../../src/handler"; +import { handler } from "../../src/handler"; import Configuration from "../../src/utils/Configuration"; import HTTPResponse from "../../src/models/HTTPResponse"; import event from "../resources/event.json"; import TechRecordsService from "../../src/services/TechRecordsService"; import mockData from "../resources/technical-records.json"; -import {cloneDeep} from "lodash"; -import {Context} from "aws-lambda"; -import {TechRecordStatusHandler} from "../../src/handlers/TechRecordStatusHandler"; +import { cloneDeep } from "lodash"; +import { Context } from "aws-lambda"; +import { TechRecordStatusHandler } from "../../src/handlers/TechRecordStatusHandler"; jest.mock("../../src/services/TechRecordsService"); @@ -22,41 +22,48 @@ describe("The lambda function handler", () => { pathParameters: null, resource: "/vehicles", httpMethod: "GET", - queryStringParameters: null + queryStringParameters: null, }; // Stub out the actual functions - TechRecordsService.prototype.getTechRecordsList = jest.fn().mockImplementation(() => { - return Promise.resolve(new HTTPResponse(200, {})); - }); + TechRecordsService.prototype.getTechRecordsList = jest + .fn() + .mockImplementation(() => { + return Promise.resolve(new HTTPResponse(200, {})); + }); const result = await handler(vehicleRecordEvent, ctx); expect(result.statusCode).toEqual(200); - expect(TechRecordsService.prototype.getTechRecordsList).toHaveBeenCalled(); + expect( + TechRecordsService.prototype.getTechRecordsList + ).toHaveBeenCalled(); }); - it("should call the /vehicles/{searchIdentifier}/tech-records function with correct event payload", async () => { // Specify your event, with correct path, payload etc const vehicleRecordEvent = { path: "/vehicles/12345678/tech-records", pathParameters: { - searchIdentifier: "12345678" + searchIdentifier: "12345678", }, resource: "/vehicles/{searchIdentifier}/tech-records", httpMethod: "GET", queryStringParameters: { - status: "all" - } + status: "all", + }, }; // Stub out the actual functions - TechRecordsService.prototype.getTechRecordsList = jest.fn().mockImplementation(() => { - return Promise.resolve(new HTTPResponse(200, {})); - }); + TechRecordsService.prototype.getTechRecordsList = jest + .fn() + .mockImplementation(() => { + return Promise.resolve(new HTTPResponse(200, {})); + }); const result = await handler(vehicleRecordEvent, ctx); expect(result.statusCode).toEqual(200); - expect(TechRecordsService.prototype.getTechRecordsList).toHaveBeenCalled(); + expect( + TechRecordsService.prototype.getTechRecordsList + ).toHaveBeenCalled(); }); it("should call /vehicles function with correct event payload", async () => { @@ -64,10 +71,10 @@ describe("The lambda function handler", () => { const payload = { msUserDetails: { msUser: "user", - msOid: "12345" + msOid: "12345", }, vin: "12345678910", - techRecord: cloneDeep(mockData[26]).techRecord + techRecord: cloneDeep(mockData[26]).techRecord, }; const vehicleRecordEvent = { path: "/vehicles", @@ -75,16 +82,20 @@ describe("The lambda function handler", () => { resource: "/vehicles/{searchIdentifier}/tech-records", httpMethod: "POST", body: JSON.stringify(payload), - queryStringParameters: null + queryStringParameters: null, }; // Stub out the actual functions - TechRecordsService.prototype.insertTechRecord = jest.fn().mockImplementation(() => { - return Promise.resolve(new HTTPResponse(201, {})); - }); + TechRecordsService.prototype.insertTechRecord = jest + .fn() + .mockImplementation(() => { + return Promise.resolve(new HTTPResponse(201, {})); + }); const result = await handler(vehicleRecordEvent, ctx); expect(result.statusCode).toEqual(201); - expect(TechRecordsService.prototype.insertTechRecord).toHaveBeenCalled(); + expect( + TechRecordsService.prototype.insertTechRecord + ).toHaveBeenCalled(); }); it("should call /vehicles/{vin} function with correct event payload", async () => { @@ -92,36 +103,40 @@ describe("The lambda function handler", () => { const payload = { msUserDetails: { msUser: "dorel", - msOid: "12314234" + msOid: "12314234", }, systemNumber: mockData[26].systemNumber, - techRecord: mockData[26].techRecord + techRecord: mockData[26].techRecord, }; const vehicleRecordEvent = { path: "/vehicles/XMGDE02FS0H999987", pathParameters: { - vin: "XMGDE02FS0H999987" + vin: "XMGDE02FS0H999987", }, resource: "/vehicles/{vin}", httpMethod: "PUT", body: JSON.stringify(payload), - queryStringParameters: null + queryStringParameters: null, }; // Stub out the actual functions - TechRecordsService.prototype.updateTechRecord = jest.fn().mockImplementation(() => { - return Promise.resolve(new HTTPResponse(200, {})); - }); + TechRecordsService.prototype.updateTechRecord = jest + .fn() + .mockImplementation(() => { + return Promise.resolve(new HTTPResponse(200, {})); + }); const result = await handler(vehicleRecordEvent, ctx); expect(result.statusCode).toEqual(200); - expect(TechRecordsService.prototype.updateTechRecord).toHaveBeenCalled(); + expect( + TechRecordsService.prototype.updateTechRecord + ).toHaveBeenCalled(); }); it("should call the update-status function with correct event payload", async () => { const vehicleRecordEvent = { path: "/vehicles/update-status/XMGDE02FS0H999987", pathParameters: { - vin: "XMGDE02FS0H999987" + vin: "XMGDE02FS0H999987", }, resource: "/vehicles/update-status/{vin}", httpMethod: "PUT", @@ -131,36 +146,42 @@ describe("The lambda function handler", () => { testTypeId: "41", }, }; - TechRecordStatusHandler.isStatusUpdateRequired = jest.fn().mockReturnValue(true); - TechRecordsService.prototype.updateTechRecordStatusCode = jest.fn().mockImplementation(() => { - return Promise.resolve(new HTTPResponse(200, {})); - }); + TechRecordsService.prototype.updateTechRecordStatusCode = jest + .fn() + .mockImplementation(() => { + return Promise.resolve(new HTTPResponse(200, {})); + }); const result = await handler(vehicleRecordEvent, ctx); expect(result.statusCode).toEqual(200); - expect(TechRecordsService.prototype.updateTechRecordStatusCode).toHaveBeenCalled(); + expect( + TechRecordsService.prototype.updateTechRecordStatusCode + ).toHaveBeenCalled(); }); it("should call the update-eu-vehicle-category function with correct event payload", async () => { const vehicleRecordEvent = { path: "/vehicles/update-eu-vehicle-category/10000027", pathParameters: { - systemNumber: "10000027" + systemNumber: "10000027", }, resource: "/vehicles/update-eu-vehicle-category/{systemNumber}", httpMethod: "PUT", queryStringParameters: { - euVehicleCategory: "m1" - } + euVehicleCategory: "m1", + }, }; const updateEuVehicleMock = jest.fn().mockImplementation(() => { return Promise.resolve(new HTTPResponse(200, {})); }); - TechRecordsService.prototype.updateEuVehicleCategory = updateEuVehicleMock; + TechRecordsService.prototype.updateEuVehicleCategory = + updateEuVehicleMock; const result = await handler(vehicleRecordEvent, ctx); expect.assertions(4); expect(result.statusCode).toEqual(200); - expect(TechRecordsService.prototype.updateEuVehicleCategory).toHaveBeenCalled(); + expect( + TechRecordsService.prototype.updateEuVehicleCategory + ).toHaveBeenCalled(); expect(updateEuVehicleMock.mock.calls[0][0]).toEqual("10000027"); expect(updateEuVehicleMock.mock.calls[0][1]).toEqual("m1"); }); @@ -169,26 +190,27 @@ describe("The lambda function handler", () => { const payload = { msUserDetails: { msUser: "dorel", - msOid: "12314234" + msOid: "12314234", }, vin: "XMGDE02FS0H012345", systemNumber: mockData[26].systemNumber, - techRecord: mockData[26].techRecord + techRecord: mockData[26].techRecord, }; const vehicleRecordEvent = { path: "/vehicles/add-provisional/10000027", pathParameters: { - systemNumber: "10000027" + systemNumber: "10000027", }, resource: "/vehicles/add-provisional/{systemNumber}", httpMethod: "POST", - body: JSON.stringify(payload) + body: JSON.stringify(payload), }; const addProvisionalMock = jest.fn().mockImplementation(() => { return Promise.resolve(new HTTPResponse(200, {})); }); - TechRecordsService.prototype.addProvisionalTechRecord = addProvisionalMock; + TechRecordsService.prototype.addProvisionalTechRecord = + addProvisionalMock; const result = await handler(vehicleRecordEvent, ctx); expect.assertions(2); expect(result.statusCode).toEqual(200); @@ -202,28 +224,29 @@ describe("The lambda function handler", () => { reasonForArchiving: "Archive please", msUserDetails: { msUser: "dorel", - msOid: "12314234" + msOid: "12314234", }, vin: "XMGDE02FS0H012345", systemNumber: mockData[26].systemNumber, - techRecord: mockData[26].techRecord + techRecord: mockData[26].techRecord, }; const vehicleRecordEvent = { path: "/vehicles/archive/10000027", pathParameters: { - systemNumber: "10000027" + systemNumber: "10000027", }, resource: "/vehicles/archive/{systemNumber}", httpMethod: "PUT", - body: JSON.stringify(payload) + body: JSON.stringify(payload), }; const archiveTechRecordStatusMock = jest.fn().mockImplementation(() => { return Promise.resolve(new HTTPResponse(200, {})); }); - TechRecordsService.prototype.archiveTechRecordStatus = archiveTechRecordStatusMock; + TechRecordsService.prototype.archiveTechRecordStatus = + archiveTechRecordStatusMock; const result = await handler(vehicleRecordEvent, ctx); - console.log("error on handler",result); + console.log("error on handler", result); expect.assertions(2); expect(result.statusCode).toEqual(200); expect( @@ -236,7 +259,9 @@ describe("The lambda function handler", () => { expect(result).toBeInstanceOf(HTTPResponse); expect(result.statusCode).toEqual(400); - expect(result.body).toEqual(JSON.stringify("AWS event is empty. Check your test event.")); + expect(result.body).toEqual( + JSON.stringify("AWS event is empty. Check your test event.") + ); }); it("should return error on invalid body json", async () => { @@ -245,7 +270,9 @@ describe("The lambda function handler", () => { const result = await handler(invalidBodyEvent, ctx); expect(result).toBeInstanceOf(HTTPResponse); expect(result.statusCode).toEqual(400); - expect(result.body).toEqual(JSON.stringify("Body is not a valid JSON.")); + expect(result.body).toEqual( + JSON.stringify("Body is not a valid JSON.") + ); }); it("should return a Route Not Found error on invalid path", async () => { @@ -253,20 +280,29 @@ describe("The lambda function handler", () => { invalidPathEvent.path = "/vehicles/123/doesntExist"; const result = await handler(invalidPathEvent, ctx); expect(result.statusCode).toEqual(400); - expect(result.body).toStrictEqual(JSON.stringify({error: `Route ${invalidPathEvent.httpMethod} ${invalidPathEvent.path} was not found.`})); + expect(result.body).toStrictEqual( + JSON.stringify({ + error: `Route ${invalidPathEvent.httpMethod} ${invalidPathEvent.path} was not found.`, + }) + ); }); }); }); context("With no routes defined in config", () => { it("should return a Route Not Found error", async () => { - const getFunctions = Configuration.prototype.getFunctions; - Configuration.prototype.getFunctions = jest.fn().mockImplementation(() => []); - const eventNoRoute = {httpMethod: "GET", path: ""}; + Configuration.prototype.getFunctions = jest + .fn() + .mockImplementation(() => []); + const eventNoRoute = { httpMethod: "GET", path: "" }; const result = await handler(eventNoRoute, ctx); expect(result.statusCode).toEqual(400); - expect(result.body).toEqual(JSON.stringify({error: `Route ${eventNoRoute.httpMethod} ${eventNoRoute.path} was not found.`})); + expect(result.body).toEqual( + JSON.stringify({ + error: `Route ${eventNoRoute.httpMethod} ${eventNoRoute.path} was not found.`, + }) + ); Configuration.prototype.getFunctions = getFunctions; }); }); @@ -288,7 +324,6 @@ describe("The configuration service", () => { expect(functions[6].name).toEqual("archiveTechRecordStatus"); expect(functions[7].name).toEqual("updateVin"); - const DBConfig = configService.getDynamoDBConfig(); expect(DBConfig).toEqual(configService.getConfig().dynamodb.local); @@ -310,7 +345,9 @@ describe("The configuration service", () => { expect(functions[7].name).toEqual("updateVin"); const DBConfig = configService.getDynamoDBConfig(); - expect(DBConfig).toEqual(configService.getConfig().dynamodb["local-global"]); + expect(DBConfig).toEqual( + configService.getConfig().dynamodb["local-global"] + ); // No Endpoints for this service }); @@ -342,7 +379,9 @@ describe("The configuration service", () => { try { config.getFunctions(); } catch (e) { - expect(e.message).toEqual("Functions were not defined in the config file."); + expect(e.message).toEqual( + "Functions were not defined in the config file." + ); } }); @@ -351,7 +390,9 @@ describe("The configuration service", () => { try { config.getDynamoDBConfig(); } catch (e) { - expect(e.message).toEqual("DynamoDB config is not defined in the config file."); + expect(e.message).toEqual( + "DynamoDB config is not defined in the config file." + ); } }); });