Files
boss/tests/admin-account-status-route.test.ts

146 lines
4.5 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
import os from "node:os";
import path from "node:path";
import { mkdtemp, rm } from "node:fs/promises";
import { NextRequest } from "next/server";
let runtimeRoot = "";
let data: typeof import("../src/lib/boss-data");
let authCookie = "";
let postAdminAccess: (typeof import("../src/app/api/v1/admin/access/route"))["POST"];
let baseState: Awaited<ReturnType<typeof import("../src/lib/boss-data")["readState"]>>;
async function setup() {
if (runtimeRoot) return;
runtimeRoot = await mkdtemp(path.join(os.tmpdir(), "boss-admin-account-status-"));
process.env.BOSS_RUNTIME_ROOT = runtimeRoot;
process.env.BOSS_STATE_FILE = path.join(runtimeRoot, "boss-state.json");
const [dataModule, authModule, routeModule] = await Promise.all([
import("../src/lib/boss-data.ts"),
import("../src/lib/boss-auth.ts"),
import("../src/app/api/v1/admin/access/route.ts"),
]);
data = dataModule;
authCookie = authModule.AUTH_SESSION_COOKIE;
postAdminAccess = routeModule.POST;
baseState = structuredClone(await data.readState());
}
test.after(async () => {
if (runtimeRoot) await rm(runtimeRoot, { recursive: true, force: true });
});
test.beforeEach(async () => {
await setup();
const state = structuredClone(baseState);
const now = "2026-04-27T12:00:00+08:00";
state.authAccounts = [
{
id: "account-owner",
account: "owner@acme.com",
passwordHash: "secret",
displayName: "Acme 老板",
role: "highest_admin",
createdAt: now,
updatedAt: now,
},
{
id: "account-worker",
account: "worker@acme.com",
passwordHash: "secret",
displayName: "Worker",
role: "member",
createdAt: now,
updatedAt: now,
},
];
state.authSessions = [];
state.permissionAuditLogs = [];
await data.writeState(state);
});
async function authedRequest(
account: string,
role: "member" | "admin" | "highest_admin",
body: Record<string, unknown>,
) {
const session = await data.createAuthSession({
account,
role,
displayName: account,
loginMethod: "password",
});
return new NextRequest("http://127.0.0.1:3000/api/v1/admin/access", {
method: "POST",
headers: {
"content-type": "application/json",
cookie: `${authCookie}=${session.sessionToken}`,
},
body: JSON.stringify(body),
});
}
async function adminPost(body: Record<string, unknown>) {
return postAdminAccess(await authedRequest("owner@acme.com", "highest_admin", body));
}
test("highest admin can disable a child account and revoke its active sessions", async () => {
const workerSession = await data.createAuthSession({
account: "worker@acme.com",
role: "member",
displayName: "Worker",
loginMethod: "password",
});
const response = await adminPost({
action: "set_account_status",
account: "worker@acme.com",
status: "disabled",
});
assert.equal(response.status, 200);
const payload = await response.json();
assert.equal(payload.account.status, "disabled");
assert.equal(payload.account.passwordHash, undefined);
const state = await data.readState();
assert.equal(state.authAccounts.find((account) => account.account === "worker@acme.com")?.status, "disabled");
assert.equal(
state.authSessions.find((session) => session.sessionToken === workerSession.sessionToken)?.revokedAt !== undefined,
true,
);
assert.equal(await data.getAuthSession(workerSession.sessionToken), null);
assert.equal(state.permissionAuditLogs.at(0)?.action, "account.updated");
});
test("highest admin can re-enable a disabled child account", async () => {
await adminPost({
action: "set_account_status",
account: "worker@acme.com",
status: "disabled",
});
const response = await adminPost({
action: "set_account_status",
account: "worker@acme.com",
status: "active",
});
assert.equal(response.status, 200);
const payload = await response.json();
assert.equal(payload.account.status, "active");
const state = await data.readState();
assert.equal(state.authAccounts.find((account) => account.account === "worker@acme.com")?.status, "active");
});
test("highest admin cannot disable a highest admin account", async () => {
const response = await adminPost({
action: "set_account_status",
account: "owner@acme.com",
status: "disabled",
});
assert.equal(response.status, 400);
const payload = await response.json();
assert.equal(payload.message, "CANNOT_DISABLE_HIGHEST_ADMIN");
});