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"; let runtimeRoot = ""; let readState: (typeof import("../src/lib/boss-data"))["readState"]; let writeState: (typeof import("../src/lib/boss-data"))["writeState"]; let applyProjectConflictDecision: (typeof import("../src/lib/boss-data"))["applyProjectConflictDecision"]; let getDeviceWorkspaceView: (typeof import("../src/lib/boss-projections"))["getDeviceWorkspaceView"]; let buildDeviceWorkspaceDetailCards: (typeof import("../src/components/app-ui"))["buildDeviceWorkspaceDetailCards"]; async function setup() { if (runtimeRoot) return; runtimeRoot = await mkdtemp(path.join(os.tmpdir(), "boss-device-detail-route-")); process.env.BOSS_RUNTIME_ROOT = runtimeRoot; process.env.BOSS_STATE_FILE = path.join(runtimeRoot, "boss-state.json"); const [data, projections, ui] = await Promise.all([ import("../src/lib/boss-data.ts"), import("../src/lib/boss-projections.ts"), import("../src/components/app-ui.tsx"), ]); readState = data.readState; writeState = data.writeState; applyProjectConflictDecision = data.applyProjectConflictDecision; getDeviceWorkspaceView = projections.getDeviceWorkspaceView; buildDeviceWorkspaceDetailCards = ui.buildDeviceWorkspaceDetailCards; } test.after(async () => { if (runtimeRoot) { await rm(runtimeRoot, { recursive: true, force: true }); } }); test("device detail exposes gui cli capability state and preferred execution mode", async () => { await setup(); const state = await readState(); const workspace = getDeviceWorkspaceView(state, "mac-studio"); const cards = buildDeviceWorkspaceDetailCards(workspace); assert.equal(cards.capabilities.title, "执行能力"); assert.equal(cards.capabilities.items.gui, "GUI:已连接"); assert.equal(cards.capabilities.items.cli, "CLI:已连接"); assert.equal(cards.capabilities.items.preferredExecutionMode, "默认执行模式:CLI"); }); test("device detail exposes folder and project conflict skeleton from workspace policy", async () => { await setup(); const state = await readState(); state.projectExecutionPolicies = [ { deviceId: "mac-studio", folderKey: "mac-studio:boss", projectId: "thread-ui", allowPolicy: "allow_always", conflictState: "warning", updatedAt: "2026-04-06T12:00:00.000Z", }, ]; await writeState(state); const workspace = getDeviceWorkspaceView(await readState(), "mac-studio"); const cards = buildDeviceWorkspaceDetailCards(workspace); assert.equal(cards.conflicts.title, "异常项目 / 文件夹冲突"); assert.equal(cards.conflicts.headerHint, "已接入,可直接调整"); assert.equal(cards.conflicts.items.device, "设备:Mac Studio"); assert.equal(cards.conflicts.items.folderKey, "文件夹:mac-studio:boss"); assert.equal(cards.conflicts.items.projectId, "项目:thread-ui"); assert.equal(cards.conflicts.items.allowPolicy, "当前策略:永久放行"); assert.equal(cards.conflicts.items.conflictState, "冲突态:存在并行风险"); }); test("device detail conflict card keeps project-scoped actions on the active folder only", async () => { await setup(); await applyProjectConflictDecision({ deviceId: "mac-studio", folderKey: "mac-studio:boss", projectId: "thread-ui", decision: "allow_once", }); const workspace = getDeviceWorkspaceView(await readState(), "mac-studio"); const cards = buildDeviceWorkspaceDetailCards(workspace); assert.equal(cards.conflicts.headerHint, "已接入,可直接调整"); assert.equal(cards.conflicts.items.allowPolicy, "当前策略:允许本次"); assert.equal(cards.conflicts.items.conflictState, "冲突态:存在并行风险"); assert.deepEqual(cards.conflicts.actions, ["禁止", "允许本次", "永久放行"]); assert.equal(cards.conflicts.scopeLabel, "仅作用于当前异常项目 / 文件夹"); });