Files
pedwfrontend/tests/phase21/admin-login-integration.test.cjs
T

284 lines
9.3 KiB
JavaScript

const assert = require("assert");
const fs = require("fs");
const path = require("path");
const vm = require("vm");
const rootDir = path.resolve(__dirname, "..", "..");
const tests = [];
const test = (name, fn) => tests.push({ name, fn });
const readSource = (relativePath) =>
fs.readFileSync(path.join(rootDir, relativePath), "utf8");
const loadAdminPageLoader = (overrides = {}) => {
const filePath = path.join(rootDir, "pages", "admin", "index.js");
let source = fs.readFileSync(filePath, "utf8");
source = source.replace(/import[\s\S]*?from\s+"[^"]+";\n?/g, "");
source = source.slice(source.indexOf("export const getServerSideProps"));
source = source.replace(
/export\s+const\s+getServerSideProps\s*=\s*/,
"const getServerSideProps = "
);
source = source.replace(/const mapDispatchToProps[\s\S]*$/, "");
source += "\nmodule.exports = { getServerSideProps };\n";
const context = {
module: { exports: {} },
exports: {},
wrapper: {
getServerSideProps: (factory) => async (ctx) => {
const store = {
dispatch: () => {},
getState: () => ({})
};
return factory(store)(ctx);
}
},
readAdminSessionCookie: () => "valid-token",
validateAdminSession: () => true,
fetchAdminStorageData: async () => ({
data: [],
userData: [],
repCompleteCount: 0
}),
getNewAppeals: async () => ({ value: [] }),
getStatusByAppeal: async () => [],
getStatusByAppealLPA: async () => [],
getSearchDetails: async () => ({}),
setSearchResults: () => ({}),
setSearchDetails: () => ({}),
setCurrentPage: () => ({}),
process: {
env: {
ALLOWED_IPS: "203.0.113.10"
}
},
...overrides
};
vm.runInNewContext(source, context, { filename: filePath });
return context.module.exports;
};
const loadAdminLoginLoader = (overrides = {}) => {
const filePath = path.join(rootDir, "pages", "admin", "login.js");
let source = fs.readFileSync(filePath, "utf8");
source = source.replace(/import[\s\S]*?from\s+"[^"]+";\n?/g, "");
source = source.slice(source.indexOf("export const getServerSideProps"));
source = source.replace(
/export\s+const\s+getServerSideProps\s*=\s*/,
"const getServerSideProps = "
);
source = source.replace(/export default[\s\S]*$/, "");
source += "\nmodule.exports = { getServerSideProps };\n";
const context = {
module: { exports: {} },
exports: {},
readAdminSessionCookie: () => null,
validateAdminSession: () => false,
...overrides
};
vm.runInNewContext(source, context, { filename: filePath });
return context.module.exports;
};
const createAdminContext = (ip = "203.0.113.10") => ({
query: {},
req: {
headers: { "x-forwarded-for": ip },
socket: { remoteAddress: ip }
},
res: {}
});
test("unauthenticated admin access redirects before privileged data loads", async () => {
let dataLoads = 0;
const mod = loadAdminPageLoader({
readAdminSessionCookie: () => null,
validateAdminSession: () => false,
fetchAdminStorageData: async () => {
dataLoads += 1;
return { data: [], userData: [], repCompleteCount: 0 };
},
getNewAppeals: async () => {
dataLoads += 1;
return { value: [] };
}
});
const result = await mod.getServerSideProps(createAdminContext());
assert.strictEqual(result.redirect.destination, "/admin/login");
assert.strictEqual(result.redirect.permanent, false);
assert.strictEqual(dataLoads, 0);
});
test("invalid or expired administrative sessions cannot access admin", async () => {
for (const token of ["invalid-token", "expired-token"]) {
const mod = loadAdminPageLoader({
readAdminSessionCookie: () => token,
validateAdminSession: () => false
});
const result = await mod.getServerSideProps(createAdminContext());
assert.strictEqual(result.redirect.destination, "/admin/login");
}
});
test("valid administrative session and allowed IP load existing admin data", async () => {
const calls = [];
const mod = loadAdminPageLoader({
fetchAdminStorageData: async () => {
calls.push("storage");
return { data: ["storage"], userData: ["user"], repCompleteCount: 1 };
},
getNewAppeals: async () => {
calls.push("appeals");
return { value: [] };
},
getStatusByAppeal: async () => {
calls.push("status");
return ["status"];
},
getStatusByAppealLPA: async () => {
calls.push("lpa-status");
return ["lpa-status"];
},
getSearchDetails: async () => {
calls.push("search-details");
return {};
}
});
const result = await mod.getServerSideProps(createAdminContext());
assert.deepStrictEqual(calls, [
"storage",
"appeals",
"status",
"lpa-status",
"search-details"
]);
assert.strictEqual(result.props.repCompleteCount, 1);
});
test("valid administrative session and disallowed IP retains 403 redirect", async () => {
let dataLoads = 0;
const mod = loadAdminPageLoader({
fetchAdminStorageData: async () => {
dataLoads += 1;
return { data: [], userData: [], repCompleteCount: 0 };
}
});
const result = await mod.getServerSideProps(
createAdminContext("198.51.100.25")
);
assert.strictEqual(result.redirect.destination, "/403");
assert.strictEqual(dataLoads, 0);
});
test("missing administrative configuration fails closed at the page boundary", async () => {
const mod = loadAdminPageLoader({ validateAdminSession: () => false });
const result = await mod.getServerSideProps(createAdminContext());
assert.strictEqual(result.redirect.destination, "/admin/login");
});
test("login page remains available for missing or invalid sessions", async () => {
const mod = loadAdminLoginLoader();
const result = await mod.getServerSideProps({ req: { headers: {} } });
assert.deepStrictEqual(JSON.parse(JSON.stringify(result)), { props: {} });
});
test("login page redirects an already authenticated administrator to admin", async () => {
const mod = loadAdminLoginLoader({
readAdminSessionCookie: () => "valid-token",
validateAdminSession: () => true
});
const result = await mod.getServerSideProps({ req: { headers: {} } });
assert.strictEqual(result.redirect.destination, "/admin");
assert.strictEqual(result.redirect.permanent, false);
});
test("login page uses the existing login API and generic error handling", () => {
const source = readSource("pages/admin/login.js");
assert.match(source, /fetch\("\/api\/admin\/auth\/login"/);
assert.match(source, /method:\s*"POST"/);
assert.match(source, /JSON\.stringify\(\{ passphrase \}\)/);
assert.match(source, /type="password"/);
assert.match(source, /htmlFor="admin-passphrase"/);
assert.match(source, /Sign in failed\. Check the details and try again\./);
assert.match(source, /router\.replace\("\/admin"\)/);
assert.doesNotMatch(source, /localStorage|sessionStorage/);
});
test("admin logout uses the existing endpoint and returns to login", () => {
const source = readSource("pages/admin/index.js");
assert.match(source, /fetch\("\/api\/admin\/auth\/logout"/);
assert.match(source, /method:\s*"POST"/);
assert.match(source, /router\.replace\("\/admin\/login"\)/);
assert.match(source, />\s*\{signingOut \? "Signing out\.\.\." : "Sign out"\}/);
assert.doesNotMatch(source, /document\.cookie/);
});
test("admin login integration adds no NextAuth, Prisma, or CRM authentication dependency", () => {
for (const relativePath of [
"pages/admin/login.js",
"lib/admin/adminAuth.js",
"pages/api/admin/auth/login.js",
"pages/api/admin/auth/logout.js",
"pages/api/admin/auth/session.js"
]) {
const source = readSource(relativePath);
assert.doesNotMatch(source, /next-auth|@prisma|Prisma|getPortalLogin|CRM/i);
}
});
test("existing signing and document service files remain unchanged", () => {
const protectedPaths = [
"pages/api/endpoint/gethash_api.js",
"actions/services/documentDirectService.js"
];
const changedPaths = require("child_process")
.execFileSync("git", ["diff", "--name-only", "HEAD"], {
cwd: rootDir,
encoding: "utf8"
})
.trim()
.split("\n")
.filter(Boolean);
for (const protectedPath of protectedPaths) {
assert.ok(!changedPaths.includes(protectedPath));
}
});
const run = async () => {
let passed = 0;
for (const currentTest of tests) {
await currentTest.fn();
passed += 1;
}
console.log(
`Administrative login integration tests passed (${passed}/${tests.length}).`
);
};
module.exports = run;
if (require.main === module) {
run().catch((error) => {
console.error(error);
process.exit(1);
});
}