TASK22269: add phase22 azurestorage helper contract tests

This commit is contained in:
2026-03-26 11:11:24 +00:00
parent 8744d3dafc
commit 58e1eef1f5
3 changed files with 159 additions and 0 deletions
+32
View File
@@ -2975,3 +2975,35 @@ Validation:
Follow-ups:
- Any further `azurestorage.js` cleanup should remain bounded (e.g., logging-only normalization) and separated from behavior-affecting refactors.
---
### CL-083: TASK22269 Slice B1.8 — phase22 azurestorage helper contract coverage
date: 2026-03-26
author: Cline
scope: `tests/phase22/{azurestorage-helper-behaviour,index}.test.cjs`
type: change
rationale: Execute the selected bounded test-only follow-up by adding focused regression coverage for recently added azurestorage helper contracts.
impact: Improves confidence in query-path and hash-metadata helper output stability without changing runtime behavior.
status: completed
Summary:
- Added new phase22 suite: `tests/phase22/azurestorage-helper-behaviour.test.cjs`.
- Test suite isolates helper block from `actions/azurestorage.js` and verifies:
- `buildDownloadBlobQueryPath` default encoding output
- `buildDeleteBlobQueryPath` non-encoded option behavior
- `buildGetBlobListQueryPath` query output contract
- `buildHashMetadataPaths` key/value shape (`hashedfilepath`, `hasheddeletepath`, `hashgetblobs`)
- Wired suite into aggregate runner `tests/phase22/index.test.cjs`.
Validation:
- `npm run lint` -> pass with warnings only (pre-existing `react-hooks/exhaustive-deps`; no new lint errors)
- `node tests/phase22/index.test.cjs` -> pass (includes new azurestorage-helper 4/4)
- `node tests/phase7/service-behaviour.test.cjs` -> pass (13/13)
Follow-ups:
- Optional next bounded slice: add an explicit assertion for encoded `casefolderID` variants containing reserved query characters (`?`, `&`) if those inputs are expected in future flows.
@@ -0,0 +1,125 @@
const fs = require("fs");
const path = require("path");
const vm = require("vm");
const assert = require("assert");
const rootDir = path.resolve(__dirname, "..", "..");
const loadAzureStorageHelperModule = (injected = {}) => {
const filePath = path.join(rootDir, "actions", "azurestorage.js");
const source = fs.readFileSync(filePath, "utf8");
const start = source.indexOf("const buildDownloadBlobQueryPath =");
const end = source.indexOf("export const createContainerSas =");
if (start < 0 || end < 0 || end <= start) {
throw new Error("Unable to isolate azurestorage helper function block");
}
let helperSource = source.slice(start, end);
helperSource +=
"\nmodule.exports = { buildDownloadBlobQueryPath, buildDeleteBlobQueryPath, buildGetBlobListQueryPath, buildHashMetadataPaths };\n";
const context = {
module: { exports: {} },
exports: {},
require,
encodeURIComponent,
hashAPIPath: (route) => `HASH(${route})`,
...injected
};
vm.runInNewContext(helperSource, context, { filename: filePath });
return context.module.exports;
};
const tests = [];
const test = (name, fn) => tests.push({ name, fn });
test("azurestorage helper builds download path with default encoding", () => {
const mod = loadAzureStorageHelperModule();
const pathResult = mod.buildDownloadBlobQueryPath({
containerName: "alpha",
casefolderID: "A/B",
blobname: "my doc.pdf"
});
assert.strictEqual(
pathResult,
"/api/file/downloadblob?container=alpha&casefolderID=A%2FB&blobname=my%20doc.pdf"
);
});
test("azurestorage helper preserves raw values when encoding disabled", () => {
const mod = loadAzureStorageHelperModule();
const deletePath = mod.buildDeleteBlobQueryPath({
containerName: "alpha",
casefolderID: "A/B",
blobname: "my doc.pdf",
encodeCasefolderID: false,
encodeBlobname: false
});
assert.strictEqual(
deletePath,
"/api/file/deleteblob?container=alpha&casefolderID=A/B&blobname=my doc.pdf"
);
});
test("azurestorage helper builds getbloblist path without field mutation", () => {
const mod = loadAzureStorageHelperModule();
const listPath = mod.buildGetBlobListQueryPath({
containerName: "alpha",
casefolderID: "A/B"
});
assert.strictEqual(
listPath,
"/api/file/getbloblist?container=alpha&casefolderID=A/B"
);
});
test("azurestorage helper builds hash metadata map with stable keys", () => {
const mod = loadAzureStorageHelperModule({
hashAPIPath: (route) => `signed:${route}`
});
const metadata = mod.buildHashMetadataPaths({
containerName: "alpha",
casefolderID: "A/B",
blobname: "my doc.pdf"
});
assert.deepStrictEqual(JSON.parse(JSON.stringify(metadata)), {
hashedfilepath:
"signed:/api/file/downloadblob?container=alpha&casefolderID=A%2FB&blobname=my%20doc.pdf",
hasheddeletepath:
"signed:/api/file/deleteblob?container=alpha&casefolderID=A%2FB&blobname=my%20doc.pdf",
hashgetblobs:
"signed:/api/file/getbloblist?container=alpha&casefolderID=A/B"
});
});
const run = async () => {
let passed = 0;
for (const currentTest of tests) {
await currentTest.fn();
passed += 1;
}
console.log(
`Phase 22 azurestorage-helper tests passed (${passed}/${tests.length}).`
);
};
module.exports = run;
if (require.main === module) {
run().catch((error) => {
console.error(error);
process.exit(1);
});
}
+2
View File
@@ -5,6 +5,7 @@ const runCaseServiceTests = require("./case-service-behaviour.test.cjs");
const runPortalServiceTests = require("./portal-service-behaviour.test.cjs");
const runAuthRedirectSafetyTests = require("./auth-redirect-safety.test.cjs");
const runI18nRouteParityTests = require("./i18n-route-parity.test.cjs");
const runAzurestorageHelperTests = require("./azurestorage-helper-behaviour.test.cjs");
const run = async () => {
await runCoreTokenTests();
@@ -14,6 +15,7 @@ const run = async () => {
await runPortalServiceTests();
await runAuthRedirectSafetyTests();
await runI18nRouteParityTests();
await runAzurestorageHelperTests();
console.log("Phase 22 combined suite passed.");
};