const fs = require("fs"); const path = require("path"); const vm = require("vm"); const assert = require("assert"); const rootDir = path.resolve(__dirname, "..", ".."); const servicesDir = path.join(rootDir, "actions", "services"); const createAxiosMock = () => { const axios = (config) => axios.request(config); axios.calls = []; axios.requestHandler = async () => { throw new Error("No axios.request handler configured"); }; axios.getHandler = async () => { throw new Error("No axios.get handler configured"); }; axios.postHandler = async () => { throw new Error("No axios.post handler configured"); }; axios.request = (config) => { axios.calls.push({ type: "request", config }); return axios.requestHandler(config); }; axios.get = (url, config) => { axios.calls.push({ type: "get", url, config }); return axios.getHandler(url, config); }; axios.post = (url, data, config) => { axios.calls.push({ type: "post", url, data, config }); return axios.postHandler(url, data, config); }; return axios; }; const createLoggerMock = () => { const calls = []; const consoleLogger = (error) => { calls.push(error); }; return { consoleLogger, calls }; }; const createAxiosError = (status = 500, statusText = "Server Error") => { return { response: { status, statusText, data: {} }, config: { url: "/mock-url" } }; }; const loadServiceModule = (fileName, injected = {}) => { const filePath = path.join(servicesDir, fileName); let source = fs.readFileSync(filePath, "utf8"); source = source.replace(/import[\s\S]*?from\s+"[^"]+";\n?/g, ""); const exportNames = Array.from( source.matchAll(/export const\s+(\w+)\s*=/g) ).map((match) => match[1]); source = source.replace(/export const\s+/g, "const "); source += `\nmodule.exports = { ${exportNames.join(", ")} };\n`; const context = { module: { exports: {} }, exports: {}, require, URLSearchParams, encodeURIComponent, FormData: global.FormData, console: { log: () => {}, info: () => {}, warn: () => {}, error: () => {} }, ...injected }; vm.runInNewContext(source, context, { filename: filePath }); return context.module.exports; }; const tests = []; const test = (name, fn) => { tests.push({ name, fn }); }; const normalize = (value) => JSON.parse(JSON.stringify(value)); test("search/getBasicSearch returns res.data on success", async () => { const axios = createAxiosMock(); const logger = createLoggerMock(); const helpers = loadServiceModule("httpServiceUtils.js", { consoleLogger: logger.consoleLogger }); axios.getHandler = async () => ({ data: { value: ["ok"] } }); const search = loadServiceModule("searchDirectService.js", { axios, BASE_URL: "http://example.local", consoleLogger: logger.consoleLogger, ...helpers }); const result = await search.getBasicSearch("abc"); assert.deepStrictEqual(normalize(result), { value: ["ok"] }); assert.strictEqual( axios.calls[0].url, "http://example.local/api/endpoint/getbasicsearch_api?searchString=abc" ); }); test("search/getBasicSearch returns error.response on failure", async () => { const axios = createAxiosMock(); const logger = createLoggerMock(); const helpers = loadServiceModule("httpServiceUtils.js", { consoleLogger: logger.consoleLogger }); const error = createAxiosError(503, "Unavailable"); axios.getHandler = async () => Promise.reject(error); const search = loadServiceModule("searchDirectService.js", { axios, BASE_URL: "", consoleLogger: logger.consoleLogger, ...helpers }); const result = await search.getBasicSearch("abc"); assert.strictEqual(result, error.response); assert.strictEqual(logger.calls.length, 1); }); test("search/getAdvancedSearch preserves ErrResponse shape on failure", async () => { const axios = createAxiosMock(); const logger = createLoggerMock(); const helpers = loadServiceModule("httpServiceUtils.js", { consoleLogger: logger.consoleLogger }); axios.getHandler = async () => Promise.reject(createAxiosError(400, "Bad Request")); const search = loadServiceModule("searchDirectService.js", { axios, BASE_URL: "", consoleLogger: logger.consoleLogger, ...helpers }); const result = await search.getAdvancedSearch({ foo: "bar" }); assert.deepStrictEqual(normalize(result), { value: [], errorCode: 400, errorMsg: "Bad Request" }); assert.strictEqual(logger.calls.length, 1); }); test("reference/getAppealsTypes preserves ErrResponse shape on failure", async () => { const axios = createAxiosMock(); const logger = createLoggerMock(); const helpers = loadServiceModule("httpServiceUtils.js", { consoleLogger: logger.consoleLogger }); axios.getHandler = async () => Promise.reject(createAxiosError(404, "Not Found")); const reference = loadServiceModule("referenceDataDirectService.js", { axios, BASE_URL: "", consoleLogger: logger.consoleLogger, ...helpers }); const result = await reference.getAppealsTypes(); assert.deepStrictEqual(normalize(result), { value: [], errorCode: 404, errorMsg: "Not Found" }); assert.strictEqual(logger.calls.length, 1); }); test("case/getCaseMessage returns error.response on failure", async () => { const axios = createAxiosMock(); const logger = createLoggerMock(); const helpers = loadServiceModule("httpServiceUtils.js", { consoleLogger: logger.consoleLogger }); const error = createAxiosError(500, "Error"); axios.getHandler = async () => Promise.reject(error); const caseService = loadServiceModule("caseDirectService.js", { axios, BASE_URL: "", consoleLogger: logger.consoleLogger, ...helpers }); const result = await caseService.getCaseMessage("x"); assert.strictEqual(result, error.response); assert.strictEqual(logger.calls.length, 1); }); test("admin/getNewAppealsPage returns res.data on success", async () => { const axios = createAxiosMock(); const logger = createLoggerMock(); const helpers = loadServiceModule("httpServiceUtils.js", { consoleLogger: logger.consoleLogger }); axios.getHandler = async () => ({ data: { value: [1, 2] } }); const admin = loadServiceModule("adminDirectService.js", { axios, BASE_URL: "", ...helpers }); const result = await admin.getNewAppealsPage("q", 1, "a", "b", 20); assert.deepStrictEqual(normalize(result), { value: [1, 2] }); assert.strictEqual(logger.calls.length, 0); }); test("admin/getNewAppealsPage returns error.response on failure", async () => { const axios = createAxiosMock(); const logger = createLoggerMock(); const helpers = loadServiceModule("httpServiceUtils.js", { consoleLogger: logger.consoleLogger }); const error = createAxiosError(502, "Bad Gateway"); axios.getHandler = async () => Promise.reject(error); const admin = loadServiceModule("adminDirectService.js", { axios, BASE_URL: "", ...helpers }); const result = await admin.getNewAppealsPage("q", 1, "a", "b", 20); assert.strictEqual(result, error.response); assert.strictEqual(logger.calls.length, 1); }); test("admin/getNewAppeals returns error.response on failure", async () => { const axios = createAxiosMock(); const logger = createLoggerMock(); const helpers = loadServiceModule("httpServiceUtils.js", { consoleLogger: logger.consoleLogger }); const error = createAxiosError(401, "Unauthorized"); axios.getHandler = async () => Promise.reject(error); const admin = loadServiceModule("adminDirectService.js", { axios, BASE_URL: "", ...helpers }); const result = await admin.getNewAppeals("ignored"); assert.strictEqual(result, error.response); assert.strictEqual(logger.calls.length, 1); }); const run = async () => { let passed = 0; for (const currentTest of tests) { await currentTest.fn(); passed += 1; } console.log( `Phase 6 behavioural tests passed (${passed}/${tests.length}).` ); }; run().catch((error) => { console.error(error); process.exit(1); });