feat: 实现 CaseRun HWPOD 执行合同

This commit is contained in:
root
2026-07-16 16:08:05 +02:00
parent 324bb9d2e4
commit 814ea533f3
8 changed files with 147 additions and 49 deletions
+22 -1
View File
@@ -5,7 +5,7 @@ import path from "node:path";
import { test } from "bun:test";
import { runHwlabCli } from "../src/hwlab-cli-lib.ts";
import { artifactsFromKeilStatusForTest, extractKeilJobId, jobStatusCommandForTest } from "../src/hwlab-caserun-lib.ts";
import { artifactsFromKeilStatusForTest, buildWaitBlockerForTest, extractKeilJobId, jobStatusCommandForTest, summarizeHwpodOperationForTest } from "../src/hwlab-caserun-lib.ts";
const SUBJECT_COMMIT_ID = "df7a4e6e551fa90d64bde5537cc000f89d63dd20";
const SUBJECT_REPO_LOCAL_PATH = "F:\\Work\\HWLAB-CASE-F103";
@@ -202,6 +202,20 @@ test("case run exposes Keil hex and axf artifacts in evidence summary", () => {
assert.deepEqual(artifactsFromKeilStatusForTest({ result: { hex_file: "F:\\out.hex", axf_file: "F:\\out.axf" } }), ["F:\\out.hex", "F:\\out.axf"]);
});
test("case run normalizes v0.3 HWPOD build operation topology and typed wait blockers", () => {
const document = { metadata: { name: "d601-f103-v2" }, spec: { nodeBinding: { nodeId: "node-d601-f103-v2" }, workspace: { path: "F:\\Work\\case" } } };
const operation = summarizeHwpodOperationForTest({ body: { hwpodId: "d601-f103-v2", nodeId: "node-d601-f103-v2", results: [{ opId: "op_build", op: "debug.build", ok: true, status: "completed", output: { stdout: '{"job_id":"job-1"}' } }] } }, document, "debug.build");
assert.equal(operation.topology.ok, true);
assert.equal(operation.jobId, "job-1");
assert.equal(buildWaitBlockerForTest(operation, { ok: true, status: "completed", warningCount: 7, artifacts: ["F:\\out.hex"] }, 0), null);
assert.equal(buildWaitBlockerForTest({ ...operation, jobId: "" }, null, 0).code, "hwpod_build_job_id_missing");
assert.equal(buildWaitBlockerForTest(operation, { ok: false, status: "timeout", timedOut: true, polls: 3 }, 0).code, "hwpod_build_wait_timeout");
assert.equal(buildWaitBlockerForTest(operation, { ok: false, status: "failed", returnCode: 2 }, 0).code, "hwpod_build_failed");
assert.equal(buildWaitBlockerForTest(operation, { ok: true, status: "completed", artifacts: ["F:\\out.axf"] }, 0).code, "hwpod_build_hex_missing");
const mismatch = summarizeHwpodOperationForTest({ body: { nodeId: "wrong-node", results: [{ op: "debug.build", ok: true, output: { stdout: '{"job_id":"job-2"}' } }] } }, document, "debug.build");
assert.equal(buildWaitBlockerForTest(mismatch, null, 0).code, "hwpod_topology_mismatch");
});
test("case run start returns immediately and status/result/logs are short polling commands", async () => {
const root = await mkdtempCaseRoot();
const caseRepo = path.join(root, "hwlab-case-registry");
@@ -470,6 +484,7 @@ test("case run orchestrates agent prompt, diff capture, and compile evidence wit
assert.equal(result.payload.evidence.hwpod.source, "case-run-runner-post-agent-compile-check");
assert.match(result.payload.agentDiff.statusShort, /projects\/01_baseline\/main\.c/u);
assert.deepEqual(result.payload.agentDiff.diffCollection.untracked, { total: 2, included: 1, omitted: 1 });
assert.deepEqual(result.payload.agentDiff.diffCollection.submodules, [{ path: "vendor/lib", commit: "0123456789abcdef", state: "modified" }]);
assert.equal(result.payload.agentDiff.diffCollection.included[0].path, "projects/01_baseline/new.c");
assert.equal(result.payload.agentDiff.diffCollection.omitted[0].path, "projects/01_baseline/build/out.bin");
assert.equal(result.payload.agentDiff.diffCollection.omitted[0].reason, "generated output");
@@ -1004,6 +1019,12 @@ function caseRunFlowFetch(requests: any[]) {
JSON.stringify({ path: "projects/01_baseline/build/out.bin", bytes: 4096, sha256: "b".repeat(64) })
].join("\n") }]);
}
if (body.ops?.[0]?.args?.argv?.includes("submodule")) {
return hwpodResponse([{ exitCode: 0, stdout: "+0123456789abcdef vendor/lib (heads/main)\n" }]);
}
if (body.ops?.[0]?.args?.argv?.includes("submodule")) {
return hwpodResponse([{ exitCode: 0, stdout: "+0123456789abcdef vendor/lib (heads/main)\n" }]);
}
if (body.ops?.[0]?.args?.argv?.includes("ls-files")) {
return hwpodResponse([{ exitCode: 0, stdout: "projects/01_baseline/new.c\nprojects/01_baseline/build/out.bin\n" }]);
}