const assert = require("assert"); const fs = require("fs"); const path = require("path"); const { loadModule, createRes, createNextConnectMock, respondSuccessMock, respondErrorMock } = require("./_shared.cjs"); const tests = []; const test = (name, fn) => tests.push({ name, fn }); test("upload handler returns HASH_REQUIRED for missing hash", async () => { const mod = loadModule("pages/api/file/upload.js", { nextConnect: createNextConnectMock(), middleware: () => {}, hashAPIPath: () => "?hash=expected", respondError: respondErrorMock, respondSuccess: respondSuccessMock, createBlob: async () => ({ ok: true }), createRepBlob: async () => ({ ok: true }) }); const req = { query: {}, body: {} }; const res = createRes(); await mod.default.handler(req, res); assert.strictEqual(res.state.statusCode, 400); assert.strictEqual(res.state.jsonBody.error.code, "HASH_REQUIRED"); }); test("upload handler returns INVALID_HASH for wrong hash", async () => { const mod = loadModule("pages/api/file/upload.js", { nextConnect: createNextConnectMock(), middleware: () => {}, hashAPIPath: () => "?hash=expected", respondError: respondErrorMock, respondSuccess: respondSuccessMock, createBlob: async () => ({ ok: true }), createRepBlob: async () => ({ ok: true }) }); const req = { query: { hash: "wrong" }, body: { containerID: ["c1"], casefolderID: ["f1"] } }; const res = createRes(); await mod.default.handler(req, res); assert.strictEqual(res.state.statusCode, 400); assert.strictEqual(res.state.jsonBody.error.code, "INVALID_HASH"); }); test("upload handler accepts alternate canonical hash candidate", async () => { const mod = loadModule("pages/api/file/upload.js", { nextConnect: createNextConnectMock(), middleware: () => {}, hashAPIPath: (queryPath) => queryPath === "/api/file/upload?" ? "?hash=expected" : "?hash=other", respondError: respondErrorMock, respondSuccess: respondSuccessMock, createBlob: async () => ({ id: "blob-alt" }), createRepBlob: async () => ({ id: "rep-alt" }) }); const req = { query: { hash: "expected" }, body: { appealData: { key: "value" }, containerID: ["c1"], casefolderID: ["f1"], repOrAppeal: false } }; const res = createRes(); await mod.default.handler(req, res); assert.strictEqual(res.state.statusCode, 200); assert.deepStrictEqual(JSON.parse(JSON.stringify(res.state.jsonBody)), { data: { id: "blob-alt" } }); }); test("deleteblob handler returns MISSING_REQUIRED_QUERY when blobname missing", async () => { const mod = loadModule("pages/api/file/deleteblob.js", { nextConnect: createNextConnectMock(), middleware: () => {}, hashAPIPath: () => "&hash=expected", respondError: respondErrorMock, respondSuccess: respondSuccessMock, deleteBlob: async () => ({ ok: true }) }); const req = { query: { container: "c1", casefolderID: "f1", hash: "expected" } }; const res = createRes(); await mod.default.handler(req, res); assert.strictEqual(res.state.statusCode, 400); assert.strictEqual(res.state.jsonBody.error.code, "MISSING_REQUIRED_QUERY"); }); test("deleteblob handler accepts encoded hash variant and normalizes prefixed path", async () => { let deletedPath = null; const mod = loadModule("pages/api/file/deleteblob.js", { nextConnect: createNextConnectMock(), middleware: () => {}, hashAPIPath: (queryPath) => queryPath.includes("/api/file/deleteblob?container=c1") ? "&hash=expected" : "&hash=other", respondError: respondErrorMock, respondSuccess: respondSuccessMock, deleteBlob: async (_container, blobPath) => { deletedPath = blobPath; return { deleted: true }; } }); const req = { query: { container: "c1", casefolderID: "TMP-LFE1C", blobname: "TMP-LFE1C/files/2026-03-18_-_Site_Location_Plan_-_test document 021.xlsx", hash: "expected" } }; const res = createRes(); await mod.default.handler(req, res); assert.strictEqual(res.state.statusCode, 200); assert.strictEqual( deletedPath, "TMP-LFE1C/files/2026-03-18_-_Site_Location_Plan_-_test document 021.xlsx" ); }); test("deleteblob handler dependency failure returns DELETE_BLOB_FAILED", async () => { const mod = loadModule("pages/api/file/deleteblob.js", { nextConnect: createNextConnectMock(), middleware: () => {}, hashAPIPath: () => "&hash=expected", respondError: respondErrorMock, respondSuccess: respondSuccessMock, deleteBlob: async () => { throw new Error("delete failed"); } }); const req = { query: { container: "c1", casefolderID: "f1", blobname: "doc.pdf", hash: "expected" } }; const res = createRes(); await mod.default.handler(req, res); assert.strictEqual(res.state.statusCode, 400); assert.strictEqual(res.state.jsonBody.error.code, "DELETE_BLOB_FAILED"); }); test("deleteblobcase handler accepts encoded hash variant", async () => { let deletedCaseFolder = null; const mod = loadModule("pages/api/file/deleteblobcase.js", { nextConnect: createNextConnectMock(), middleware: () => {}, getSession: async () => ({ user: { id: "c1" } }), hashAPIPath: (queryPath) => queryPath.includes("casefolderID=CASE%2FSUB") ? "&hash=expected" : "&hash=other", respondError: respondErrorMock, respondSuccess: respondSuccessMock, deleteBlobCase: async (_container, casefolder) => { deletedCaseFolder = casefolder; return { deleted: true }; } }); const req = { query: { container: "c1", casefolderID: "CASE/SUB", hash: "expected" } }; const res = createRes(); await mod.default.handler(req, res); assert.strictEqual(res.state.statusCode, 200); assert.strictEqual(deletedCaseFolder, "CASE/SUB"); }); test("deleteblobcase handler dependency failure returns DELETE_BLOB_CASE_FAILED", async () => { const mod = loadModule("pages/api/file/deleteblobcase.js", { nextConnect: createNextConnectMock(), middleware: () => {}, getSession: async () => ({ user: { id: "c1" } }), hashAPIPath: () => "&hash=expected", respondError: respondErrorMock, respondSuccess: respondSuccessMock, deleteBlobCase: async () => { throw new Error("delete case failed"); } }); const req = { query: { container: "c1", casefolderID: "f1", hash: "expected" } }; const res = createRes(); await mod.default.handler(req, res); assert.strictEqual(res.state.statusCode, 400); assert.strictEqual( res.state.jsonBody.error.code, "DELETE_BLOB_CASE_FAILED" ); }); test("deleteblobrep handler accepts encoded hash variant", async () => { let deletedRepPath = null; const mod = loadModule("pages/api/file/deleteblobrep.js", { nextConnect: createNextConnectMock(), middleware: () => {}, getSession: async () => ({ user: { id: "c1" } }), hashAPIPath: (queryPath) => queryPath.includes("repfile=Rep%20Name") ? "&hash=expected" : "&hash=other", respondError: respondErrorMock, respondSuccess: respondSuccessMock, deleteBlobRep: async (_container, repPath) => { deletedRepPath = repPath; return { deleted: true }; } }); const req = { query: { container: "c1", casefolderID: "CASE-1", repfile: "Rep Name", hash: "expected" } }; const res = createRes(); await mod.default.handler(req, res); assert.strictEqual(res.state.statusCode, 200); assert.strictEqual(deletedRepPath, "CASE-1/Rep Name"); }); test("deleteblobrep handler dependency failure returns DELETE_BLOB_REP_FAILED", async () => { const mod = loadModule("pages/api/file/deleteblobrep.js", { nextConnect: createNextConnectMock(), middleware: () => {}, getSession: async () => ({ user: { id: "c1" } }), hashAPIPath: () => "&hash=expected", respondError: respondErrorMock, respondSuccess: respondSuccessMock, deleteBlobRep: async () => { throw new Error("delete rep failed"); } }); const req = { query: { container: "c1", casefolderID: "f1", repfile: "rep1", hash: "expected" } }; const res = createRes(); await mod.default.handler(req, res); assert.strictEqual(res.state.statusCode, 400); assert.strictEqual(res.state.jsonBody.error.code, "DELETE_BLOB_REP_FAILED"); }); const ordinaryDeletionCases = [ { name: "deleteblobcase", route: "pages/api/file/deleteblobcase.js", query: { container: "user-container", casefolderID: "CASE-1", hash: "expected" }, dependency: "deleteBlobCase" }, { name: "deleteblobrep", route: "pages/api/file/deleteblobrep.js", query: { container: "user-container", casefolderID: "CASE-1", repfile: "representation.json", hash: "expected" }, dependency: "deleteBlobRep" } ]; for (const deletionCase of ordinaryDeletionCases) { test(`${deletionCase.name} forbids a missing NextAuth session before deletion`, async () => { let deletionCalls = 0; const mod = loadModule(deletionCase.route, { nextConnect: createNextConnectMock(), middleware: () => {}, getSession: async () => null, hashAPIPath: () => "&hash=expected", respondError: respondErrorMock, respondSuccess: respondSuccessMock, [deletionCase.dependency]: async () => { deletionCalls += 1; } }); const res = createRes(); await mod.default.handler({ query: deletionCase.query }, res); assert.strictEqual(res.state.statusCode, 403); assert.strictEqual(res.state.jsonBody.error.code, "FORBIDDEN"); assert.strictEqual(deletionCalls, 0); }); test(`${deletionCase.name} forbids a session without user id before deletion`, async () => { let deletionCalls = 0; const mod = loadModule(deletionCase.route, { nextConnect: createNextConnectMock(), middleware: () => {}, getSession: async () => ({ user: {} }), hashAPIPath: () => "&hash=expected", respondError: respondErrorMock, respondSuccess: respondSuccessMock, [deletionCase.dependency]: async () => { deletionCalls += 1; } }); const res = createRes(); await mod.default.handler({ query: deletionCase.query }, res); assert.strictEqual(res.state.statusCode, 403); assert.strictEqual(deletionCalls, 0); }); test(`${deletionCase.name} forbids cross-container deletion before HMAC or Azure deletion`, async () => { let hashCalls = 0; let deletionCalls = 0; const mod = loadModule(deletionCase.route, { nextConnect: createNextConnectMock(), middleware: () => {}, getSession: async () => ({ user: { id: "different-user" } }), hashAPIPath: () => { hashCalls += 1; return "&hash=expected"; }, respondError: respondErrorMock, respondSuccess: respondSuccessMock, [deletionCase.dependency]: async () => { deletionCalls += 1; } }); const res = createRes(); await mod.default.handler({ query: deletionCase.query }, res); assert.strictEqual(res.state.statusCode, 403); assert.strictEqual(hashCalls, 0); assert.strictEqual(deletionCalls, 0); }); test(`${deletionCase.name} rejects invalid HMAC for the owning user without deletion`, async () => { let deletionCalls = 0; const mod = loadModule(deletionCase.route, { nextConnect: createNextConnectMock(), middleware: () => {}, getSession: async () => ({ user: { id: "user-container" } }), hashAPIPath: () => "&hash=different", respondError: respondErrorMock, respondSuccess: respondSuccessMock, [deletionCase.dependency]: async () => { deletionCalls += 1; } }); const res = createRes(); await mod.default.handler({ query: deletionCase.query }, res); assert.strictEqual(res.state.statusCode, 400); assert.strictEqual(res.state.jsonBody.error.code, "INVALID_HASH"); assert.strictEqual(deletionCalls, 0); }); test(`${deletionCase.name} preserves valid same-user deletion`, async () => { const deletionCalls = []; const mod = loadModule(deletionCase.route, { nextConnect: createNextConnectMock(), middleware: () => {}, getSession: async () => ({ user: { id: "user-container" } }), hashAPIPath: () => "&hash=expected", respondError: respondErrorMock, respondSuccess: respondSuccessMock, [deletionCase.dependency]: async (...args) => { deletionCalls.push(args); return { deleted: true }; } }); const res = createRes(); await mod.default.handler({ query: deletionCase.query }, res); assert.strictEqual(res.state.statusCode, 200); assert.strictEqual(deletionCalls.length, 1); assert.strictEqual(deletionCalls[0][0], "user-container"); }); } const loadAdminDeletion = (overrides = {}) => { let deletionCalls = 0; const mod = loadModule("pages/api/admin/deleteblobrep.js", { deleteBlobRep: async () => { deletionCalls += 1; return { deleted: true }; }, readAdminSessionCookie: () => "admin-token", validateAdminSession: () => true, hasTrustedSameOrigin: () => true, setAdminAuthNoStore: () => {}, respondError: respondErrorMock, respondSuccess: respondSuccessMock, ...overrides }); return { mod, getDeletionCalls: () => deletionCalls }; }; const validAdminDeletionRequest = () => ({ method: "POST", headers: { origin: "https://portal.example.test" }, body: { container: "ck-user-container", casefolderID: "CASE-1", repfile: "representation.json" } }); test("admin representation deletion forbids a missing administrative session", async () => { const loaded = loadAdminDeletion({ readAdminSessionCookie: () => null, validateAdminSession: () => false }); const res = createRes(); await loaded.mod.default(validAdminDeletionRequest(), res); assert.strictEqual(res.state.statusCode, 403); assert.strictEqual(res.state.jsonBody.error.code, "FORBIDDEN"); assert.strictEqual(loaded.getDeletionCalls(), 0); }); test("admin representation deletion forbids an invalid or expired administrative session", async () => { const loaded = loadAdminDeletion({ validateAdminSession: () => false }); const res = createRes(); await loaded.mod.default(validAdminDeletionRequest(), res); assert.strictEqual(res.state.statusCode, 403); assert.strictEqual(loaded.getDeletionCalls(), 0); }); for (const originCase of ["missing", "cross-origin"]) { test(`admin representation deletion forbids ${originCase} Origin`, async () => { const loaded = loadAdminDeletion({ hasTrustedSameOrigin: () => false }); const req = validAdminDeletionRequest(); if (originCase === "missing") delete req.headers.origin; if (originCase === "cross-origin") { req.headers.origin = "https://attacker.example"; } const res = createRes(); await loaded.mod.default(req, res); assert.strictEqual(res.state.statusCode, 403); assert.strictEqual(loaded.getDeletionCalls(), 0); }); } test("ordinary user HMAC cannot authorize administrative representation deletion", async () => { const loaded = loadAdminDeletion({ readAdminSessionCookie: () => null, validateAdminSession: () => false }); const req = validAdminDeletionRequest(); req.body.hash = "ordinary-user-hmac"; const res = createRes(); await loaded.mod.default(req, res); assert.strictEqual(res.state.statusCode, 403); assert.strictEqual(loaded.getDeletionCalls(), 0); }); test("admin representation deletion rejects malformed paths without deletion", async () => { for (const malformedBody of [ { casefolderID: "../CASE-1" }, { repfile: "folder/representation.json" }, { repfile: ".." } ]) { const loaded = loadAdminDeletion(); const req = validAdminDeletionRequest(); req.body = { ...req.body, ...malformedBody }; const res = createRes(); await loaded.mod.default(req, res); assert.strictEqual(res.state.statusCode, 400); assert.strictEqual(res.state.jsonBody.error.code, "INVALID_REQUEST"); assert.strictEqual(loaded.getDeletionCalls(), 0); } }); test("valid administrative session and origin authorize cross-container representation deletion", async () => { const deletionCalls = []; const loaded = loadAdminDeletion({ deleteBlobRep: async (...args) => { deletionCalls.push(args); return { deleted: args[1] }; } }); const res = createRes(); await loaded.mod.default(validAdminDeletionRequest(), res); assert.strictEqual(res.state.statusCode, 200); assert.deepStrictEqual(JSON.parse(JSON.stringify(deletionCalls)), [ ["ck-user-container", "CASE-1/representation.json"] ]); }); test("admin storage caller uses dedicated POST deletion while ordinary caller remains signed GET", () => { const rootDir = path.resolve(__dirname, "..", ".."); const adminSource = fs.readFileSync( path.join(rootDir, "components/admin/tabs/storage.js"), "utf8" ); const ordinarySource = fs.readFileSync( path.join(rootDir, "actions/services/documentDirectService.js"), "utf8" ); assert.match(adminSource, /fetch\("\/api\/admin\/deleteblobrep"/); assert.match(adminSource, /method:\s*"POST"/); assert.doesNotMatch(adminSource, /deleteMyRepresentationsFromBlob/); assert.match( ordinarySource, /buildFileQuery\("\/api\/file\/deleteblobrep"[\s\S]*getSignedFileJson\(queryUrl\)/ ); }); const loadAdminRepJsonEdit = (overrides = {}) => { let azureClientConstructions = 0; const storageCalls = []; const sourceJson = { containerID: "ck-user-container", ticketnumber: "CASE-1", repComplete: true, representationType: "Support", nested: { retained: true } }; const uploaded = {}; class DefaultAzureCredentialMock {} class BlobServiceClientMock { constructor(url, credential) { azureClientConstructions += 1; storageCalls.push(["construct", url, credential]); } getContainerClient(container) { storageCalls.push(["container", container]); return { getBlockBlobClient(blobName) { storageCalls.push(["blob", blobName]); return { async download(offset) { storageCalls.push(["download", offset]); const { Readable } = require("stream"); return { readableStreamBody: Readable.from([ JSON.stringify(sourceJson) ]) }; }, async upload(content, length, options) { uploaded.content = content; uploaded.length = length; uploaded.options = options; storageCalls.push(["upload"]); }, async setTags(tags) { uploaded.tags = tags; storageCalls.push(["tags"]); }, async setMetadata(metadata) { uploaded.metadata = metadata; storageCalls.push(["metadata"]); } }; } }; } } const mod = loadModule("pages/api/admin/editRepJson.js", { Buffer, DefaultAzureCredential: DefaultAzureCredentialMock, BlobServiceClient: BlobServiceClientMock, consoleLogger: () => {}, readAdminSessionCookie: () => "admin-token", validateAdminSession: () => true, hasTrustedSameOrigin: () => true, setAdminAuthNoStore: () => {}, respondError: respondErrorMock, respondSuccess: respondSuccessMock, ...overrides }); return { mod, getAzureClientConstructions: () => azureClientConstructions, storageCalls, sourceJson, uploaded }; }; const validAdminRepJsonEditRequest = () => ({ method: "POST", headers: { origin: "https://portal.example.test" }, body: { container: "ck-user-container", blobName: "CASE-1/representation-1/representation_rep.json" } }); test("admin representation JSON edit forbids a missing administrative session before Azure access", async () => { const loaded = loadAdminRepJsonEdit({ readAdminSessionCookie: () => null, validateAdminSession: () => false }); const res = createRes(); await loaded.mod.default(validAdminRepJsonEditRequest(), res); assert.strictEqual(res.state.statusCode, 403); assert.strictEqual(res.state.jsonBody.error.code, "FORBIDDEN"); assert.strictEqual(loaded.getAzureClientConstructions(), 0); }); test("admin representation JSON edit forbids an invalid administrative session before Azure access", async () => { const loaded = loadAdminRepJsonEdit({ validateAdminSession: () => false }); const res = createRes(); await loaded.mod.default(validAdminRepJsonEditRequest(), res); assert.strictEqual(res.state.statusCode, 403); assert.strictEqual(loaded.getAzureClientConstructions(), 0); }); test("admin representation JSON edit forbids cross-origin requests before Azure access", async () => { const loaded = loadAdminRepJsonEdit({ hasTrustedSameOrigin: () => false }); const req = validAdminRepJsonEditRequest(); req.headers.origin = "https://attacker.example"; const res = createRes(); await loaded.mod.default(req, res); assert.strictEqual(res.state.statusCode, 403); assert.strictEqual(loaded.getAzureClientConstructions(), 0); }); test("admin representation JSON edit rejects unsupported methods before Azure access", async () => { const loaded = loadAdminRepJsonEdit(); const req = validAdminRepJsonEditRequest(); req.method = "GET"; const res = createRes(); await loaded.mod.default(req, res); assert.strictEqual(res.state.statusCode, 405); assert.strictEqual(res.state.jsonBody.error.code, "METHOD_NOT_ALLOWED"); assert.strictEqual(res.state.headers.allow, "POST"); assert.strictEqual(loaded.getAzureClientConstructions(), 0); }); test("admin representation JSON edit rejects malformed identifiers before Azure access", async () => { for (const malformedBody of [ { container: "UPPERCASE" }, { container: "ab" }, { blobName: "../representation_rep.json" }, { blobName: "CASE-1//representation_rep.json" }, { blobName: "CASE-1\\representation_rep.json" }, { blobName: " representation_rep.json" } ]) { const loaded = loadAdminRepJsonEdit(); const req = validAdminRepJsonEditRequest(); req.body = { ...req.body, ...malformedBody }; const res = createRes(); await loaded.mod.default(req, res); assert.strictEqual(res.state.statusCode, 400); assert.strictEqual(res.state.jsonBody.error.code, "INVALID_REQUEST"); assert.strictEqual(loaded.getAzureClientConstructions(), 0); } }); test("valid administrative representation JSON edit preserves the existing mutation contract", async () => { const loaded = loadAdminRepJsonEdit(); const res = createRes(); await loaded.mod.default(validAdminRepJsonEditRequest(), res); assert.strictEqual(res.state.statusCode, 200); assert.deepStrictEqual(JSON.parse(JSON.stringify(res.state.jsonBody)), { message: "repComplete removed successfully" }); assert.strictEqual(loaded.getAzureClientConstructions(), 1); assert.deepStrictEqual(loaded.storageCalls.slice(1, 4), [ ["container", "ck-user-container"], ["blob", "CASE-1/representation-1/representation_rep.json"], ["download", 0] ]); const updatedJson = JSON.parse(loaded.uploaded.content); assert.strictEqual(Object.hasOwn(updatedJson, "repComplete"), false); assert.strictEqual(updatedJson.containerID, loaded.sourceJson.containerID); assert.strictEqual(updatedJson.ticketnumber, loaded.sourceJson.ticketnumber); assert.strictEqual( updatedJson.representationType, loaded.sourceJson.representationType ); assert.deepStrictEqual( JSON.parse(JSON.stringify(updatedJson.nested)), loaded.sourceJson.nested ); assert.strictEqual( loaded.uploaded.length, Buffer.byteLength(loaded.uploaded.content) ); assert.deepStrictEqual( JSON.parse(JSON.stringify(loaded.uploaded.options)), { blobHTTPHeaders: { blobContentType: "application/json" } } ); assert.deepStrictEqual(JSON.parse(JSON.stringify(loaded.uploaded.tags)), { containerid: "ck-user-container", caseID: "CASE-1", blobType: "Representation" }); assert.deepStrictEqual( JSON.parse(JSON.stringify(loaded.uploaded.metadata)), JSON.parse(JSON.stringify(loaded.uploaded.tags)) ); }); test("retired file representation JSON endpoint cannot mutate storage", async () => { let storageCalls = 0; const mod = loadModule("pages/api/file/editRepJson.js", { BlobServiceClient: class { constructor() { storageCalls += 1; } }, respondError: respondErrorMock }); const res = createRes(); await mod.default(validAdminRepJsonEditRequest(), res); assert.strictEqual(res.state.statusCode, 410); assert.strictEqual(res.state.jsonBody.error.code, "ENDPOINT_RETIRED"); assert.strictEqual(storageCalls, 0); }); test("admin storage caller uses the protected representation JSON edit endpoint", () => { const rootDir = path.resolve(__dirname, "..", ".."); const adminSource = fs.readFileSync( path.join(rootDir, "components/admin/tabs/storage.js"), "utf8" ); assert.match(adminSource, /fetch\(\s*"\/api\/admin\/editRepJson"/); assert.doesNotMatch(adminSource, /fetch\(\s*"\/api\/file\/editRepJson"/); }); test("deleteawaitingsubmissionfromblob accepts legacy deleteblob hash variant", async () => { let deletedPath = null; const mod = loadModule( "pages/api/file/deleteawaitingsubmissionfromblob.js", { nextConnect: createNextConnectMock(), middleware: () => {}, hashAPIPath: (queryPath) => queryPath.includes("/api/file/deleteblob?container=c1") ? "&hash=expected" : "&hash=other", respondError: respondErrorMock, respondSuccess: respondSuccessMock, deleteBlob: async (_container, blobPath) => { deletedPath = blobPath; return { deleted: true }; } } ); const req = { query: { container: "c1", casefolderID: "CASE-1", blobname: "CASE-1/files/doc.pdf", hash: "expected" } }; const res = createRes(); await mod.default.handler(req, res); assert.strictEqual(res.state.statusCode, 200); assert.strictEqual(deletedPath, "CASE-1/files/doc.pdf"); }); test("deleteawaitingsubmissionfromblob dependency failure returns DELETE_AWAITING_SUBMISSION_BLOB_FAILED", async () => { const mod = loadModule( "pages/api/file/deleteawaitingsubmissionfromblob.js", { nextConnect: createNextConnectMock(), middleware: () => {}, hashAPIPath: () => "&hash=expected", respondError: respondErrorMock, respondSuccess: respondSuccessMock, deleteBlob: async () => { throw new Error("delete awaiting failed"); } } ); const req = { query: { container: "c1", casefolderID: "CASE-1", blobname: "doc.pdf", hash: "expected" } }; const res = createRes(); await mod.default.handler(req, res); assert.strictEqual(res.state.statusCode, 400); assert.strictEqual( res.state.jsonBody.error.code, "DELETE_AWAITING_SUBMISSION_BLOB_FAILED" ); }); test("getrepsblob handler success returns representations payload", async () => { const mod = loadModule("pages/api/file/getrepsblob.js", { nextConnect: createNextConnectMock(), middleware: () => {}, hashAPIPath: () => "&hash=expected", respondError: respondErrorMock, respondSuccess: respondSuccessMock, consoleLogger: () => {}, getRepsBlobs: async () => [ { path: "c/ref_rep.json", name: "ref_rep.json" } ], downloadAllRepsFiles: async () => ({ "@odata.count": 1, value: [{ id: "r1" }] }) }); const req = { query: { container: "c1", 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)), { "@odata.count": 1, value: [{ id: "r1" }] }); }); test("getrepsblob handler dependency failure returns GET_REPS_BLOB_FAILED", async () => { const mod = loadModule("pages/api/file/getrepsblob.js", { nextConnect: createNextConnectMock(), middleware: () => {}, hashAPIPath: () => "&hash=expected", respondError: respondErrorMock, respondSuccess: respondSuccessMock, consoleLogger: () => {}, getRepsBlobs: async () => { throw new Error("storage failure"); }, downloadAllRepsFiles: async () => ({}) }); const req = { query: { container: "c1", hash: "expected" } }; const res = createRes(); await mod.default.handler(req, res); assert.strictEqual(res.state.statusCode, 400); assert.strictEqual(res.state.jsonBody.error.code, "GET_REPS_BLOB_FAILED"); }); test("getrepsblob handler accepts encoded container hash variant", async () => { const mod = loadModule("pages/api/file/getrepsblob.js", { nextConnect: createNextConnectMock(), middleware: () => {}, hashAPIPath: (queryPath) => queryPath.includes("container=cont%2F1") ? "&hash=expected" : "&hash=other", respondError: respondErrorMock, respondSuccess: respondSuccessMock, consoleLogger: () => {}, getRepsBlobs: async () => [{ path: "cont/rep1.json" }], downloadAllRepsFiles: async () => ({ value: [{ id: "r-encoded" }] }) }); const req = { query: { container: "cont/1", 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)), { value: [{ id: "r-encoded" }] }); }); test("uploadsinglefile handler returns HASH_REQUIRED when hash missing", async () => { const mod = loadModule("pages/api/file/uploadsinglefile.js", { nextConnect: createNextConnectMock(), middleware: () => {}, hashAPIPath: () => "?hash=expected", respondError: respondErrorMock, respondSuccess: respondSuccessMock, consoleLogger: () => {}, uploadSingleFile: async () => ({ uploaded: 0 }), fileTypeFromBuffer: async () => ({ mime: "application/pdf" }), fs: { readFileSync: () => Buffer.from("x") } }); const req = { query: {}, body: {}, files: {} }; const res = createRes(); await mod.default.handler(req, res); assert.strictEqual(res.state.statusCode, 400); assert.strictEqual(res.state.jsonBody.error.code, "HASH_REQUIRED"); }); test("uploadsinglefile handler dependency failure returns UPLOAD_SINGLE_FILE_FAILED", async () => { const mod = loadModule("pages/api/file/uploadsinglefile.js", { nextConnect: createNextConnectMock(), middleware: () => {}, hashAPIPath: () => "?hash=expected", respondError: respondErrorMock, respondSuccess: respondSuccessMock, consoleLogger: () => {}, uploadSingleFile: async () => { throw new Error("upload failed"); }, fileTypeFromBuffer: async () => ({ mime: "application/pdf" }), fs: { readFileSync: () => Buffer.from("x") }, path: { basename: (value) => value } }); const req = { query: { hash: "expected" }, body: { containerID: ["c1"], casefolderID: ["f1"] }, files: { document: [ { fieldName: "document.pdf", originalFilename: "document.pdf", path: "/tmp/document.pdf", size: 1, headers: { "content-type": "application/pdf" } } ] } }; const res = createRes(); await mod.default.handler(req, res); assert.strictEqual(res.state.statusCode, 500); assert.strictEqual( res.state.jsonBody.error.code, "UPLOAD_SINGLE_FILE_FAILED" ); }); test("createrepcompletemessage handler accepts encoded hash variant", async () => { const mod = loadModule("pages/api/file/createrepcompletemessage_api.js", { nextConnect: createNextConnectMock(), middleware: () => {}, hashAPIPath: (queryPath) => queryPath.includes("repid=rep%201") ? "&hash=expected" : "&hash=other", respondError: respondErrorMock, respondSuccess: respondSuccessMock, consoleLogger: () => {}, createRepCompleteMessage: async () => ({ ok: true }) }); const req = { query: { container: "c1", tempcaseref: "TMP/1", repid: "rep 1", 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)), { ok: true }); }); test("createrepcompletemessage handler dependency failure returns CREATE_REP_COMPLETE_MESSAGE_FAILED", async () => { const mod = loadModule("pages/api/file/createrepcompletemessage_api.js", { nextConnect: createNextConnectMock(), middleware: () => {}, hashAPIPath: () => "&hash=expected", respondError: respondErrorMock, respondSuccess: respondSuccessMock, consoleLogger: () => {}, createRepCompleteMessage: async () => { throw new Error("rep message failed"); } }); const req = { query: { container: "c1", tempcaseref: "TMP1", repid: "rep1", 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_REP_COMPLETE_MESSAGE_FAILED" ); }); test("getbloblistproxy success returns proxied payload", async () => { const mod = loadModule("pages/api/file/getbloblistproxy.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, getToken: async () => ({ access_token: "tok" }), azureHeaders: () => ({ headers: { Authorization: "Bearer tok" } }), hashAPIPath: () => "&hash=abc", consoleLogger: () => {}, process: { env: { API_ROOT: "http://example.com" } }, axios: { get: async () => ({ data: { value: [{ id: "b1" }] } }) } }); const req = { query: { container: "cont/1", casefolderID: "CASE/1" } }; const res = createRes(); await mod.default(req, res); assert.strictEqual(res.state.statusCode, 200); assert.deepStrictEqual(JSON.parse(JSON.stringify(res.state.jsonBody)), { value: [{ id: "b1" }] }); }); test("getbloblistproxy dependency failure returns GET_BLOB_LIST_PROXY_FAILED", async () => { const mod = loadModule("pages/api/file/getbloblistproxy.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, getToken: async () => ({ access_token: "tok" }), azureHeaders: () => ({ headers: {} }), hashAPIPath: () => "&hash=abc", consoleLogger: () => {}, process: { env: { API_ROOT: "http://example.com" } }, axios: { get: async () => { throw new Error("proxy failed"); } } }); const req = { query: { container: "c1", casefolderID: "f1" } }; const res = createRes(); await mod.default(req, res); assert.strictEqual(res.state.statusCode, 400); assert.strictEqual( res.state.jsonBody.error.code, "GET_BLOB_LIST_PROXY_FAILED" ); }); test("getrepsblobproxy success returns proxied payload", async () => { const mod = loadModule("pages/api/file/getrepsblobproxy.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, getToken: async () => ({ access_token: "tok" }), azureHeaders: () => ({ headers: {} }), hashAPIPath: () => "&hash=abc", consoleLogger: () => {}, process: { env: { API_ROOT: "http://example.com" } }, axios: { get: async () => ({ data: { value: [{ id: "r1" }] } }) } }); const req = { query: { container: "c1" } }; const res = createRes(); await mod.default(req, res); assert.strictEqual(res.state.statusCode, 200); assert.deepStrictEqual(JSON.parse(JSON.stringify(res.state.jsonBody)), { value: [{ id: "r1" }] }); }); test("getawaitingsubmissionfromblobproxy dependency failure returns GET_AWAITING_SUBMISSION_PROXY_FAILED", async () => { const mod = loadModule( "pages/api/file/getawaitingsubmissionfromblobproxy.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, getToken: async () => ({ access_token: "tok" }), azureHeaders: () => ({ headers: {} }), hashAPIPath: () => "&hash=abc", consoleLogger: () => {}, process: { env: { API_ROOT: "http://example.com" } }, axios: { get: async () => { throw new Error("awaiting failed"); } } } ); const req = { query: { container: "c1" } }; const res = createRes(); await mod.default(req, res); assert.strictEqual(res.state.statusCode, 400); assert.strictEqual( res.state.jsonBody.error.code, "GET_AWAITING_SUBMISSION_PROXY_FAILED" ); }); test("createappealcompletemessageproxy dependency failure returns CREATE_APPEAL_COMPLETE_MESSAGE_PROXY_FAILED", async () => { const mod = loadModule( "pages/api/file/createappealcompletemessageproxy_api.js", { nextConnect: createNextConnectMock(), middleware: () => {}, respondError: respondErrorMock, respondSuccess: respondSuccessMock, getToken: async () => ({ access_token: "tok" }), azureHeaders: () => ({ headers: {} }), hashAPIPath: () => "&hash=abc", consoleLogger: () => {}, process: { env: { API_ROOT: "http://example.com" } }, axios: { get: async () => { throw new Error("create appeal proxy failed"); } } } ); const req = { query: { container: "c1", tempcaseref: "TMP1" } }; 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_PROXY_FAILED" ); }); 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, respondSuccess: respondSuccessMock, getToken: async () => ({ access_token: "tok" }), hashAPIPath: () => "?hash=abc", consoleLogger: () => {}, process: { env: { CRMURL: "crm.example", RELAY_ROOT: "https://relay/" } }, axios: async () => { const error = new Error("exists"); error.status = 412; throw error; } }); const req = { body: { contactid: "c1", incidentid: "i1" } }; const res = createRes(); await mod.default(req, res); assert.strictEqual(res.state.statusCode, 200); assert.deepStrictEqual(JSON.parse(JSON.stringify(res.state.jsonBody)), { record: "exists" }); }); test("createrepinvolvement dependency failure returns CREATE_REP_INVOLVEMENT_FAILED", async () => { const mod = loadModule("pages/api/file/createrepinvolvement_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, getToken: async () => ({ access_token: "tok" }), getPersonalAccount: async () => ({}), hashAPIPath: () => "?hash=abc", consoleLogger: () => {}, process: { env: { CRMURL: "crm.example", CRMURL_VERSION: "v9.2", RELAY_ROOT: "https://relay/" } }, axios: async () => { throw new Error("rep involvement failed"); } }); const req = { body: { contactid: "c1", incidentid: "i1" } }; const res = createRes(); await mod.default(req, res); assert.strictEqual(res.state.statusCode, 400); assert.strictEqual( res.state.jsonBody.error.code, "CREATE_REP_INVOLVEMENT_FAILED" ); }); test("updatecase dependency failure returns UPDATE_CASE_FAILED", async () => { const mod = loadModule("pages/api/file/updatecase_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, getToken: async () => ({ access_token: "tok" }), hashAPIPath: () => "?hash=abc", consoleLogger: () => {}, process: { env: { RELAY_ROOT: "https://relay/" } }, axios: async () => { throw new Error("update failed"); } }); const req = { query: { updateFormCollection: "pinswg_forms", appealObj: "obj1", incident: "inc1" }, body: { field: "value" } }; const res = createRes(); await mod.default(req, res); assert.strictEqual(res.state.statusCode, 400); assert.strictEqual(res.state.jsonBody.error.code, "UPDATE_CASE_FAILED"); }); test("getawaitingsubmissionfromblob handler success returns payload", async () => { const mod = loadModule("pages/api/file/getawaitingsubmissionfromblob.js", { nextConnect: createNextConnectMock(), middleware: () => {}, hashAPIPath: () => "&hash=expected", respondError: respondErrorMock, respondSuccess: respondSuccessMock, getAllProgressBlobs: async () => [{ path: "c1/case1_appeal.json" }], downloadAllProgressFiles: async () => ({ value: [{ id: "a1" }] }) }); const req = { query: { container: "c1", 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)), { value: [{ id: "a1" }] }); }); test("getawaitingsubmissionfromblob handler dependency failure returns GET_AWAITING_SUBMISSION_BLOB_FAILED", async () => { const mod = loadModule("pages/api/file/getawaitingsubmissionfromblob.js", { nextConnect: createNextConnectMock(), middleware: () => {}, hashAPIPath: () => "&hash=expected", respondError: respondErrorMock, respondSuccess: respondSuccessMock, getAllProgressBlobs: async () => { throw new Error("progress fetch failed"); }, downloadAllProgressFiles: async () => ({}) }); const req = { query: { container: "c1", hash: "expected" } }; const res = createRes(); await mod.default.handler(req, res); assert.strictEqual(res.state.statusCode, 400); assert.strictEqual( res.state.jsonBody.error.code, "GET_AWAITING_SUBMISSION_BLOB_FAILED" ); }); test("getbloblist handler returns INVALID_HASH for mismatch", async () => { const mod = loadModule("pages/api/file/getbloblist.js", { nextConnect: createNextConnectMock(), middleware: () => {}, hashAPIPath: () => "&hash=expected", respondError: respondErrorMock, respondSuccess: respondSuccessMock, getBlobs: async () => ({ id: "x" }), getRepsFilesBlobs: async () => ({ id: "x" }) }); const req = { query: { container: "c1", casefolderID: "f1", hash: "wrong" } }; const res = createRes(); await mod.default.handler(req, res); assert.strictEqual(res.state.statusCode, 400); assert.strictEqual(res.state.jsonBody.error.code, "INVALID_HASH"); }); test("getbloblist handler accepts encoded casefolder hash variant", async () => { const mod = loadModule("pages/api/file/getbloblist.js", { nextConnect: createNextConnectMock(), middleware: () => {}, hashAPIPath: (queryPath) => queryPath.includes("casefolderID=CASE%2FSUB") ? "&hash=expected" : "&hash=other", respondError: respondErrorMock, respondSuccess: respondSuccessMock, getBlobs: async () => ({ file: "a.pdf" }), getRepsFilesBlobs: async () => ({ file: "rep.pdf" }) }); const req = { query: { container: "c1", casefolderID: "CASE/SUB", 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)), { value: [{ file: "rep.pdf" }] }); }); test("getbloblist handler dependency failure returns GET_BLOB_LIST_FAILED", async () => { const mod = loadModule("pages/api/file/getbloblist.js", { nextConnect: createNextConnectMock(), middleware: () => {}, hashAPIPath: () => "&hash=expected", respondError: respondErrorMock, respondSuccess: respondSuccessMock, getBlobs: async () => { throw new Error("blob lookup failed"); }, getRepsFilesBlobs: async () => ({ file: "rep.pdf" }) }); const req = { query: { container: "c1", casefolderID: "f1", hash: "expected" } }; const res = createRes(); await mod.default.handler(req, res); assert.strictEqual(res.state.statusCode, 400); assert.strictEqual(res.state.jsonBody.error.code, "GET_BLOB_LIST_FAILED"); }); test("getprogressobjblob handler success returns payload", async () => { const mod = loadModule("pages/api/file/getprogressobjblob.js", { nextConnect: createNextConnectMock(), middleware: () => {}, hashAPIPath: () => "&hash=expected", respondError: respondErrorMock, respondSuccess: respondSuccessMock, getProgressBlobs: async () => ({ path: "CASE1/CASE1_appeal.json" }), downloadProgressFile: async () => ({ id: "progress-1" }) }); const req = { query: { container: "c1", casefolderID: "CASE1", 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)), { id: "progress-1" }); }); test("getprogressobjblob handler accepts encoded casefolder hash variant", async () => { const mod = loadModule("pages/api/file/getprogressobjblob.js", { nextConnect: createNextConnectMock(), middleware: () => {}, hashAPIPath: (queryPath) => queryPath.includes("casefolderID=CASE%2FSUB") ? "&hash=expected" : "&hash=other", respondError: respondErrorMock, respondSuccess: respondSuccessMock, getProgressBlobs: async () => ({ path: "CASE/SUB/CASE_appeal.json" }), downloadProgressFile: async () => ({ id: "progress-2" }) }); const req = { query: { container: "c1", casefolderID: "CASE/SUB", 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)), { id: "progress-2" }); }); test("getprogressobjblob handler dependency failure returns GET_PROGRESS_OBJ_BLOB_FAILED", async () => { const mod = loadModule("pages/api/file/getprogressobjblob.js", { nextConnect: createNextConnectMock(), middleware: () => {}, hashAPIPath: () => "&hash=expected", respondError: respondErrorMock, respondSuccess: respondSuccessMock, getProgressBlobs: async () => { throw new Error("progress lookup failed"); }, downloadProgressFile: async () => ({}) }); const req = { query: { container: "c1", casefolderID: "CASE1", hash: "expected" } }; const res = createRes(); await mod.default.handler(req, res); assert.strictEqual(res.state.statusCode, 400); assert.strictEqual( res.state.jsonBody.error.code, "GET_PROGRESS_OBJ_BLOB_FAILED" ); }); test("setupcontainer handler returns MISSING_REQUIRED_QUERY when ident missing", async () => { const mod = loadModule("pages/api/file/setupcontainer.js", { nextConnect: createNextConnectMock(), middleware: () => {}, hashAPIPath: () => "&hash=expected", consoleLogger: () => {}, respondError: respondErrorMock, respondSuccess: respondSuccessMock, createContainer: async () => ({ ok: true }) }); const req = { query: { hash: "expected" } }; const res = createRes(); await mod.default.handler(req, res); assert.strictEqual(res.state.statusCode, 400); assert.strictEqual(res.state.jsonBody.error.code, "MISSING_REQUIRED_QUERY"); }); test("setupcontainer handler accepts encoded ident hash variant", async () => { const mod = loadModule("pages/api/file/setupcontainer.js", { nextConnect: createNextConnectMock(), middleware: () => {}, hashAPIPath: (queryPath) => queryPath.includes("ident=container%2Fname") ? "&hash=expected" : "&hash=other", consoleLogger: () => {}, respondError: respondErrorMock, respondSuccess: respondSuccessMock, createContainer: async () => ({ created: true }) }); const req = { query: { ident: "container/name", 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)), { data: "success", output: { created: true } }); }); test("upload handler success returns data payload with 200", async () => { const mod = loadModule("pages/api/file/upload.js", { nextConnect: createNextConnectMock(), middleware: () => {}, hashAPIPath: () => "?hash=expected", respondError: respondErrorMock, respondSuccess: respondSuccessMock, createBlob: async () => ({ id: "blob-1" }), createRepBlob: async () => ({ id: "rep-1" }) }); const req = { query: { hash: "expected" }, body: { appealData: { key: "value" }, containerID: ["c1"], casefolderID: ["f1"], repOrAppeal: false } }; const res = createRes(); await mod.default.handler(req, res); assert.strictEqual(res.state.statusCode, 200); assert.deepStrictEqual(JSON.parse(JSON.stringify(res.state.jsonBody)), { data: { id: "blob-1" } }); }); test("setupcontainer handler success returns expected contract", async () => { const mod = loadModule("pages/api/file/setupcontainer.js", { nextConnect: createNextConnectMock(), middleware: () => {}, hashAPIPath: () => "&hash=expected", consoleLogger: () => {}, respondError: respondErrorMock, respondSuccess: respondSuccessMock, createContainer: async () => ({ created: true }) }); const req = { query: { ident: "cont-1", 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)), { data: "success", output: { created: true } }); }); test("getbloblist handler success returns value array contract", async () => { const mod = loadModule("pages/api/file/getbloblist.js", { nextConnect: createNextConnectMock(), middleware: () => {}, hashAPIPath: () => "&hash=expected", respondError: respondErrorMock, respondSuccess: respondSuccessMock, getBlobs: async () => ({ file: "a.pdf" }), getRepsFilesBlobs: async () => ({ file: "rep.pdf" }) }); const req = { query: { container: "c1", casefolderID: "f1", 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)), { value: [{ file: "a.pdf" }] }); }); test("downloadblob handler success sets attachment header and returns body", async () => { const mod = loadModule("pages/api/file/downloadblob.js", { nextConnect: createNextConnectMock(), middleware: () => {}, hashAPIPath: () => "&hash=expected", respondError: respondErrorMock, downloadFile: async () => Buffer.from("file-content") }); const req = { query: { container: "c1", casefolderID: "f1", blobname: "doc.pdf", hash: "expected" } }; const res = createRes(); await mod.default.handler(req, res); assert.strictEqual(res.state.statusCode, 200); assert.strictEqual( res.state.headers["content-disposition"], "attachment; filename=doc.pdf" ); assert.strictEqual(res.state.sentBody.toString(), "file-content"); }); test("upload handler dependency failure returns UPLOAD_FAILED", async () => { const mod = loadModule("pages/api/file/upload.js", { nextConnect: createNextConnectMock(), middleware: () => {}, hashAPIPath: () => "?hash=expected", respondError: respondErrorMock, respondSuccess: respondSuccessMock, createBlob: async () => { throw new Error("storage down"); }, createRepBlob: async () => ({ id: "rep-1" }) }); const req = { query: { hash: "expected" }, body: { appealData: { key: "value" }, containerID: ["c1"], casefolderID: ["f1"], repOrAppeal: false } }; const res = createRes(); await mod.default.handler(req, res); assert.strictEqual(res.state.statusCode, 400); assert.strictEqual(res.state.jsonBody.error.code, "UPLOAD_FAILED"); }); test("setupcontainer dependency failure returns SETUP_CONTAINER_FAILED", async () => { const mod = loadModule("pages/api/file/setupcontainer.js", { nextConnect: createNextConnectMock(), middleware: () => {}, hashAPIPath: () => "&hash=expected", consoleLogger: () => {}, respondError: respondErrorMock, respondSuccess: respondSuccessMock, createContainer: async () => { throw new Error("container failed"); } }); const req = { query: { ident: "cont-1", hash: "expected" } }; const res = createRes(); await mod.default.handler(req, res); assert.strictEqual(res.state.statusCode, 400); assert.strictEqual(res.state.jsonBody.error.code, "SETUP_CONTAINER_FAILED"); }); test("downloadblob dependency failure returns DOWNLOAD_BLOB_FAILED", async () => { const mod = loadModule("pages/api/file/downloadblob.js", { nextConnect: createNextConnectMock(), middleware: () => {}, hashAPIPath: () => "&hash=expected", respondError: respondErrorMock, downloadFile: async () => { throw new Error("download failed"); } }); const req = { query: { container: "c1", casefolderID: "f1", blobname: "doc.pdf", hash: "expected" } }; const res = createRes(); await mod.default.handler(req, res); assert.strictEqual(res.state.statusCode, 400); assert.strictEqual(res.state.jsonBody.error.code, "DOWNLOAD_BLOB_FAILED"); }); test("generateappealpdfcopy returns INCIDENT_ID_REQUIRED when docProps missing", async () => { const mod = loadModule("pages/api/file/generateappealpdfcopy.js", { respondError: respondErrorMock, getCaseByID: async () => [], getPortalModuleDetails: async () => ({ value: [{}] }), getAppealPDFDocs: async () => [], getFormCollectionByID: () => ({ UrlName: "planningappeals78" }), getPickLists: async () => ({}), planningappeals78_pdf: () => ({}), other_pdf: () => ({}), pdf: () => ({ toBuffer: async () => Buffer.from("pdf") }) }); const req = { body: {} }; const res = createRes(); await mod.default(req, res); assert.strictEqual(res.state.statusCode, 400); assert.strictEqual(res.state.jsonBody.error.code, "INCIDENT_ID_REQUIRED"); }); test("generateappealpdfcopy returns CASE_NOT_FOUND when case lookup is empty", async () => { const mod = loadModule("pages/api/file/generateappealpdfcopy.js", { respondError: respondErrorMock, getCaseByID: async () => [], getPortalModuleDetails: async () => ({ value: [{}] }), getAppealPDFDocs: async () => [], getFormCollectionByID: () => ({ UrlName: "planningappeals78" }), getPickLists: async () => ({}), planningappeals78_pdf: () => ({}), other_pdf: () => ({}), pdf: () => ({ toBuffer: async () => Buffer.from("pdf") }) }); const req = { body: { docProps: "i1" } }; const res = createRes(); await mod.default(req, res); assert.strictEqual(res.state.statusCode, 404); assert.strictEqual(res.state.jsonBody.error.code, "CASE_NOT_FOUND"); }); test("generateappealpdfcopy returns FORM_COLLECTION_NOT_FOUND when collection is unresolved", async () => { const mod = loadModule("pages/api/file/generateappealpdfcopy.js", { respondError: respondErrorMock, getCaseByID: async () => [ { pinswg_appealcasetype: 846040000, ticketnumber: "CAS-1", description: "desc" } ], getPortalModuleDetails: async () => ({ value: [{}] }), getAppealPDFDocs: async () => [], getFormCollectionByID: () => null, getPickLists: async () => ({}), planningappeals78_pdf: () => ({}), other_pdf: () => ({}), pdf: () => ({ toBuffer: async () => Buffer.from("pdf") }) }); const req = { body: { docProps: "i1" } }; const res = createRes(); await mod.default(req, res); assert.strictEqual(res.state.statusCode, 400); assert.strictEqual( res.state.jsonBody.error.code, "FORM_COLLECTION_NOT_FOUND" ); }); test("generateappealpdfcopy success returns pdf buffer and headers", async () => { const mod = loadModule("pages/api/file/generateappealpdfcopy.js", { respondError: respondErrorMock, getCaseByID: async () => [ { pinswg_appealcasetype: 846040000, ticketnumber: "CAS-1", description: "desc" } ], getPortalModuleDetails: async () => ({ value: [{}] }), getAppealPDFDocs: async () => [{ id: "d1" }], getFormCollectionByID: () => ({ UrlName: "planningappeals78", LogicalCollectionName: "pinswg_planningappeals78s" }), getPickLists: async () => ({ a: 1 }), planningappeals78_pdf: () => ({ type: "pdfComponent" }), other_pdf: () => ({ type: "otherComponent" }), pdf: () => ({ toBuffer: async () => Buffer.from("pdf-content") }) }); const req = { body: { docProps: "i1" } }; const res = createRes(); await mod.default(req, res); assert.strictEqual(res.state.headers["content-type"], "application/pdf"); assert.strictEqual( typeof res.state.headers["content-disposition"], "string" ); assert.strictEqual(res.state.sentBody.toString(), "pdf-content"); }); test("generateappealpdfcopy catch path returns APPEAL_PDF_COPY_GENERATION_FAILED", async () => { const mod = loadModule("pages/api/file/generateappealpdfcopy.js", { respondError: respondErrorMock, getCaseByID: async () => [ { pinswg_appealcasetype: 846040000, ticketnumber: "CAS-1", description: "desc" } ], getPortalModuleDetails: async () => ({ value: [{}] }), getAppealPDFDocs: async () => [{ id: "d1" }], getFormCollectionByID: () => ({ UrlName: "planningappeals78", LogicalCollectionName: "pinswg_planningappeals78s" }), getPickLists: async () => ({ a: 1 }), planningappeals78_pdf: () => ({ type: "pdfComponent" }), other_pdf: () => ({ type: "otherComponent" }), pdf: () => ({ toBuffer: async () => { throw new Error("pdf failure"); } }) }); const req = { body: { docProps: "i1" } }; const res = createRes(); await mod.default(req, res); assert.strictEqual(res.state.statusCode, 400); assert.strictEqual( res.state.jsonBody.error.code, "APPEAL_PDF_COPY_GENERATION_FAILED" ); }); test("generatepdf catch path returns GENERATE_PDF_FAILED", async () => { const { Readable } = require("stream"); const mod = loadModule("pages/api/file/generatepdf.js", { Buffer, respondError: respondErrorMock, respondSuccess: respondSuccessMock, hashAPIPath: () => "&hash=expected", consoleLogger: () => {}, ReactPDF: { renderToStream: async () => Readable.from(["pdf"]) }, createRepPDFBlob: async () => { throw new Error("pdf write failed"); }, other_pdf: () => ({ type: "other" }) }); const req = { query: { hash: "expected", download: "true" }, body: { representationType: "Other", repfile_name: "rep-file", containerID: "c1", casefolderID: "f1", caseRef: "CAS-1" } }; const res = createRes(); await mod.default(req, res); assert.strictEqual(res.state.statusCode, 400); assert.strictEqual(res.state.jsonBody.error.code, "GENERATE_PDF_FAILED"); }); test("generateappealpdf catch path returns GENERATE_APPEAL_PDF_FAILED", async () => { const mod = loadModule("pages/api/file/generateappealpdf.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, hashAPIPath: () => "&hash=expected", getTempCaseBlob: async () => ({ ticketnumber: "CAS-1" }), getProgressBlobs: async () => ({ path: "CAS-1/progress.json" }), downloadProgressFile: async () => { throw new Error("progress failed"); }, getPickLists: async () => ({}), planningappeals78_pdf: () => ({ type: "planning" }), other_pdf: () => ({ type: "other" }), ReactPDF: { renderToStream: async () => { throw new Error("render failed"); } } }); const req = { query: { hash: "expected", appealType: "846040000" }, body: { containerID: "c1", casefolderID: "f1", caseRef: "CAS-1", filesList: [] } }; const res = createRes(); await mod.default(req, res); assert.strictEqual(res.state.statusCode, 400); assert.strictEqual( res.state.jsonBody.error.code, "GENERATE_APPEAL_PDF_FAILED" ); }); const run = async () => { let passed = 0; for (const currentTest of tests) { await currentTest.fn(); passed += 1; } console.log( `Phase 21 file-handler contract tests passed (${passed}/${tests.length}).` ); }; module.exports = run; if (require.main === module) { run().catch((error) => { console.error(error); process.exit(1); }); }