@planning-inspectorate/data-model 2.14.0 → 2.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/package.json +1 -1
  2. package/pins_data_model/models/model_appeal_document.py +1 -1
  3. package/pins_data_model/models/model_appeal_event.py +1 -1
  4. package/pins_data_model/models/model_appeal_event_estimate.py +1 -1
  5. package/pins_data_model/models/model_appeal_has.py +1 -1
  6. package/pins_data_model/models/model_appeal_representation.py +1 -1
  7. package/pins_data_model/models/model_appeal_s78.py +1 -1
  8. package/pins_data_model/models/model_dart_response.py +1 -1
  9. package/pins_data_model/models/model_entraid.py +1 -1
  10. package/pins_data_model/models/model_folder.py +1 -1
  11. package/pins_data_model/models/model_gettimesheets_response.py +1 -1
  12. package/pins_data_model/models/model_listed_building.py +1 -1
  13. package/pins_data_model/models/model_listed_buildings.py +1 -1
  14. package/pins_data_model/models/model_nsip_document.py +1 -1
  15. package/pins_data_model/models/model_nsip_exam_timetable.py +1 -1
  16. package/pins_data_model/models/model_nsip_invoice.py +1 -1
  17. package/pins_data_model/models/model_nsip_meeting.py +1 -1
  18. package/pins_data_model/models/model_nsip_project.py +1 -1
  19. package/pins_data_model/models/model_nsip_project_update.py +1 -1
  20. package/pins_data_model/models/model_nsip_representation.py +1 -1
  21. package/pins_data_model/models/model_nsip_subscription.py +1 -1
  22. package/pins_data_model/models/model_pins_inspector.py +1 -1
  23. package/pins_data_model/models/model_pins_lpa.py +1 -1
  24. package/pins_data_model/models/model_s51_advice.py +1 -1
  25. package/pins_data_model/models/model_service_user.py +1 -1
  26. package/schemas/commands/appellant-submission.schema.json +8 -0
  27. package/schemas/commands/lpa-questionnaire.schema.json +2 -2
  28. package/src/schemas.d.ts +6 -2
  29. package/tests/appeals/commands/enforcement-listed-questionnaire.test.js +109 -0
  30. package/tests/appeals/commands/enforcement-listed-submission.test.js +187 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@planning-inspectorate/data-model",
3
- "version": "2.14.0",
3
+ "version": "2.15.0",
4
4
  "description": "JSON Schemas for the Planning Inspectorate's Data Model",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: appeal-document.schema.json
