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 deviceHeartbeatRoute: (typeof import("../src/app/api/device-heartbeat/route"))["POST"]; let claimMasterTaskRoute: (typeof import("../src/app/api/v1/master-agent/tasks/claim/route"))["POST"]; let getBossAgentOtaRoute: (typeof import("../src/app/api/v1/boss-agent/ota/route"))["GET"]; let postAdminAccessRoute: (typeof import("../src/app/api/v1/admin/access/route"))["POST"]; async function setup() { if (runtimeRoot) return; runtimeRoot = await mkdtemp(path.join(os.tmpdir(), "boss-device-revocation-")); process.env.BOSS_RUNTIME_ROOT = runtimeRoot; process.env.BOSS_STATE_FILE = path.join(runtimeRoot, "boss-state.json"); const [dataModule, authModule, heartbeatModule, claimModule, otaModule, adminAccessModule] = await Promise.all([ import("../src/lib/boss-data.ts"), import("../src/lib/boss-auth.ts"), import("../src/app/api/device-heartbeat/route.ts"), import("../src/app/api/v1/master-agent/tasks/claim/route.ts"), import("../src/app/api/v1/boss-agent/ota/route.ts"), import("../src/app/api/v1/admin/access/route.ts"), ]); data = dataModule; authCookie = authModule.AUTH_SESSION_COOKIE; deviceHeartbeatRoute = heartbeatModule.POST; claimMasterTaskRoute = claimModule.POST; getBossAgentOtaRoute = otaModule.GET; postAdminAccessRoute = adminAccessModule.POST; } test.after(async () => { if (runtimeRoot) await rm(runtimeRoot, { recursive: true, force: true }); }); test.beforeEach(async () => { await setup(); await rm(runtimeRoot, { recursive: true, force: true }); }); function heartbeatRequest(body: Record) { return new NextRequest("http://127.0.0.1:3000/api/device-heartbeat", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ deviceId: "mac-studio", name: "Mac Studio", avatar: "M", account: "krisolo", status: "online", quota5h: 80, quota7d: 90, projects: ["Boss"], ...body, }), }); } async function highestAdminRequest(body: Record) { const session = await data.createAuthSession({ account: "krisolo", role: "highest_admin", displayName: "Boss 超级管理员", 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), }); } test("existing device heartbeat requires its device token and cannot be kept alive by an empty token request", async () => { const state = await data.readState(); const device = state.devices.find((item) => item.id === "mac-studio"); assert.ok(device); device.status = "offline"; device.lastSeenAt = "2026-05-17T01:00:00.000Z"; device.projects = ["原项目"]; await data.writeState(state); const response = await deviceHeartbeatRoute(heartbeatRequest({ token: undefined })); assert.equal(response.status, 401); const payload = await response.json(); assert.equal(payload.message, "DEVICE_TOKEN_REQUIRED"); const nextState = await data.readState(); const nextDevice = nextState.devices.find((item) => item.id === "mac-studio"); assert.equal(nextDevice?.status, "offline"); assert.equal(nextDevice?.lastSeenAt, "2026-05-17T01:00:00.000Z"); assert.deepEqual(nextDevice?.projects, ["原项目"]); }); test("revoked device token is rejected by heartbeat, master task claim, and boss-agent ota", async () => { const initial = await data.readState(); const device = initial.devices.find((item) => item.id === "mac-studio"); assert.ok(device?.token); const oldToken = device.token; const revokeResponse = await postAdminAccessRoute( await highestAdminRequest({ action: "revoke_device", deviceId: "mac-studio", reason: "客户解绑设备", }), ); assert.equal(revokeResponse.status, 200); const revokePayload = await revokeResponse.json(); assert.equal(revokePayload.device.id, "mac-studio"); assert.ok(revokePayload.device.revokedAt); assert.equal(revokePayload.device.status, "offline"); assert.equal(await data.verifyDeviceToken("mac-studio", oldToken), false); const queued = await data.queueMasterAgentTask({ taskId: "revoked-device-task", requestMessageId: "message-revoked", requestText: "打开浏览器", executionPrompt: "打开浏览器", requestedBy: "krisolo", requestedByAccount: "krisolo", deviceId: "mac-studio", }); assert.equal(queued.status, "queued"); const claimResponse = await claimMasterTaskRoute( new NextRequest("http://127.0.0.1:3000/api/v1/master-agent/tasks/claim", { method: "POST", headers: { "content-type": "application/json", "x-boss-device-token": oldToken, }, body: JSON.stringify({ deviceId: "mac-studio" }), }), ); assert.equal(claimResponse.status, 401); const heartbeatResponse = await deviceHeartbeatRoute(heartbeatRequest({ token: oldToken })); assert.equal(heartbeatResponse.status, 403); assert.equal((await heartbeatResponse.json()).message, "DEVICE_REVOKED"); const otaResponse = await getBossAgentOtaRoute( new NextRequest("http://127.0.0.1:3000/api/v1/boss-agent/ota?deviceId=mac-studio", { method: "GET", headers: { "x-boss-device-token": oldToken }, }), ); assert.equal(otaResponse.status, 401); const nextState = await data.readState(); const nextTask = nextState.masterAgentTasks.find((item) => item.taskId === "revoked-device-task"); const audit = nextState.permissionAuditLogs.find((item) => item.action === "device.revoked"); assert.equal(nextTask?.status, "queued"); assert.equal(nextState.devices.find((item) => item.id === "mac-studio")?.status, "offline"); assert.equal(audit?.deviceId, "mac-studio"); assert.equal(audit?.actorAccount, "krisolo"); }); test("a new device cannot self-register through heartbeat without a prepared enrollment", async () => { const response = await deviceHeartbeatRoute( new NextRequest("http://127.0.0.1:3000/api/device-heartbeat", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ deviceId: "rogue-mac", token: "rogue-token", name: "Rogue Mac", avatar: "R", account: "krisolo", status: "online", quota5h: 1, quota7d: 1, projects: ["Boss"], }), }), ); assert.equal(response.status, 401); assert.equal((await response.json()).message, "DEVICE_ENROLLMENT_REQUIRED"); assert.equal((await data.readState()).devices.some((item) => item.id === "rogue-mac"), false); });