TASK22224: harden createappealcompletemessage parity

This commit is contained in:
2026-03-24 05:49:58 +00:00
parent cc4fb36439
commit 9b0365ffbd
3 changed files with 139 additions and 9 deletions
+34
View File
@@ -516,3 +516,37 @@ Validation:
Follow-ups:
- Remaining optional cleanup in these handlers is dead import/unused local pruning (non-behavioral) if we want a final low-risk tidy pass.
---
### CL-013: TASK22224 completion-message route parity closure slice
date: 2026-03-24
author: Cline
scope: `pages/api/file/createappealcompletemessage_api.js`, `tests/phase21/file-handler-contract.test.cjs`
type: change
rationale: Continue next requested slice by closing the final promise-chain parity outlier in file completion-message flow and strengthening phase21 contract coverage.
impact: Improves maintainability and async error hygiene while preserving route behavior and existing error contracts.
status: completed
Summary:
- `pages/api/file/createappealcompletemessage_api.js`
- replaced inline `.catch(...)` on fire-and-forget `updateAccount(...)` with explicit async IIFE + `try/catch` and `void` invocation
- preserved non-blocking behavior and logging semantics for account-update failure path
- preserved primary route contracts and success payload (`{ status: "success" }`)
- phase21 file contract tests expanded:
- success path for encoded hash candidate on `createappealcompletemessage_api`
- dependency-failure contract assertion for `CREATE_APPEAL_COMPLETE_MESSAGE_FAILED`
Validation:
- `node tests/phase21/api-contract-slice1.test.cjs` -> pass
- helper: 4/4
- file-handler: 53/53
- email-handler: 12/12
- endpoint-handler: 152/152
Follow-ups:
- Optional final low-risk tidy sweep: remove dead imports/unused locals in legacy file handlers now that contract hardening stream is functionally complete.
@@ -115,15 +115,19 @@ ApiProxy.get(async (req, res) => {
contactId = contactId.match(/\(([^)]+)\)/);
if (typeofinvolvement != 846040000) {
updateAccount(
contactId[1],
{
pinswg_typeofinvolvement: 846040001
},
true
).catch((error) => {
consoleLogger(error);
});
void (async () => {
try {
await updateAccount(
contactId[1],
{
pinswg_typeofinvolvement: 846040001
},
true
);
} catch (error) {
consoleLogger(error);
}
})();
}
await createCaseCompleteMessage(containerName, tempCaseRef);
@@ -694,6 +694,98 @@ test("createappealcompletemessageproxy dependency failure returns CREATE_APPEAL_
);
});
test("createappealcompletemessage_api accepts encoded hash variant and returns success", async () => {
const mod = loadModule(
"pages/api/file/createappealcompletemessage_api.js",
{
nextConnect: createNextConnectMock(),
middleware: () => {},
respondError: respondErrorMock,
respondSuccess: respondSuccessMock,
hashAPIPath: (queryPath) =>
queryPath.includes("tempcaseref=TMP%2F1")
? "&hash=expected"
: "&hash=other",
getProgressBlobs: async () => ({ path: "TMP/1/progress.json" }),
downloadProgressFile: async (_container, path) => {
if (path.includes("_case.json")) {
return {
"customerid_contact@odata.bind":
"/contacts(00000000-0000-0000-0000-000000000001)"
};
}
return {
pinswg_siteaddressline1: "Line 1",
pinswg_siteaddressline2: "",
pinswg_siteaddresstown: "Town",
pinswg_siteaddresscounty: "County",
pinswg_siteaddresspostcode: "CF1 1AA",
pinswg_lpaapplicationreference: "LPA-1",
pinswg_developmentdescription: "Desc",
pinswg_name: "tmp"
};
},
createBlob: async () => ({}),
getCaseBlob: async () => ({}),
createCaseCompleteMessage: async () => ({}),
updateAccount: async () => ({}),
consoleLogger: () => {},
_: { isEmpty: (v) => v === undefined || v === null || v === "" }
}
);
const req = {
query: {
container: "cont/1",
tempcaseref: "TMP/1",
inv: "846040001",
hash: "expected"
}
};
const res = createRes();
await mod.default.handler(req, res);
assert.strictEqual(res.state.statusCode, 200);
assert.deepStrictEqual(JSON.parse(JSON.stringify(res.state.jsonBody)), {
status: "success"
});
});
test("createappealcompletemessage_api dependency failure returns CREATE_APPEAL_COMPLETE_MESSAGE_FAILED", async () => {
const mod = loadModule(
"pages/api/file/createappealcompletemessage_api.js",
{
nextConnect: createNextConnectMock(),
middleware: () => {},
respondError: respondErrorMock,
respondSuccess: respondSuccessMock,
hashAPIPath: () => "&hash=expected",
getProgressBlobs: async () => {
throw new Error("progress failed");
},
consoleLogger: () => {},
_: { isEmpty: () => true }
}
);
const req = {
query: {
container: "c1",
tempcaseref: "TMP1",
hash: "expected"
}
};
const res = createRes();
await mod.default.handler(req, res);
assert.strictEqual(res.state.statusCode, 400);
assert.strictEqual(
res.state.jsonBody.error.code,
"CREATE_APPEAL_COMPLETE_MESSAGE_FAILED"
);
});
test("createcaseinvolvement returns record exists on 412 conflict", async () => {
const mod = loadModule("pages/api/file/createcaseinvolvement_api.js", {
respondError: respondErrorMock,