3
- # timestamp: 2026-02-04T16:06:56+00:00
3
+ # timestamp: 2026-02-05T10:08:01+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: appeal-event.schema.json
3
- # timestamp: 2026-02-04T16:06:56+00:00
3
+ # timestamp: 2026-02-05T10:08:01+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: appeal-event-estimate.schema.json
3
- # timestamp: 2026-02-04T16:06:56+00:00
3
+ # timestamp: 2026-02-05T10:08:01+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: appeal-has.schema.json
3
- # timestamp: 2026-02-04T16:06:56+00:00
3
+ # timestamp: 2026-02-05T10:08:01+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: appeal-representation.schema.json
3
- # timestamp: 2026-02-04T16:06:56+00:00
3
+ # timestamp: 2026-02-05T10:08:01+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: appeal-s78.schema.json
3
- # timestamp: 2026-02-04T16:06:57+00:00
3
+ # timestamp: 2026-02-05T10:08:01+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: dart-response.schema.json
3
- # timestamp: 2026-02-04T16:06:57+00:00
3
+ # timestamp: 2026-02-05T10:08:01+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: entraid.schema.json
3
- # timestamp: 2026-02-04T16:06:57+00:00
3
+ # timestamp: 2026-02-05T10:08:01+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: folder.schema.json
3
- # timestamp: 2026-02-04T16:06:57+00:00
3
+ # timestamp: 2026-02-05T10:08:01+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: gettimesheets-response.schema.json
3
- # timestamp: 2026-02-04T16:06:57+00:00
3
+ # timestamp: 2026-02-05T10:08:01+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: listed-building.schema.json
3
- # timestamp: 2026-02-04T16:06:57+00:00
3
+ # timestamp: 2026-02-05T10:08:01+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: listed-buildings.schema.json
3
- # timestamp: 2026-02-04T16:06:57+00:00
3
+ # timestamp: 2026-02-05T10:08:01+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: nsip-document.schema.json
3
- # timestamp: 2026-02-04T16:06:57+00:00
3
+ # timestamp: 2026-02-05T10:08:01+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: nsip-exam-timetable.schema.json
3
- # timestamp: 2026-02-04T16:06:57+00:00
3
+ # timestamp: 2026-02-05T10:08:01+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: nsip-invoice.schema.json
3
- # timestamp: 2026-02-04T16:06:57+00:00
3
+ # timestamp: 2026-02-05T10:08:01+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: nsip-meeting.schema.json
3
- # timestamp: 2026-02-04T16:06:57+00:00
3
+ # timestamp: 2026-02-05T10:08:01+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: nsip-project.schema.json
3
- # timestamp: 2026-02-04T16:06:57+00:00
3
+ # timestamp: 2026-02-05T10:08:01+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: nsip-project-update.schema.json
3
- # timestamp: 2026-02-04T16:06:57+00:00
3
+ # timestamp: 2026-02-05T10:08:01+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: nsip-representation.schema.json
3
- # timestamp: 2026-02-04T16:06:57+00:00
3
+ # timestamp: 2026-02-05T10:08:01+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: nsip-subscription.schema.json
3
- # timestamp: 2026-02-04T16:06:57+00:00
3
+ # timestamp: 2026-02-05T10:08:01+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: pins-inspector.schema.json
3
- # timestamp: 2026-02-04T16:06:57+00:00
3
+ # timestamp: 2026-02-05T10:08:01+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: pins-lpa.schema.json
3
- # timestamp: 2026-02-04T16:06:57+00:00
3
+ # timestamp: 2026-02-05T10:08:01+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: s51-advice.schema.json
3
- # timestamp: 2026-02-04T16:06:57+00:00
3
+ # timestamp: 2026-02-05T10:08:01+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: service-user.schema.json
3
- # timestamp: 2026-02-04T16:06:58+00:00
3
+ # timestamp: 2026-02-05T10:08:01+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -73,6 +73,14 @@
73
73
  { "$ref": "appeals-components/appellant-procedure-preference.schema.json" },
74
74
  { "$ref": "appeals-components/appeal-ldc.submission.schema.json" }
75
75
  ]
76
+ },
77
+ {
78
+ "$comment": "Enforcement listed building",
79
+ "allOf": [
80
+ { "properties": { "caseType": { "enum": ["F"] } } },
81
+ { "$ref": "appeals-components/appeal-enforcement-submission.schema.json" },
82
+ { "$ref": "appeals-components/appellant-procedure-preference.schema.json" }
83
+ ]
76
84
  }
77
85
  ]
78
86
  },
@@ -71,9 +71,9 @@
71
71
  ]
72
72
  },
