diff --git a/app/lib/static-lookups.lib.js b/app/lib/static-lookups.lib.js index a2ec4b1a76..d45aab5408 100644 --- a/app/lib/static-lookups.lib.js +++ b/app/lib/static-lookups.lib.js @@ -5,6 +5,32 @@ const billRunTypes = [ 'two_part_tariff' ] +const companyTypes = [ + 'person', + 'organisation' +] + +const contactTypes = [ + 'person', + 'department' +] + +const organisationTypes = [ + 'individual', + 'limitedCompany', + 'limitedLiabilityPartnership', + 'publicLimitedCompany' +] + +const sources = [ + 'nald', + 'wrls' +] + module.exports = { - billRunTypes + billRunTypes, + companyTypes, + contactTypes, + organisationTypes, + sources } diff --git a/app/validators/change-address.validator.js b/app/validators/change-address.validator.js new file mode 100644 index 0000000000..2e2253834b --- /dev/null +++ b/app/validators/change-address.validator.js @@ -0,0 +1,75 @@ +'use strict' + +/** + * @module ChangeAddressValidator + */ + +const Joi = require('joi') + +const StaticLookupsLib = require('../lib/static-lookups.lib.js') + +/** + * Checks that the payload of a `/billing-accounts/{invoiceAccountId}/address` request is valid + * + * The JOI schema was interpreted from what we found in water-abstraction-service/src/modules/invoice-accounts/routes.js + * + * @param {Object} data The data to be validated (assumed to be the request payload) + * + * @returns {Object} the result from calling Joi's schema.validate(). It will be an object with a `value:` property. If + * any errors are found the `error:` property will also exist detailing what the issues were +*/ +function go (data) { + const schema = Joi.object({ + address: _addressSchema(), + agentCompany: _agentCompanySchema(), + contact: _contactSchema() + }) + + return schema.validate(data) +} + +function _addressSchema () { + return Joi.object({ + id: Joi.string().guid().optional(), + addressLine1: Joi.string().optional(), + addressLine2: Joi.string().optional(), + addressLine3: Joi.string().optional(), + addressLine4: Joi.string().optional(), + town: Joi.string().optional(), + county: Joi.string().optional(), + country: Joi.string().optional(), + postcode: Joi.string().optional(), + uprn: Joi.number().optional(), + source: Joi.string().optional() + }).required() +} + +function _agentCompanySchema () { + return Joi.object({ + id: Joi.string().guid().optional(), + type: Joi.string().valid(...StaticLookupsLib.companyTypes).optional(), + organisationType: Joi.string().valid(...StaticLookupsLib.organisationTypes).optional(), + name: Joi.string().optional(), + companyNumber: Joi.string().optional() + }).optional() +} + +function _contactSchema () { + return Joi.object({ + id: Joi.string().guid().optional(), + type: Joi.string().valid(...StaticLookupsLib.contactTypes).optional(), + salutation: Joi.string().optional(), + firstName: Joi.string().optional(), + initials: Joi.string().optional(), + middleInitials: Joi.string().optional(), + lastName: Joi.string().optional(), + suffix: Joi.string().optional(), + department: Joi.string().optional(), + source: Joi.string().valid(...StaticLookupsLib.sources).optional(), + isTest: Joi.boolean().default(false).optional() + }).optional() +} + +module.exports = { + go +} diff --git a/test/validators/change-address.validator.test.js b/test/validators/change-address.validator.test.js new file mode 100644 index 0000000000..d424fff414 --- /dev/null +++ b/test/validators/change-address.validator.test.js @@ -0,0 +1,218 @@ +'use strict' + +// Test framework dependencies +const Lab = require('@hapi/lab') +const Code = require('@hapi/code') + +const { describe, it, beforeEach } = exports.lab = Lab.script() +const { expect } = Code + +// Thing under test +const ChangeAddressValidator = require('../../app/validators/change-address.validator.js') + +describe('Create Bill Run validator', () => { + const completeAddress = { + id: '752446c0-a649-4383-967c-4e4fb6e13dde', + addressLine1: 'Building 12', + addressLine2: 'SCP Headquarters', + addressLine3: 'East campus', + addressLine4: '227 Roundly Rd', + town: 'Leeds', + county: 'West Yorkshire', + country: 'United Kingdom', + postcode: 'LS8 4HS', + uprn: 97564, + source: 'ea-address-facade' + } + const completeAgentCompany = { + id: 'aa0dd66a-6f38-4ea6-b2e1-9639cb51c792', + type: 'organisation', + organisationType: 'limitedCompany', + name: 'SCP', + companyNumber: 'OE008471' + } + const completeContact = { + id: '9969a9e6-c440-4065-ba2c-16566cb59575', + type: 'person', + salutation: 'Mrs', + firstName: 'Margherita', + middleInitials: 'OE008471', + lastName: 'Villar', + suffix: 'MBE', + department: 'Humanoid Risk Assessment', + source: 'wrls', + isTest: false + } + + let data + + describe('when valid data is provided', () => { + describe('and it is the minimalist valid data allowed', () => { + beforeEach(() => { + data = { + address: {} + } + }) + + it('confirms the data is valid', () => { + const result = ChangeAddressValidator.go(data) + + expect(result.value).to.exist() + expect(result.error).not.to.exist() + }) + }) + + describe('which includes only a valid address', () => { + beforeEach(() => { + data = { + address: { + ...completeAddress + } + } + }) + + it('confirms the data is valid', () => { + const result = ChangeAddressValidator.go(data) + + expect(result.value).to.exist() + expect(result.error).not.to.exist() + expect(result.value.address.id).to.equal(data.address.id) + }) + }) + + describe('which includes a minimal address and a valid agent company', () => { + beforeEach(() => { + data = { + agentCompany: { + ...completeAgentCompany + }, + address: {} + } + }) + + it('confirms the data is valid', () => { + const result = ChangeAddressValidator.go(data) + + expect(result.value).to.exist() + expect(result.error).not.to.exist() + expect(result.value.agentCompany.id).to.equal(data.agentCompany.id) + }) + }) + + describe('which includes a minimal address and a valid contact', () => { + beforeEach(() => { + data = { + contact: { + ...completeContact + }, + address: {} + } + }) + + it('confirms the data is valid', () => { + const result = ChangeAddressValidator.go(data) + + expect(result.value).to.exist() + expect(result.error).not.to.exist() + expect(result.value.contact.id).to.equal(data.contact.id) + }) + }) + }) + + describe('when invalid data is provided', () => { + describe("because no 'address' is provided", () => { + beforeEach(() => { + data = { + agentCompany: {}, + contact: {} + } + }) + + it('returns an error', () => { + const result = ChangeAddressValidator.go(data) + + expect(result.value).to.exist() + expect(result.error).to.exist() + expect(result.error.details[0].message).to.equal('"address" is required') + }) + }) + + describe('and the issue is in the agent company', () => { + beforeEach(() => { + data = { + agentCompany: { + ...completeAgentCompany + }, + address: {} + } + }) + + describe("because 'type' is unrecognised", () => { + beforeEach(() => { + data.agentCompany.type = 'INVALID' + }) + + it('returns an error', () => { + const result = ChangeAddressValidator.go(data) + + expect(result.value).to.exist() + expect(result.error).to.exist() + expect(result.error.details[0].message).startsWith('"agentCompany.type" must be one of') + }) + }) + + describe("because 'organisationType' is unrecognised", () => { + beforeEach(() => { + data.agentCompany.organisationType = 'INVALID' + }) + + it('returns an error', () => { + const result = ChangeAddressValidator.go(data) + + expect(result.value).to.exist() + expect(result.error).to.exist() + expect(result.error.details[0].message).startsWith('"agentCompany.organisationType" must be one of') + }) + }) + }) + + describe('and it is an issue in the contact', () => { + beforeEach(() => { + data = { + contact: { + ...completeContact + }, + address: {} + } + }) + + describe("because 'type' is unrecognised", () => { + beforeEach(() => { + data.contact.type = 'INVALID' + }) + + it('returns an error', () => { + const result = ChangeAddressValidator.go(data) + + expect(result.value).to.exist() + expect(result.error).to.exist() + expect(result.error.details[0].message).startsWith('"contact.type" must be one of') + }) + }) + + describe("because 'source' is unrecognised", () => { + beforeEach(() => { + data.contact.source = 'INVALID' + }) + + it('returns an error', () => { + const result = ChangeAddressValidator.go(data) + + expect(result.value).to.exist() + expect(result.error).to.exist() + expect(result.error.details[0].message).startsWith('"contact.source" must be one of') + }) + }) + }) + }) +})