const fs = require("fs"); const path = require("path"); const vm = require("vm"); const assert = require("assert"); const rootDir = path.resolve(__dirname, "..", ".."); const loadModule = (relativePath, injected = {}) => { const filePath = path.join(rootDir, relativePath); let source = fs.readFileSync(filePath, "utf8"); source = source.replace(/import[\s\S]*?from\s+"[^"]+";\n?/g, ""); source = source.replace( /export default async function\s+(\w+)\s*\(/, "async function $1(" ); source = source.replace(/export const\s+/g, "const "); source = source.replace( /export default\s+(\w+);/g, "module.exports.default = $1;" ); source += '\nif (typeof ApiProxy !== "undefined" && !module.exports.default) module.exports.default = ApiProxy;\n'; const context = { module: { exports: {} }, exports: {}, require, process, console: { log: () => {}, info: () => {}, warn: () => {}, error: () => {} }, ...injected }; vm.runInNewContext(source, context, { filename: filePath }); return context.module.exports; }; const createNextConnectMock = () => { const router = { handler: null, use: () => {}, get(fn) { this.handler = fn; } }; return () => router; }; const createRes = () => { const state = { statusCode: null, jsonBody: undefined }; return { state, status(code) { state.statusCode = code; return this; }, json(payload) { state.jsonBody = payload; return payload; } }; }; const tests = []; const test = (name, fn) => tests.push({ name, fn }); test("deleteblobcase rejects missing hash with 400", async () => { const calls = []; const nextConnect = createNextConnectMock(); const mod = loadModule("pages/api/file/deleteblobcase.js", { hashAPIPath: () => "&hash=good", deleteBlobCase: async (...args) => { calls.push(args); return { ok: true }; }, nextConnect, middleware: () => {} }); const req = { query: { container: "c1", casefolderID: "case-1" } }; const res = createRes(); await mod.default.handler(req, res); assert.strictEqual(res.state.statusCode, 400); assert.strictEqual(calls.length, 0); }); test("deleteblobrep rejects missing repfile with 400", async () => { const calls = []; const nextConnect = createNextConnectMock(); const mod = loadModule("pages/api/file/deleteblobrep.js", { hashAPIPath: () => "&hash=good", deleteBlobRep: async (...args) => { calls.push(args); return { ok: true }; }, nextConnect, middleware: () => {} }); const req = { query: { container: "c1", casefolderID: "case-1", hash: "good" } }; const res = createRes(); await mod.default.handler(req, res); assert.strictEqual(res.state.statusCode, 400); assert.strictEqual(calls.length, 0); }); test("createappealcompletemessage_api rejects invalid hash with 400", async () => { const calls = []; const nextConnect = createNextConnectMock(); const mod = loadModule( "pages/api/file/createappealcompletemessage_api.js", { hashAPIPath: () => "&hash=expected", consoleLogger: () => {}, getProgressBlobs: async () => { calls.push("getProgressBlobs"); return { path: "x" }; }, downloadProgressFile: async () => ({}), createBlob: async () => {}, getCaseBlob: async () => {}, createCaseCompleteMessage: () => {}, updateAccount: async () => {}, _: { isEmpty: (value) => !value }, nextConnect, middleware: () => {} } ); const req = { query: { container: "c1", tempcaseref: "temp-1", inv: "846040001", hash: "wrong" } }; const res = createRes(); await mod.default.handler(req, res); assert.strictEqual(res.state.statusCode, 400); assert.strictEqual(calls.length, 0); }); test("getportallogin_api rejects invalid hash with 400", async () => { const tokenCalls = []; const mod = loadModule("pages/api/endpoint/getportallogin_api.js", { CryptoJS: { HmacSHA256: () => ({ toString: () => "hashed" }), enc: { Hex: { parse: () => "parsed", toString: () => "" } } }, axios: { get: async () => ({ data: { value: [] } }) }, azureHeadersPaged: () => ({ headers: {} }), consoleLogger: () => {}, getToken: async () => { tokenCalls.push(true); return { access_token: "token" }; }, process: { env: { HASHKEY: "00", RELAY_ROOT: "http://relay/" } } }); const req = { query: { emailAddress: "person@example.com", hash: "wrong" } }; const res = createRes(); await mod.default(req, res); assert.strictEqual(res.state.statusCode, 400); assert.strictEqual(tokenCalls.length, 0); }); test("getportallogin_api happy path returns 200 and payload", async () => { const axiosCalls = []; const mod = loadModule("pages/api/endpoint/getportallogin_api.js", { CryptoJS: { HmacSHA256: () => ({ toString: () => "hashed" }), enc: { Hex: { parse: () => "parsed", toString: () => "" } } }, axios: { get: async (url, config) => { axiosCalls.push({ url, config }); return { data: { value: [{ id: "user-1" }] } }; } }, azureHeadersPaged: (token) => ({ token }), consoleLogger: () => {}, getToken: async () => ({ access_token: "token-1" }), process: { env: { HASHKEY: "00", RELAY_ROOT: "http://relay/" } } }); const req = { query: { emailAddress: "person@example.com", hash: "hashed" } }; 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: "user-1" }] }); assert.strictEqual(axiosCalls.length, 1); }); const run = async () => { let passed = 0; for (const currentTest of tests) { await currentTest.fn(); passed += 1; } console.log( `Phase 8 behavioural tests passed (${passed}/${tests.length}).` ); }; run().catch((error) => { console.error(error); process.exit(1); });