feat: support hwpod L1 over-api operations
This commit is contained in:
@@ -119,6 +119,82 @@ test("HWPOD L0 local mode fails closed without an explicit spec", async () => {
|
||||
assert.equal(result.payload.error.code, "hwpod_l0_spec_required");
|
||||
});
|
||||
|
||||
test("HWPOD execution modes fail closed when local and over-api are combined", async () => {
|
||||
const result = await runHwpodCli(["workspace", "ls", ".", "--local", "--over-api", "--spec", "unused.yaml"]);
|
||||
assert.equal(result.exitCode, 1);
|
||||
assert.equal(result.payload.error.code, "hwpod_execution_mode_conflict");
|
||||
});
|
||||
|
||||
test("HWPOD L0 and L1 over-api expose the same operation contract", async () => {
|
||||
const root = await mkdtemp(path.join(os.tmpdir(), "hwpod-cli-compare-"));
|
||||
const specPath = path.join(root, "hwpod-spec.yaml");
|
||||
const operationId = "hwpod_compare_001";
|
||||
let submitted: any = null;
|
||||
const fetchImpl = async (input: string | URL | Request, init?: RequestInit) => {
|
||||
const url = new URL(String(input));
|
||||
if (url.pathname === "/v1/hwpod/operations" && init?.method === "POST") {
|
||||
submitted = JSON.parse(String(init.body));
|
||||
return Response.json({
|
||||
ok: true,
|
||||
status: "accepted",
|
||||
operationId: submitted.operationId,
|
||||
workflowId: `hwpod-operation-${submitted.operationId}`,
|
||||
workflowRunId: "run-001",
|
||||
authority: "hwpod-temporal",
|
||||
valuesPrinted: false
|
||||
}, { status: 202 });
|
||||
}
|
||||
if (url.pathname === `/v1/hwpod/operations/${operationId}` && init?.method === "GET") {
|
||||
return Response.json({
|
||||
ok: true,
|
||||
operationId,
|
||||
workflowId: `hwpod-operation-${operationId}`,
|
||||
workflowRunId: "run-001",
|
||||
status: "completed",
|
||||
result: { ok: true, status: "completed" },
|
||||
valuesPrinted: false
|
||||
});
|
||||
}
|
||||
return Response.json({ ok: false, error: { code: "unexpected_route" } }, { status: 404 });
|
||||
};
|
||||
const env = { HWLAB_HWPOD_OPERATION_IDENTITY: operationId };
|
||||
|
||||
try {
|
||||
await writeFile(specPath, hwpodSpecText(root), "utf8");
|
||||
const local = await runHwpodCli(["workspace", "ls", ".", "--local", "--spec", specPath], { env });
|
||||
const overApi = await runHwpodCli(["workspace", "ls", ".", "--over-api", "--spec", specPath, "--api-base-url", "http://hwpod.test"], { env, fetchImpl: fetchImpl as typeof fetch });
|
||||
|
||||
assert.equal(local.exitCode, 0);
|
||||
assert.equal(overApi.exitCode, 0);
|
||||
assert.equal(overApi.payload.action, "hwpod-cli.over-api");
|
||||
assert.equal(overApi.payload.mode, "l1-native-api");
|
||||
assert.equal(overApi.payload.transport, "over-api");
|
||||
assert.equal(overApi.payload.httpStatus, 202);
|
||||
for (const field of ["operationId", "hwpodId", "nodeId", "intent", "contractVersion", "ops"]) {
|
||||
assert.deepEqual(overApi.payload[field], local.payload[field], field);
|
||||
}
|
||||
assert.equal(submitted.operationId, operationId);
|
||||
assert.equal(submitted.plan.planId, operationId);
|
||||
assert.equal(submitted.plan.intent, local.payload.intent);
|
||||
assert.deepEqual(submitted.plan.ops.map((operation: any) => operation.op), local.payload.ops);
|
||||
|
||||
const status = await runHwpodCli(["operation", "status", operationId, "--over-api", "--api-base-url", "http://hwpod.test"], { env, fetchImpl: fetchImpl as typeof fetch });
|
||||
assert.equal(status.exitCode, 0);
|
||||
assert.equal(status.payload.action, "hwpod-cli.operation.status");
|
||||
assert.equal(status.payload.mode, "l1-native-api");
|
||||
assert.equal(status.payload.operationId, operationId);
|
||||
assert.equal(status.payload.body.status, "completed");
|
||||
} finally {
|
||||
await rm(root, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test("HWPOD operation status requires explicit over-api mode", async () => {
|
||||
const result = await runHwpodCli(["operation", "status", "operation-001"]);
|
||||
assert.equal(result.exitCode, 1);
|
||||
assert.equal(result.payload.error.code, "hwpod_operation_status_over_api_required");
|
||||
});
|
||||
|
||||
function hwpodSpecText(workspacePath = "F:\\Work\\HWLAB-CASE-F103", buildCommand = "") {
|
||||
return `kind: Hwpod\nmetadata:\n name: test-hwpod\nspec:\n nodeBinding:\n nodeId: test-node\n workspace:\n path: ${JSON.stringify(workspacePath)}\n${buildCommand ? ` buildCommand: ${JSON.stringify(buildCommand)}\n` : ""} targetDevice:\n board: D601-F103-V2\n debugProbe:\n type: daplink\n ioProbe:\n uart:\n port: COM9\n`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user