Files
pikasTech-HWLAB/tools/hwpod-harness.test.ts
T
2026-06-05 11:52:47 +08:00

119 lines
5.3 KiB
TypeScript

import assert from "node:assert/strict";
import { mkdtemp, readFile, rm } from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { test } from "bun:test";
import { runHwpodCli, runHwpodCompilerCli, runHwpodCtl } from "./src/hwpod-harness-lib.ts";
const NOW = "2026-06-05T00:00:00.000Z";
test("hwpod-ctl initializes, validates, edits, and binds workspace-local hwpod-spec", async () => {
const root = await mkdtemp(path.join(os.tmpdir(), "hwlab-hwpod-ctl-"));
const specPath = path.join(root, ".hwlab", "hwpod-spec.yaml");
try {
const init = await runHwpodCtl([
"spec",
"init",
"--spec",
specPath,
"--name",
"stm32-local",
"--node",
"pc-host-1",
"--workspace",
"/workspace/fw"
], { now: () => NOW });
assert.equal(init.exitCode, 0);
assert.equal(init.payload.document.kind, "Hwpod");
assert.equal(init.payload.document.spec.nodeBinding.nodeId, "pc-host-1");
const validate = await runHwpodCtl(["spec", "validate", "--spec", specPath], { now: () => NOW });
assert.equal(validate.exitCode, 0);
assert.deepEqual(validate.payload.fourElements, ["targetDevice", "workspace", "debugProbe", "ioProbe"]);
const set = await runHwpodCtl(["spec", "set", "spec.targetDevice.id", "stm32f103", "--spec", specPath], { now: () => NOW });
assert.equal(set.exitCode, 0);
assert.equal(set.payload.document.spec.targetDevice.id, "stm32f103");
const bind = await runHwpodCtl(["bind", "--spec", specPath, "--node", "edge-gateway-1"], { now: () => NOW });
assert.equal(bind.exitCode, 0);
assert.equal(bind.payload.nodeId, "edge-gateway-1");
assert.match(await readFile(specPath, "utf8"), /nodeId: edge-gateway-1/u);
} finally {
await rm(root, { recursive: true, force: true });
}
});
test("hwpod-compiler-cli compiles workspace-local spec into node ops", async () => {
const root = await mkdtemp(path.join(os.tmpdir(), "hwlab-hwpod-compiler-"));
const specPath = path.join(root, ".hwlab", "hwpod-spec.yaml");
try {
await runHwpodCtl(["spec", "init", "--spec", specPath, "--node", "pc-host-1", "--workspace", "/workspace/fw"], { now: () => NOW });
const result = await runHwpodCompilerCli([
"compile",
"--spec",
specPath,
"--intent",
"workspace.ls",
"--args",
"{\"path\":\"src\"}"
], { now: () => NOW });
assert.equal(result.exitCode, 0);
assert.equal(result.payload.contractVersion, "hwpod-node-ops-v1");
assert.equal(result.payload.plan.nodeId, "pc-host-1");
assert.equal(result.payload.plan.ops.length, 1);
assert.equal(result.payload.plan.ops[0].op, "workspace.ls");
assert.equal(result.payload.plan.ops[0].args.path, "src");
assert.equal(result.payload.plan.ops[0].args.workspacePath, "/workspace/fw");
} finally {
await rm(root, { recursive: true, force: true });
}
});
test("hwpod-cli dry-run keeps high-level intent out of hwlab-api and exposes hwpod-node-ops plan", async () => {
const root = await mkdtemp(path.join(os.tmpdir(), "hwlab-hwpod-cli-"));
const specPath = path.join(root, ".hwlab", "hwpod-spec.yaml");
try {
await runHwpodCtl(["spec", "init", "--spec", specPath, "--node", "pc-host-1"], { now: () => NOW });
const inspect = await runHwpodCli(["inspect", "--spec", specPath, "--dry-run"], { now: () => NOW });
assert.equal(inspect.exitCode, 0);
assert.equal(inspect.payload.dryRun, true);
assert.deepEqual(inspect.payload.plan.ops.map((op: any) => op.op), ["node.health", "node.inventory"]);
const build = await runHwpodCli(["build", "--spec", specPath, "--dry-run", "--target", "Debug"], { now: () => NOW });
assert.equal(build.exitCode, 0);
assert.equal(build.payload.plan.ops[0].op, "debug.build");
assert.equal(build.payload.plan.ops[0].args.target, "Debug");
} finally {
await rm(root, { recursive: true, force: true });
}
});
test("hwpod-cli submits compiled node ops to hwlab-api when not dry-run", async () => {
const root = await mkdtemp(path.join(os.tmpdir(), "hwlab-hwpod-cli-submit-"));
const specPath = path.join(root, ".hwlab", "hwpod-spec.yaml");
const seen: any[] = [];
try {
await runHwpodCtl(["spec", "init", "--spec", specPath, "--node", "pc-host-1"], { now: () => NOW });
const result = await runHwpodCli(["workspace", "ls", "src", "--spec", specPath], {
env: {
HWLAB_RUNTIME_API_URL: "http://cloud.test",
HWLAB_RUNTIME_NAMESPACE: "hwlab-v02",
HWLAB_RUNTIME_ENDPOINT_LOCKED: "1",
HWLAB_API_KEY: "hwl_live_test"
},
fetchImpl: async (url, init) => {
seen.push({ url: String(url), init, body: JSON.parse(String(init?.body ?? "{}")) });
return new Response(JSON.stringify({ ok: true, status: "completed", contractVersion: "hwpod-node-ops-v1", results: [{ op: "workspace.ls", ok: true }] }), { status: 200, headers: { "content-type": "application/json" } });
},
now: () => NOW
});
assert.equal(result.exitCode, 0);
assert.equal(seen[0].url, "http://cloud.test/v1/hwpod-node-ops");
assert.equal(seen[0].init.headers["x-hwlab-device-pod-api-key"], "hwl_live_test");
assert.equal(seen[0].body.contractVersion, "hwpod-node-ops-v1");
assert.equal(seen[0].body.ops[0].op, "workspace.ls");
} finally {
await rm(root, { recursive: true, force: true });
}
});