Files
boss/tests/device-codex-remote-control-route.test.ts
2026-06-04 17:12:23 +08:00

179 lines
5.8 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 postRemoteControl: (typeof import("../src/app/api/v1/devices/[deviceId]/codex-remote-control/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-device-codex-remote-control-"));
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/devices/[deviceId]/codex-remote-control/route.ts"),
]);
data = dataModule;
authCookie = authModule.AUTH_SESSION_COOKIE;
postRemoteControl = routeModule.POST;
baseState = structuredClone(await data.readState());
}
test.beforeEach(async () => {
await setup();
await rm(runtimeRoot, { recursive: true, force: true });
const state = structuredClone(baseState);
const now = "2026-06-04T10:00:00+08:00";
state.authAccounts = [
{
id: "account-owner",
account: "owner@boss.test",
passwordHash: "secret",
displayName: "企业老板",
role: "highest_admin",
primaryDeviceId: "mac-1",
createdAt: now,
updatedAt: now,
},
{
id: "account-operator",
account: "operator@boss.test",
passwordHash: "secret",
displayName: "设备操作者",
role: "member",
primaryDeviceId: "mac-1",
createdAt: now,
updatedAt: now,
},
];
state.authSessions = [];
state.accountDeviceGrants = [];
state.permissionAuditLogs = [];
state.devices = [
{
id: "mac-1",
name: "客户 Mac",
avatar: "M",
account: "owner@boss.test",
source: "production",
status: "online",
projects: ["master-agent"],
quota5h: 0,
quota7d: 0,
lastSeenAt: now,
preferredExecutionMode: "cli",
capabilities: {
gui: { connected: true, lastSeenAt: now },
cli: { connected: true, lastSeenAt: now },
browserAutomation: { connected: true, lastSeenAt: now },
computerUse: { connected: true, lastSeenAt: now },
codexAppServer: {
connected: true,
lastSeenAt: now,
metadata: {
remoteControlSummary: {
supported: true,
startCommandLabel: "codex remote-control start --json",
},
},
},
},
},
];
await data.writeState(state);
});
test.after(async () => {
if (runtimeRoot) {
await rm(runtimeRoot, { recursive: true, force: true });
}
});
async function sessionCookie(account: string, role: "member" | "admin" | "highest_admin") {
const session = await data.createAuthSession({
account,
role,
displayName: account,
loginMethod: "password",
});
return `${authCookie}=${session.sessionToken}`;
}
async function postAs(
account: string,
role: "member" | "admin" | "highest_admin",
body: Record<string, unknown>,
) {
return postRemoteControl(
new NextRequest("http://127.0.0.1:3000/api/v1/devices/mac-1/codex-remote-control", {
method: "POST",
headers: {
"content-type": "application/json",
cookie: await sessionCookie(account, role),
"x-request-id": "req-remote-control",
},
body: JSON.stringify(body),
}),
{ params: Promise.resolve({ deviceId: "mac-1" }) },
);
}
test("codex remote control action requires login, explicit confirmation, and computer control permission", async () => {
await setup();
const anonymous = await postRemoteControl(
new NextRequest("http://127.0.0.1:3000/api/v1/devices/mac-1/codex-remote-control", {
method: "POST",
body: JSON.stringify({ action: "start", confirmed: true }),
}),
{ params: Promise.resolve({ deviceId: "mac-1" }) },
);
assert.equal(anonymous.status, 401);
const missingConfirmation = await postAs("owner@boss.test", "highest_admin", { action: "start" });
assert.equal(missingConfirmation.status, 400);
const forbidden = await postAs("operator@boss.test", "member", { action: "start", confirmed: true });
assert.equal(forbidden.status, 403);
const state = await data.readState();
assert.equal(state.masterAgentTasks.length, 0);
assert.equal(state.permissionAuditLogs.at(0)?.action, "task.denied");
});
test("highest admin can queue a device maintenance task for codex remote control start", async () => {
const response = await postAs("owner@boss.test", "highest_admin", {
action: "start",
confirmed: true,
reason: "开启 Codex Remote Control 供 Boss App 真时控制。",
});
assert.equal(response.status, 200);
const payload = await response.json();
assert.equal(payload.ok, true);
assert.equal(payload.task.taskType, "device_maintenance");
assert.equal(payload.task.maintenanceKind, "codex_remote_control");
assert.equal(payload.task.codexRemoteControlAction, "start");
assert.deepEqual(payload.task.requiredPermissions, ["computer.control"]);
const claimed = await data.claimNextMasterAgentTask("mac-1");
assert.equal(claimed?.taskId, payload.task.taskId);
assert.equal(claimed?.maintenanceKind, "codex_remote_control");
assert.equal(claimed?.codexRemoteControlAction, "start");
const state = await data.readState();
const audit = state.permissionAuditLogs.find((item) => item.action === "task.authorized");
assert.equal(audit?.actorAccount, "owner@boss.test");
assert.equal(audit?.deviceId, "mac-1");
assert.equal(audit?.detail, "codex_remote_control:start");
});