From 886b721fa180df7e6c7fbfe2d56a988895ac2847 Mon Sep 17 00:00:00 2001 From: Craig Broady <79261988+cb-cs@users.noreply.github.com> Date: Mon, 25 Nov 2024 14:02:39 +0000 Subject: [PATCH] CB2-14764: Update Test Result Service for recalls (#428) * feat(cb2-14764): bumped types def and updated joi validation * feat(cb2-14764): linter * feat(cb2-14764): allow empty string for manufacturer --- package-lock.json | 8 +- package.json | 2 +- .../TestResultsSchemaHGVCancelled.ts | 6 + .../TestResultsSchemaHGVSubmitted.ts | 6 + .../TestResultsSchemaPSVCancelled.ts | 6 + .../TestResultsSchemaPSVSubmitted.ts | 6 + .../TestResultsSchemaTRLCancelled.ts | 6 + .../TestResultsSchemaTRLSubmitted.ts | 6 + tests/unit/insertTestResult.unitTest.ts | 179 ++++++++++++++++++ 9 files changed, 220 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6292e465..c2ec9fb5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "@aws-sdk/lib-dynamodb": "^3.632.0", "@aws-sdk/smithy-client": "^3.374.0", "@dvsa/cvs-microservice-common": "^1.2.4", - "@dvsa/cvs-type-definitions": "^7.6.1", + "@dvsa/cvs-type-definitions": "^7.7.1", "@smithy/smithy-client": "^2.5.1", "@smithy/util-utf8": "^2.3.0", "aws-lambda": "^1.0.7", @@ -12895,9 +12895,9 @@ } }, "node_modules/@dvsa/cvs-type-definitions": { - "version": "7.6.1", - "resolved": "https://registry.npmjs.org/@dvsa/cvs-type-definitions/-/cvs-type-definitions-7.6.1.tgz", - "integrity": "sha512-j30M+ZDmJyD/R4wuYsHeJc4e2gJ/hm6ZrgNbeS26eaItTswVzVnGWCtcd+VI9d9gUZKTHzPP8tvmAbD+9P8Ifw==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@dvsa/cvs-type-definitions/-/cvs-type-definitions-7.7.1.tgz", + "integrity": "sha512-OSCxSLc6tQb+Gqt9GiikwCTE5GYNm+zzzMcwvGMpoWtOBzDW9qZvglAF+7XvBG3DmQvGSA+7EHvVdIYwGIrF/A==", "dependencies": { "ajv": "^8.12.0", "json-schema-deref-sync": "^0.14.0", diff --git a/package.json b/package.json index 2cf3c25b..2e273315 100644 --- a/package.json +++ b/package.json @@ -87,7 +87,7 @@ "@aws-sdk/lib-dynamodb": "^3.632.0", "@aws-sdk/smithy-client": "^3.374.0", "@dvsa/cvs-microservice-common": "^1.2.4", - "@dvsa/cvs-type-definitions": "^7.6.1", + "@dvsa/cvs-type-definitions": "^7.7.1", "@smithy/smithy-client": "^2.5.1", "@smithy/util-utf8": "^2.3.0", "aws-lambda": "^1.0.7", diff --git a/src/models/validators/TestResultsSchemaHGVCancelled.ts b/src/models/validators/TestResultsSchemaHGVCancelled.ts index 4ef50f1b..ed13e7b2 100644 --- a/src/models/validators/TestResultsSchemaHGVCancelled.ts +++ b/src/models/validators/TestResultsSchemaHGVCancelled.ts @@ -78,4 +78,10 @@ export const hgvCancelled = testResultsCommonSchema.keys({ .required(), testTypes: Joi.array().items(testTypesSchema).required(), regnDate: Joi.string().allow('', null), + recalls: Joi.object() + .keys({ + hasRecall: Joi.boolean().required(), + manufacturer: Joi.string().required().allow('', null), + }) + .optional(), }); diff --git a/src/models/validators/TestResultsSchemaHGVSubmitted.ts b/src/models/validators/TestResultsSchemaHGVSubmitted.ts index 32480af4..bc48c26b 100644 --- a/src/models/validators/TestResultsSchemaHGVSubmitted.ts +++ b/src/models/validators/TestResultsSchemaHGVSubmitted.ts @@ -78,4 +78,10 @@ export const hgvSubmitted = testResultsCommonSchema.keys({ .required(), testTypes: Joi.array().items(testTypesSchema).required(), regnDate: Joi.string().allow('', null), + recalls: Joi.object() + .keys({ + hasRecall: Joi.boolean().required(), + manufacturer: Joi.string().required().allow('', null), + }) + .optional(), }); diff --git a/src/models/validators/TestResultsSchemaPSVCancelled.ts b/src/models/validators/TestResultsSchemaPSVCancelled.ts index 754582c4..7c8c3129 100644 --- a/src/models/validators/TestResultsSchemaPSVCancelled.ts +++ b/src/models/validators/TestResultsSchemaPSVCancelled.ts @@ -79,4 +79,10 @@ export const psvCancelled = testResultsCommonSchema.keys({ countryOfRegistration: Joi.string().required().allow('', null), vehicleSize: Joi.string().valid('small', 'large').required(), testTypes: Joi.array().items(testTypesSchema).required(), + recalls: Joi.object() + .keys({ + hasRecall: Joi.boolean().required(), + manufacturer: Joi.string().required().allow('', null), + }) + .optional(), }); diff --git a/src/models/validators/TestResultsSchemaPSVSubmitted.ts b/src/models/validators/TestResultsSchemaPSVSubmitted.ts index 418eb9aa..b02db9ae 100644 --- a/src/models/validators/TestResultsSchemaPSVSubmitted.ts +++ b/src/models/validators/TestResultsSchemaPSVSubmitted.ts @@ -79,4 +79,10 @@ export const psvSubmitted = testResultsCommonSchema.keys({ vehicleSize: Joi.string().valid('small', 'large').required(), reasonForCancellation: Joi.string().max(500).required().allow('', null), testTypes: Joi.array().items(testTypesSchema).required(), + recalls: Joi.object() + .keys({ + hasRecall: Joi.boolean().required(), + manufacturer: Joi.string().required().allow('', null), + }) + .optional(), }); diff --git a/src/models/validators/TestResultsSchemaTRLCancelled.ts b/src/models/validators/TestResultsSchemaTRLCancelled.ts index 91bd0e94..a3f62568 100644 --- a/src/models/validators/TestResultsSchemaTRLCancelled.ts +++ b/src/models/validators/TestResultsSchemaTRLCancelled.ts @@ -73,4 +73,10 @@ export const trlCancelled = testResultsCommonSchema.keys({ trailerId: Joi.string().required(), testTypes: Joi.array().items(testTypesSchema).required(), firstUseDate: Joi.string().allow('', null), + recalls: Joi.object() + .keys({ + hasRecall: Joi.boolean().required(), + manufacturer: Joi.string().required().allow('', null), + }) + .optional(), }); diff --git a/src/models/validators/TestResultsSchemaTRLSubmitted.ts b/src/models/validators/TestResultsSchemaTRLSubmitted.ts index 63b0b277..03ba6316 100644 --- a/src/models/validators/TestResultsSchemaTRLSubmitted.ts +++ b/src/models/validators/TestResultsSchemaTRLSubmitted.ts @@ -74,4 +74,10 @@ export const trlSubmitted = testResultsCommonSchema.keys({ trailerId: Joi.string().required(), testTypes: Joi.array().items(testTypesSchema).required(), firstUseDate: Joi.string().allow('', null), + recalls: Joi.object() + .keys({ + hasRecall: Joi.boolean().required(), + manufacturer: Joi.string().required().allow('', null), + }) + .optional(), }); diff --git a/tests/unit/insertTestResult.unitTest.ts b/tests/unit/insertTestResult.unitTest.ts index 854e024c..28f5257b 100644 --- a/tests/unit/insertTestResult.unitTest.ts +++ b/tests/unit/insertTestResult.unitTest.ts @@ -7,6 +7,8 @@ import { TestTypeSchema, } from '@dvsa/cvs-type-definitions/types/v1/test-result'; import { TestResults } from '@dvsa/cvs-type-definitions/types/v1/enums/testResult.enum'; +import { RecallsSchema } from '@dvsa/cvs-type-definitions/types/v1/recalls'; +import { TestStatus } from '@dvsa/cvs-type-definitions/types/v1/enums/testStatus.enum'; import { ERRORS, MESSAGES, @@ -4252,4 +4254,181 @@ describe('insertTestResult', () => { }, ); }); + + describe('Vehicle Recalls', () => { + let testResult: TestResultSchema; + let recallsMock: RecallsSchema; + + describe('validateInsertTestResultPayload', () => { + describe('when inserting a test result with valid vehicle recalls present', () => { + context('and the status is submitted', () => { + beforeEach(() => { + testResult = { ...testResultsPostMock[0] } as TestResultSchema; + recallsMock = { + hasRecall: true, + manufacturer: 'manufacturer', + }; + }); + + it('should create the record successfully when hasRecalls and manufacturer are present', () => { + testResult.recalls = recallsMock; + const validationResult = + ValidationUtil.validateInsertTestResultPayload(testResult); + expect(validationResult).toBe(true); + }); + + it('should create the record successfully when hasRecalls is present and manufacturer is null', () => { + recallsMock.manufacturer = null; + testResult.recalls = recallsMock; + const validationResult = + ValidationUtil.validateInsertTestResultPayload(testResult); + expect(validationResult).toBe(true); + }); + + it('should create the record successfully when hasRecalls is present and manufacturer is empty string', () => { + recallsMock.manufacturer = ''; + testResult.recalls = recallsMock; + const validationResult = + ValidationUtil.validateInsertTestResultPayload(testResult); + expect(validationResult).toBe(true); + }); + + it('should create the record successfully when recalls object is not on payload', () => { + const validationResult = + ValidationUtil.validateInsertTestResultPayload(testResult); + expect(validationResult).toBe(true); + }); + }); + context('and the status is cancelled', () => { + beforeEach(() => { + testResult = { ...testResultsPostMock[0] } as TestResultSchema; + testResult.testStatus = 'cancelled' as TestStatus; + recallsMock = { + hasRecall: true, + manufacturer: 'manufacturer', + }; + }); + + it('should create the record successfully when hasRecalls and manufacturer are present', () => { + testResult.recalls = recallsMock; + const validationResult = + ValidationUtil.validateInsertTestResultPayload(testResult); + expect(validationResult).toBe(true); + }); + + it('should create the record successfully when hasRecalls is present and manufacturer is null', () => { + recallsMock.manufacturer = null; + testResult.recalls = recallsMock; + const validationResult = + ValidationUtil.validateInsertTestResultPayload(testResult); + expect(validationResult).toBe(true); + }); + + it('should create the record successfully when hasRecalls is present and manufacturer is empty string', () => { + recallsMock.manufacturer = ''; + testResult.recalls = recallsMock; + const validationResult = + ValidationUtil.validateInsertTestResultPayload(testResult); + expect(validationResult).toBe(true); + }); + + it('should create the record successfully when recalls object is not on payload', () => { + const validationResult = + ValidationUtil.validateInsertTestResultPayload(testResult); + expect(validationResult).toBe(true); + }); + }); + }); + describe('when inserting a test result', () => { + beforeEach(() => { + testResult = { ...testResultsPostMock[0] } as TestResultSchema; + recallsMock = { + hasRecall: true, + manufacturer: 'manufacturer', + }; + }); + + context('with an invalid recalls object value present', () => { + it('should throw validation error', () => { + testResult.recalls = true as any; + try { + ValidationUtil.validateInsertTestResultPayload(testResult); + } catch (error) { + expect(error).toBeInstanceOf(HTTPError); + expect(error.statusCode).toBe(400); + expect(error.body).toEqual({ + errors: ['"recalls" must be of type object'], + }); + } + }); + }); + context( + 'with an invalid hasRecall value in recalls object present', + () => { + it('should throw validation error', () => { + recallsMock.hasRecall = 'dummy value' as any; + testResult.recalls = recallsMock; + try { + ValidationUtil.validateInsertTestResultPayload(testResult); + } catch (error) { + expect(error).toBeInstanceOf(HTTPError); + expect(error.statusCode).toBe(400); + expect(error.body).toEqual({ + errors: ['"recalls.hasRecall" must be a boolean'], + }); + } + }); + }, + ); + context('with hasRecall missing from recalls object', () => { + it('should throw validation error', () => { + recallsMock.hasRecall = undefined as any; + testResult.recalls = recallsMock; + try { + ValidationUtil.validateInsertTestResultPayload(testResult); + } catch (error) { + expect(error).toBeInstanceOf(HTTPError); + expect(error.statusCode).toBe(400); + expect(error.body).toEqual({ + errors: ['"recalls.hasRecall" is required'], + }); + } + }); + }); + context( + 'with an invalid manufacture value in recalls object present', + () => { + it('should throw validation error', () => { + recallsMock.manufacturer = 123 as any; + testResult.recalls = recallsMock; + try { + ValidationUtil.validateInsertTestResultPayload(testResult); + } catch (error) { + expect(error).toBeInstanceOf(HTTPError); + expect(error.statusCode).toBe(400); + expect(error.body).toEqual({ + errors: ['"recalls.manufacturer" must be a string'], + }); + } + }); + }, + ); + context('with manufacturer missing from recalls object', () => { + it('should throw validation error', () => { + recallsMock.manufacturer = undefined as any; + testResult.recalls = recallsMock; + try { + ValidationUtil.validateInsertTestResultPayload(testResult); + } catch (error) { + expect(error).toBeInstanceOf(HTTPError); + expect(error.statusCode).toBe(400); + expect(error.body).toEqual({ + errors: ['"recalls.manufacturer" is required'], + }); + } + }); + }); + }); + }); + }); });