Files
pikasTech-HWLAB/internal/cloud/hwpod-topology-read-model.test.ts
2026-07-21 13:23:55 +02:00

121 lines
6.2 KiB
TypeScript

/*
* SPEC: PJ2026-010405 云端控制台;PJ2026-010103 HWPOD 服务。
* Implementation reference: draft-2026-07-13-p0-cloud-console。
* Responsibility: 验证 HWPOD typed topology 的状态、脱敏、筛选与双向 cursor。
*/
import assert from "node:assert/strict";
import { test } from "bun:test";
import { buildHwpodTopologyReadModel } from "./hwpod-topology-read-model.ts";
test("typed topology covers multi-device, offline, unconfigured, invalid, mismatch, and busy states", () => {
const specs = [
spec("hwpod-a", "node-a", ["workspace.ls"], true),
spec("hwpod-b", "node-a", ["workspace.ls", "debug.download"], true),
spec("hwpod-c", "node-c", ["workspace.ls"], null),
{ ok: false, status: "invalid", specPath: "/registry/broken.yaml", error: { code: "invalid_hwpod_spec", message: "missing targetDevice" } }
];
const registry = {
mode: "hwpod-node-outbound-native-ws",
pendingCount: 1,
nodes: [
{
nodeId: "node-a",
name: "D601 Python Node",
status: "online",
platform: "win32",
version: "0.1.2-python-gui",
runtimeKind: "python-tkinter-gui",
installed: true,
desktopVisible: true,
capabilities: ["workspace.ls", "node.health"],
inFlightCount: 1,
maxInFlight: 1,
lastDiagnostic: { level: "ERROR", source: "ws", message: "connect failed: Bearer private-token?token=still-secret" },
latestOperation: { requestId: "req_busy", planId: "plan_busy", hwpodId: "hwpod-a", status: "running", opCount: 1, startedAt: "2026-07-13T00:00:00.000Z" }
},
{ nodeId: "node-b", name: "Online without spec", status: "online", capabilities: [], inFlightCount: 0, maxInFlight: 1 }
]
};
const devicePayload = buildHwpodTopologyReadModel(specs, registry, { resource: "device", observedAt: "2026-07-13T01:00:00.000Z" });
assert.equal(devicePayload.contractVersion, "hwpod-topology-v1");
assert.equal(devicePayload.valuesRedacted, true);
assert.equal(devicePayload.summary.nodeCount, 3);
assert.equal(devicePayload.summary.deviceCount, 3);
assert.equal(devicePayload.summary.invalidSpecCount, 1);
const deviceA = devicePayload.items.find((item: any) => item.hwpodId === "hwpod-a");
const deviceB = devicePayload.items.find((item: any) => item.hwpodId === "hwpod-b");
assert.equal(deviceA.status, "online");
assert.equal(deviceA.busy, true);
assert.equal(deviceA.available, false);
assert.equal(deviceB.status, "online");
assert.equal(deviceB.capabilityMismatch, true);
assert.deepEqual(deviceB.missingCapabilities, ["debug.download"]);
assert.deepEqual(deviceB.capabilityCoverage, { matched: 1, declared: 2, actual: 2, missing: 1, extra: 1 });
assert.equal(deviceB.blockers.some((item: any) => item.code === "hwpod_capability_mismatch"), true);
assert.equal(devicePayload.items.find((item: any) => item.hwpodId === "hwpod-c").status, "offline");
const nodePayload = buildHwpodTopologyReadModel(specs, registry, { resource: "node", observedAt: "2026-07-13T01:00:00.000Z" });
const nodeA = nodePayload.items.find((item: any) => item.nodeId === "node-a");
assert.equal(nodeA.deviceCount, 2);
assert.equal(nodeA.status, "online");
assert.equal(nodeA.inFlight.busy, true);
assert.deepEqual(nodeA.capabilityCoverage, { matched: 1, declared: 2, actual: 2, missing: 1, extra: 1 });
assert.equal(nodeA.diagnostic.message.includes("private-token"), false);
assert.equal(nodeA.diagnostic.message.includes("still-secret"), false);
assert.equal(nodeA.diagnostic.message.includes("[REDACTED]"), true);
assert.equal(nodeA.diagnostic.valuesRedacted, true);
assert.equal(nodeA.readiness.find((stage: any) => stage.id === "desktop-visible").status, "ready");
assert.equal(nodeA.readiness.find((stage: any) => stage.id === "desktop-visible").nextAction, null);
assert.equal(nodeA.readiness.find((stage: any) => stage.id === "hwpod-ready").nextAction.entrypoint, "hwpod-topology");
assert.equal(nodePayload.items.find((item: any) => item.nodeId === "node-b").readiness.find((stage: any) => stage.id === "workspace-ready").blocker.code, "hwpod_node_spec_missing");
assert.equal(nodePayload.items.find((item: any) => item.nodeId === "node-b").status, "online");
assert.equal(nodePayload.summary.unconfiguredNodeCount, 1);
assert.equal(nodePayload.items.find((item: any) => item.nodeId === "node-c").status, "offline");
assert.equal(nodePayload.invalidSpecs[0].blocker.code, "invalid_hwpod_spec");
});
test("typed topology keeps server-side status filtering and cursor bounded", () => {
const specs = [spec("hwpod-a", "node-a", ["workspace.ls"], true), spec("hwpod-b", "node-b", ["workspace.ls"], true)];
const registry = {
nodes: [
{ nodeId: "node-a", status: "online", capabilities: ["workspace.ls"], inFlightCount: 0, maxInFlight: 1 },
{ nodeId: "node-b", status: "online", capabilities: ["workspace.ls"], inFlightCount: 0, maxInFlight: 1 }
]
};
const first = buildHwpodTopologyReadModel(specs, registry, { resource: "device", status: "online", limit: 1 });
assert.equal(first.items.length, 1);
assert.equal(first.page.total, 2);
assert.ok(first.page.nextCursor);
const second = buildHwpodTopologyReadModel(specs, registry, { resource: "device", status: "online", limit: 1, cursor: first.page.nextCursor });
assert.equal(second.items.length, 1);
assert.notEqual(first.items[0].hwpodId, second.items[0].hwpodId);
assert.equal(second.page.previousCursor, "b2Zmc2V0OjA");
});
function spec(hwpodId: string, nodeId: string, capabilities: string[], available: boolean | null) {
return {
ok: true,
hwpodId,
name: hwpodId,
uid: hwpodId.toUpperCase(),
nodeId,
authority: "test-registry",
specPath: `/registry/${hwpodId}.yaml`,
availability: { ok: available, status: available === true ? "available" : available === false ? "blocked" : "unprobed", checkedAt: null },
document: {
kind: "Hwpod",
metadata: { name: hwpodId },
spec: {
capabilities,
nodeBinding: { nodeId },
targetDevice: { board: `${hwpodId} board`, mcu: "STM32" },
workspace: { path: `F:\\Work\\${hwpodId}`, toolchain: "keil-mdk" },
debugProbe: { type: "cmsis-dap", probeName: "MicroLink" },
ioProbe: { uart: { id: "uart/1", port: "COM4" } }
}
}
};
}