Merged PR 2374: addeding domain layer extrraction
# Summary This PR introduces a **Case Lifecycle Domain Boundary** to centralize lifecycle decision logic and reduce coupling within the appeals application. The work is **behaviour-preserving** and introduces no intentional changes to business rules, CRM integrations, translations, dashboards, API routes, or user-facing functionality. ## What was added New lifecycle boundary: ```text lib/domain/case-lifecycle/ ``` Key responsibilities extracted: - Specialist process normalization - Appeal type mapping - Specialist process stage override mapping - Stage case-type resolution - Stage catalogue lookup - Closed-case status recognition - Lifecycle stage index resolution - Lifecycle stage status assignment ## Behaviour preserved Characterization tests were added before each extraction to preserve: - Appeal type mapping and aliases - Specialist process handling - Lifecycle stage progression - Closed-case handling - Status assignment (`complete`, `in-progress`, `not-started`) - Existing ROW behaviour - Existing `statuscode` lifecycle semantics Closed-case recognition remains unchanged for: ```text 1000 5 6 846040013 846040059 846040060 ``` ## Documentation Added: ```text lib/domain/case-lifecycle/README.md ``` Documenting: - Boundary ownership - Non-goals - Lifecycle invariants - Known architectural constraints - Future extraction roadmap ## Testing Added lifecycle characterization coverage for: - Stage wrapper behaviour - Specialist process normalization - Appeal type mapping - Specialist process stage mapping - Stage case-type resolution - Stage catalogue lookup - Progress behaviour - Closed-case status handling - Stage index resolution - Stage status assignment ## Validation - Lifecycle characterization tests passed - `npm run lint` passed with no errors ## Out of Scope No changes to: - Stage catalogue ownership - Representation eligibility - Dashboard calculations - CRM/OData queries - API routes - Redux state - EN/CY translations - Event visibility logic ## Risk **Low risk** The refactor was delivered through small, characterization-first slices with no functional changes intended. Related work items: #23527
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const vm = require("vm");
|
||||
const assert = require("assert");
|
||||
|
||||
const rootDir = path.resolve(__dirname, "..", "..");
|
||||
|
||||
const loadNormalizationModule = () => {
|
||||
const filePath = path.join(
|
||||
rootDir,
|
||||
"lib",
|
||||
"domain",
|
||||
"case-lifecycle",
|
||||
"normalizeSpecialistProcess.js"
|
||||
);
|
||||
|
||||
let source = fs.readFileSync(filePath, "utf8");
|
||||
source = source.replace(
|
||||
/export function\s+normalizeSpecialistProcess/,
|
||||
"function normalizeSpecialistProcess"
|
||||
);
|
||||
source += "\nmodule.exports = { normalizeSpecialistProcess };\n";
|
||||
|
||||
const context = {
|
||||
module: { exports: {} },
|
||||
exports: {},
|
||||
require
|
||||
};
|
||||
|
||||
vm.runInNewContext(source, context, { filename: filePath });
|
||||
return context.module.exports;
|
||||
};
|
||||
|
||||
const tests = [];
|
||||
const test = (name, fn) => tests.push({ name, fn });
|
||||
|
||||
const { normalizeSpecialistProcess } = loadNormalizationModule();
|
||||
|
||||
test("returns pinswg_specialistcaseprocess when present", () => {
|
||||
assert.strictEqual(
|
||||
normalizeSpecialistProcess({ pinswg_specialistcaseprocess: 846040001 }),
|
||||
846040001
|
||||
);
|
||||
});
|
||||
|
||||
test("returns misspelled fallback when canonical field is absent", () => {
|
||||
assert.strictEqual(
|
||||
normalizeSpecialistProcess({
|
||||
pinswg_speacialistcaseprocess: 846040000
|
||||
}),
|
||||
846040000
|
||||
);
|
||||
});
|
||||
|
||||
test("canonical field wins when both fields exist", () => {
|
||||
assert.strictEqual(
|
||||
normalizeSpecialistProcess({
|
||||
pinswg_specialistcaseprocess: 846040001,
|
||||
pinswg_speacialistcaseprocess: 846040000
|
||||
}),
|
||||
846040001
|
||||
);
|
||||
});
|
||||
|
||||
test("returns empty string when neither field exists", () => {
|
||||
assert.strictEqual(normalizeSpecialistProcess({}), "");
|
||||
});
|
||||
|
||||
test("returns empty string for null or undefined source", () => {
|
||||
assert.strictEqual(normalizeSpecialistProcess(null), "");
|
||||
assert.strictEqual(normalizeSpecialistProcess(undefined), "");
|
||||
});
|
||||
|
||||
test("preserves number values without coercing to strings", () => {
|
||||
const result = normalizeSpecialistProcess({
|
||||
pinswg_specialistcaseprocess: 846040001
|
||||
});
|
||||
|
||||
assert.strictEqual(typeof result, "number");
|
||||
assert.strictEqual(result, 846040001);
|
||||
});
|
||||
|
||||
test("preserves string values without coercing to numbers", () => {
|
||||
const result = normalizeSpecialistProcess({
|
||||
pinswg_specialistcaseprocess: "846040001"
|
||||
});
|
||||
|
||||
assert.strictEqual(typeof result, "string");
|
||||
assert.strictEqual(result, "846040001");
|
||||
});
|
||||
|
||||
const run = async () => {
|
||||
let passed = 0;
|
||||
|
||||
for (const currentTest of tests) {
|
||||
await currentTest.fn();
|
||||
passed += 1;
|
||||
}
|
||||
|
||||
console.log(
|
||||
`Phase 22 case-lifecycle-specialist-process-normalization tests passed (${passed}/${tests.length}).`
|
||||
);
|
||||
};
|
||||
|
||||
module.exports = run;
|
||||
|
||||
if (require.main === module) {
|
||||
run().catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user