98 lines
3.8 KiB
TypeScript
98 lines
3.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";
|
||
|
||
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.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, "当前策略:allow_always");
|
||
assert.equal(cards.conflicts.items.conflictState, "冲突态:warning");
|
||
});
|
||
|
||
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.items.allowPolicy, "当前策略:allow_once");
|
||
assert.equal(cards.conflicts.items.conflictState, "冲突态:warning");
|
||
assert.deepEqual(cards.conflicts.actions, ["禁止", "允许本次", "永久放行"]);
|
||
assert.equal(cards.conflicts.scopeLabel, "仅作用于当前异常项目 / 文件夹");
|
||
});
|