73
73
  {
74
- "$comment": "Enforcement",
74
+ "$comment": "Enforcement + Enforcement listed building",
75
75
  "allOf": [
76
- { "properties": { "caseType": { "enum": ["C"] } } },
76
+ { "properties": { "caseType": { "enum": ["C", "F"] } } },
77
77
  { "$ref": "appeals-components/lpa-questionnaire-common.schema.json" },
78
78
  { "$ref": "appeals-components/lpa-questionnaire-has.schema.json" },
79
79
  { "$ref": "appeals-components/lpa-procedure-preference.schema.json" },
package/src/schemas.d.ts CHANGED
@@ -3993,7 +3993,11 @@ export interface AppellantSubmissionCommand {
3993
3993
  } & AppellantCommonSubmissionProperties &
3994
3994
  AppellantCommonPlanningProperties &
3995
3995
  AppellantProcedurePreferenceProperties &
3996
- LDCSpecificProperties);
3996
+ LDCSpecificProperties)
3997
+ | ({
3998
+ caseType?: 'F';
3999
+ } & AppellantEnforcementSubmissionProperties &
4000
+ AppellantProcedurePreferenceProperties);
3997
4001
  documents: {
3998
4002
  /**
3999
4003
  * The unique identifier for the document
@@ -4597,7 +4601,7 @@ export interface LPAQuestionnaireCommand {
4597
4601
  LPAProcedurePreferenceProperties &
4598
4602
  ChangedListedBuildingNumbersProperties)
4599
4603
  | ({
4600
- caseType?: 'C';
4604
+ caseType?: 'C' | 'F';
4601
4605
  } & LPAQCommonSubmissionProperties &
4602
4606
  LPAQHASSubmissionProperties &
4603
4607
  LPAProcedurePreferenceProperties &
@@ -0,0 +1,109 @@
1
+ import { describe, it } from 'node:test';
2
+ import assert from 'node:assert';
3
+ import Ajv from 'ajv';
4
+ import addFormats from 'ajv-formats';
5
+ import { loadAllSchemasSync } from '../../../src/index.js';
6
+
7
+ const schema = 'lpa-questionnaire.schema.json';
8
+
9
+ const document = {
10
+ documentId: 'deserunt sint aliqua adipisicing dolore',
11
+ filename: 'ut dolore',
12
+ originalFilename: 'Lorem amet fugiat adipisicing Excepteur',
13
+ size: -90476934,
14
+ mime: 'magna labore exercitation ut',
15
+ documentURI: 'ut reprehenderit',
16
+ dateCreated: '1933-04-03T03:24:38.0Z',
17
+ documentType: 'plansDrawings'
18
+ };
19
+
20
+ const exampleEnforcementListedQuestionnaireSchema = {
21
+ casedata: {
22
+ caseType: 'F',
23
+ caseReference: 'est ea elit in nulla',
24
+ lpaQuestionnaireSubmittedDate: null,
25
+ siteAccessDetails: ['velit adipisicing fugiat dolor ullamco'],
26
+ siteSafetyDetails: ['ut', 'dolore ut sint Duis', 'magna consequat quis laboris officia'],
27
+ nearbyCaseReferences: ['nulla', 'incididunt eu nostrud eiusmod', 'exercitation', 'Ut nostrud', 'eiusmod cillum eu'],
28
+ neighbouringSiteAddresses: null,
29
+ isCorrectAppealType: true,
30
+ affectedListedBuildingNumbers: null,
31
+ isGreenBelt: null,
32
+ inConservationArea: null,
33
+ newConditionDetails: null,
34
+ notificationMethod: ['irure', 'cillum tempor incididunt labore in'],
35
+ lpaCostsAppliedFor: true,
36
+ lpaStatement: 'ex laborum cupidatat',
37
+ changedListedBuildingNumbers: null,
38
+ affectsScheduledMonument: true,
39
+ hasProtectedSpecies: false,
40
+ isAonbNationalLandscape: true,
41
+ designatedSitesNames: ['quis nulla eu officia exercitation'],
42
+ hasTreePreservationOrder: null,
43
+ isGypsyOrTravellerSite: true,
44
+ isPublicRightOfWay: null,
45
+ eiaEnvironmentalImpactSchedule: 'schedule-2',
46
+ eiaDevelopmentDescription: 'production-processing-of-metals',
47
+ eiaSensitiveAreaDetails: null,
48
+ eiaColumnTwoThreshold: false,
49
+ eiaScreeningOpinion: false,
50
+ eiaRequiresEnvironmentalStatement: null,
51
+ eiaCompletedEnvironmentalStatement: true,
52
+ hasStatutoryConsultees: true,
53
+ consultedBodiesDetails: 'in in labore irure nostrud',
54
+ hasConsultationResponses: null,
55
+ hasEmergingPlan: null,
56
+ hasSupplementaryPlanningDocs: null,
57
+ hasInfrastructureLevy: true,
58
+ isInfrastructureLevyFormallyAdopted: false,
59
+ infrastructureLevyAdoptedDate: '1913-06-14T10:40:33.0Z',
60
+ infrastructureLevyExpectedDate: '1912-06-14T10:40:33.0Z',
61
+ lpaProcedurePreference: null,
62
+ lpaProcedurePreferenceDetails: 'in reprehenderit',
63
+ lpaProcedurePreferenceDuration: -34480053.16630782,
64
+ noticeRelatesToBuildingEngineeringMiningOther: null,
65
+ siteAreaSquareMetres: null,
66
+ hasAllegedBreachArea: null,
67
+ doesAllegedBreachCreateFloorSpace: null,
68
+ changeOfUseRefuseOrWaste: null,
69
+ changeOfUseMineralExtraction: null,
70
+ changeOfUseMineralStorage: null,
71
+ relatesToErectionOfBuildingOrBuildings: null,
72
+ relatesToBuildingWithAgriculturalPurpose: null,
73
+ relatesToBuildingSingleDwellingHouse: null,
74
+ affectedTrunkRoadName: null,
75
+ isSiteOnCrownLand: null,
76
+ article4AffectedDevelopmentRights: null
77
+ },
78
+ documents: [document]
79
+ };
80
+
81
+ describe('Enforcement listed building questionnaire command schema', () => {
82
+ const schemas = loadAllSchemasSync();
83
+ const flatSchemas = Object.values(schemas).reduce((a, c) => ({ ...a, ...c }), {});
84
+ const ajv = new Ajv({ schemas: flatSchemas, allErrors: true });
85
+ addFormats(ajv);
86
+
87
+ it('should allow valid schema for Enforcement listed building', () => {
88
+ const validationResult = ajv.validate(schema, exampleEnforcementListedQuestionnaireSchema);
89
+ assert.strictEqual(validationResult, true);
90
+ });
91
+
92
+ it('should reject missing caseReference', () => {
93
+ const test = structuredClone(exampleEnforcementListedQuestionnaireSchema);
94
+ delete test.casedata.caseReference;
95
+ const validationResult = ajv.validate(schema, test);
96
+ assert.strictEqual(validationResult, false);
97
+ assert.strictEqual(
98
+ ajv.errors.some((x) => x.message === "must have required property 'caseReference'"),
99
+ true
100
+ );
101
+ });
102
+
103
+ it('should allow additional props', () => {
104
+ const test = structuredClone(exampleEnforcementListedQuestionnaireSchema);
105
+ test.test = 1;
106
+ const validationResult = ajv.validate(schema, test);
107
+ assert.strictEqual(validationResult, true);
108
+ });
109
+ });
@@ -0,0 +1,187 @@
1
+ import { describe, it } from 'node:test';
2
+ import assert from 'node:assert';
3
+ import Ajv from 'ajv';
4
+ import addFormats from 'ajv-formats';
5
+ import { loadAllSchemasSync } from '../../../src/index.js';
6
+
7
+ const schema = 'appellant-submission.schema.json';
8
+
9
+ const user = {
10
+ salutation: 'nisi aute tempor Ut',
11
+ firstName: null,
12
+ lastName: null,
13
+ emailAddress: null,
14
+ telephoneNumber: 'irure',
15
+ organisation: null,
16
+ serviceUserType: 'Appellant'
17
+ };
18
+
19
+ const document = {
20
+ documentId: 'Lorem',
21
+ filename: 'sunt',
22
+ originalFilename: 'dolore fugiat',
23
+ size: 663667,
24
+ mime: 'consequat veniam ipsum ullamco sint',
25
+ documentURI: 'reprehenderit',
26
+ dateCreated: '1904-04-10T14:53:32.0Z',
27
+ documentType: 'priorCorrespondenceWithPINS'
28
+ };
29
+
30
+ const exampleEnforcementListedSubmissionSchema = {
31
+ casedata: {
32
+ caseType: 'F',
33
+ submissionId: '4b78fc58-fcb0-70d6-5dea-b9b774f802d6',
34
+ caseProcedure: 'written',
35
+ lpaCode: 'cupidatat',
36
+ caseSubmittedDate: '1890-12-28T22:49:47.0Z',
37
+ enforcementNotice: true,
38
+ enforcementReference: '12345',
39
+ enforcementIssueDate: '1890-12-28T22:49:47.0Z',
40
+ enforcementEffectiveDate: '1890-12-28T22:49:47.0Z',
41
+ contactPlanningInspectorateDate: null,
42
+ interestInLand: 'owner',
43
+ writtenOrVerbalPermission: null,
44
+ descriptionOfAllegedBreach: 'a new chimney',
45
+ applicationMadeAndFeePaid: true,
46
+ applicationDevelopmentAllOrPart: 'all-of-the-development',
47
+ originalDevelopmentDescription: 'in amet',
48
+ changedDevelopmentDescription: true,
49
+ applicationReference: '43010/APP/1/290572',
50
+ applicationDate: '1890-12-28T22:49:47.0Z',
51
+ applicationDecision: 'granted',
52
+ applicationDecisionAppealed: false,
53
+ applicationDecisionDate: '1890-12-28T22:49:47.0Z',
54
+ appealDecisionDate: null,
55
+ caseSubmissionDueDate: null,
56
+ siteAddressLine1: 'occaecat aliquip non pariatur consequat',
57
+ siteAddressLine2: null,
58
+ siteAddressTown: 'et',
59
+ siteAddressCounty: 'velit',
60
+ siteAddressPostcode: 'nisi minim laboris sed',
61
+ siteGridReferenceEasting: '357144',
62
+ siteGridReferenceNorthing: '400534',
63
+ contactAddressLine1: null,
64
+ contactAddressLine2: null,
65
+ contactAddressTown: null,
66
+ contactAddressCounty: null,
67
+ contactAddressPostcode: null,
68
+ contactGridReferenceEasting: null,
69
+ contactGridReferenceNorthing: null,
70
+ siteAccessDetails: ['laborum', 'aute velit quis', 'dolor'],
71
+ siteSafetyDetails: ['velit aliqua', 'qui', 'tempor Duis ut dolore', 'anim Ut in ea', 'eiusmod laborum'],
72
+ appellantCostsAppliedFor: false,
73
+ appellantProcedurePreference: null,
74
+ appellantProcedurePreferenceDetails: 'eiusmod ex exercitation',
75
+ appellantProcedurePreferenceDuration: 10816414.941069126,
76
+ appellantProcedurePreferenceWitnessCount: -91300569.36444478,
77
+ statusPlanningObligation: 'proident aute',
78
+ nearbyCaseReferences: ['incididunt'],
79
+ namedIndividuals: [
80
+ {
81
+ firstName: 'Bob',
82
+ lastName: 'Bobberson',
83
+ interestInLand: 'owner',
84
+ writtenOrVerbalPermission: 'yes'
85
+ }
86
+ ],
87
+ appealGrounds: [
88
+ {
89
+ groundRef: 'a',
90
+ factsForGround: 'I like Christmas'
91
+ }
92
+ ]
93
+ },
94
+ documents: [document],
95
+ users: [user]
96
+ };
97
+
98
+ describe(' listed building submission command schema', () => {
99
+ const schemas = loadAllSchemasSync();
100
+ const flatSchemas = Object.values(schemas).reduce((a, c) => ({ ...a, ...c }), {});
101
+ const ajv = new Ajv({ schemas: flatSchemas, allErrors: true });
102
+ addFormats(ajv);
103
+
104
+ it('should allow valid schema for enforcement listed building', () => {
105
+ const validationResult = ajv.validate(schema, exampleEnforcementListedSubmissionSchema);
106
+ assert.strictEqual(validationResult, true);
107
+ });
108
+
109
+ it('should enforce eastings-northings pattern', () => {
110
+ const invalidPatterns = [
111
+ { easting: 'abc123', northing: '400534', description: 'easting with letters' },
112
+ { easting: '1234567*', northing: '400534', description: 'easting too long' },
113
+ { easting: '12345', northing: '400534', description: 'easting too short' },
114
+ { easting: '357144', northing: 'xyz123', description: 'northing with letters' },
115
+ { easting: '357144', northing: '12345678', description: 'northing too long' },
116
+ { easting: '357144', northing: '12345', description: 'northing too short' },
117
+ { easting: '', northing: '400534', description: 'empty easting' },
118
+ { easting: '357144', northing: '', description: 'empty northing' }
119
+ ];
120
+
121
+ for (const pattern of invalidPatterns) {
122
+ const test = structuredClone(exampleEnforcementListedSubmissionSchema);
123
+ test.casedata.siteGridReferenceEasting = pattern.easting;
124
+ test.casedata.siteGridReferenceNorthing = pattern.northing;
125
+
126
+ const validationResult = ajv.validate(schema, test);
127
+ assert.strictEqual(validationResult, false, `Expected validation to fail for ${pattern.description}`);
128
+ }
129
+ });
130
+
131
+ it('should allow only eastings-northings', () => {
132
+ const test = structuredClone(exampleEnforcementListedSubmissionSchema);
133
+ test.casedata.siteAddressPostcode = undefined;
134
+ test.casedata.siteAddressLine1 = null;
135
+ test.casedata.siteAddressTown = null;
136
+ const validationResult = ajv.validate(schema, test);
137
+ assert.strictEqual(validationResult, true);
138
+ });
139
+
140
+ it('should allow only site address', () => {
141
+ const test = structuredClone(exampleEnforcementListedSubmissionSchema);
142
+ test.casedata.siteGridReferenceEasting = undefined;
143
+ test.casedata.siteGridReferenceNorthing = null;
144
+ const validationResult = ajv.validate(schema, test);
145
+ assert.strictEqual(validationResult, true);
146
+ });
147
+
148
+ it('should enforce either site address or easting-northing', () => {
149
+ const test = structuredClone(exampleEnforcementListedSubmissionSchema);
150
+ test.casedata.siteAddressPostcode = undefined;
151
+ test.casedata.siteAddressLine1 = null;
152
+ test.casedata.siteAddressTown = null;
153
+ test.casedata.siteGridReferenceEasting = undefined;
154
+ test.casedata.siteGridReferenceNorthing = null;
155
+ const validationResult = ajv.validate(schema, test);
156
+ assert.strictEqual(validationResult, false);
157
+ });
158
+
159
+ it('should reject missing root property', () => {
160
+ const test = structuredClone(exampleEnforcementListedSubmissionSchema);
161
+ delete test.casedata.caseType;
162
+ const validationResult = ajv.validate(schema, test);
163
+ assert.strictEqual(validationResult, false);
164
+ assert.strictEqual(
165
+ ajv.errors.some((x) => x.message === "must have required property 'caseType'"),
166
+ true
167
+ );
168
+ });
169
+
170
+ it('should reject missing nested property', () => {
171
+ const test = structuredClone(exampleEnforcementListedSubmissionSchema);
172
+ delete test.casedata.caseProcedure;
173
+ const validationResult = ajv.validate(schema, test);
174
+ assert.strictEqual(validationResult, false);
175
+ assert.strictEqual(
176
+ ajv.errors.some((x) => x.message === "must have required property 'caseProcedure'"),
177
+ true
178
+ );
179
+ });
180
+
181
+ it('should allow additional props', () => {
182
+ const test = structuredClone(exampleEnforcementListedSubmissionSchema);
183
+ test.test = 1;
184
+ const validationResult = ajv.validate(schema, test);
185
+ assert.strictEqual(validationResult, true);
186
+ });
187
+ });