Files
pikasTech-HWLAB/tools/hwlab-cli/lib/cli.test.mjs
T
2026-05-24 07:05:05 +00:00

187 lines
6.1 KiB
JavaScript

import assert from "node:assert/strict";
import { mkdtemp, readFile, writeFile } from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import test from "node:test";
import {
HWLAB_M3_IO_API_ROUTE,
HWLAB_M3_STATUS_API_ROUTE
} from "../../../skills/hwlab-agent-runtime/scripts/src/m3-io-skill-client.mjs";
import { runCli } from "./cli.mjs";
async function captureCli(args, options = {}) {
let stdout = "";
let stderr = "";
const exitCode = await runCli(args, {
cwd: options.cwd ?? process.cwd(),
env: options.env ?? {
PATH: process.env.PATH
},
requestJson: options.requestJson,
spawnDetached: options.spawnDetached,
stdout: {
write(chunk) {
stdout += chunk;
}
},
stderr: {
write(chunk) {
stderr += chunk;
}
}
});
return { exitCode, stdout, stderr };
}
test("repo hwlab-cli cicd submit is non-blocking and records follow-up commands", async () => {
const cwd = await mkdtemp(path.join(os.tmpdir(), "hwlab-cli-cicd-"));
const launched = [];
const result = await captureCli(
["cicd", "submit", "--kind", "ci-publish", "--services", "hwlab-cloud-web", "--concurrency", "2"],
{
cwd,
spawnDetached: async (job) => {
launched.push(job);
return { pid: 123456 };
}
}
);
assert.equal(result.exitCode, 0, result.stderr);
const body = JSON.parse(result.stdout);
assert.equal(body.ok, true);
assert.equal(body.nonBlocking, true);
assert.equal(body.kind, "ci-publish");
assert.match(body.jobId, /^ci-publish-/u);
assert.equal(body.pid, 123456);
assert.equal(body.followup.status, `hwlab-cli cicd status ${body.jobId}`);
assert.equal(launched.length, 1);
const spec = JSON.parse(await readFile(path.join(cwd, ".state", "hwlab-cicd", "jobs", body.jobId, "spec.json"), "utf8"));
assert.deepEqual(spec.args.slice(0, 4), ["scripts/dev-artifact-publish.mjs", "--publish", "--report", body.reportPath]);
assert.ok(spec.args.includes("--quiet-build"));
assert.equal(spec.envPatch.HWLAB_CI_ARTIFACT_RUN_ID, body.jobId);
});
test("repo hwlab-cli cicd status/logs/report disclose bounded job state", async () => {
const cwd = await mkdtemp(path.join(os.tmpdir(), "hwlab-cli-cicd-"));
const submit = await captureCli(["cicd", "submit", "--kind", "ci-publish"], {
cwd,
spawnDetached: async () => ({ pid: 123457 })
});
const submitted = JSON.parse(submit.stdout);
const jobDir = path.join(cwd, ".state", "hwlab-cicd", "jobs", submitted.jobId);
await writeFile(path.join(jobDir, "stdout.log"), "build line 1\nbuild line 2\n", "utf8");
await writeFile(path.join(jobDir, "stderr.log"), "warning line\n", "utf8");
await writeFile(
path.join(jobDir, "report.json"),
`${JSON.stringify({
sourceCommitId: "abc123",
artifactPublish: {
status: "published",
serviceCount: 1,
requiredServiceCount: 1,
publishedCount: 1,
timings: { durationMs: 42 }
},
blockers: []
})}\n`,
"utf8"
);
const status = await captureCli(["cicd", "status", submitted.jobId], { cwd });
assert.equal(status.exitCode, 0, status.stderr);
assert.equal(JSON.parse(status.stdout).status, "submitted");
const logs = await captureCli(["cicd", "logs", submitted.jobId, "--tail-bytes", "12"], { cwd });
assert.equal(logs.exitCode, 0, logs.stderr);
const logBody = JSON.parse(logs.stdout);
assert.equal(logBody.stdout.returnedBytes, 12);
assert.equal(logBody.stdout.truncated, true);
assert.ok(logBody.stdout.text.includes("line 2"));
const report = await captureCli(["cicd", "report", submitted.jobId], { cwd });
assert.equal(report.exitCode, 0, report.stderr);
const reportBody = JSON.parse(report.stdout);
assert.equal(reportBody.summary.kind, "ci-publish");
assert.equal(reportBody.summary.status, "published");
assert.equal(reportBody.summary.publishedCount, 1);
assert.equal(Object.hasOwn(reportBody, "report"), false);
});
test("repo hwlab-cli m3 status uses Skill CLI and calls only HWLAB API /v1/m3/status", async () => {
const calls = [];
const result = await captureCli(
[
"m3",
"status",
"--api-base-url",
"http://hwlab-cloud-api.hwlab-dev.svc.cluster.local:6667",
"--trace-id",
"trc_hwlab_cli_status"
],
{
requestJson: async (url, request) => {
calls.push({ url, request });
assert.equal(url, `http://hwlab-cloud-api.hwlab-dev.svc.cluster.local:6667${HWLAB_M3_STATUS_API_ROUTE}`);
assert.equal(request.method, "GET");
return {
ok: true,
status: 200,
body: {
status: "live",
sourceKind: "DEV-LIVE",
traceId: "trc_hwlab_cli_status",
boxes: [{
id: "boxsimu_2",
resourceId: "res_boxsimu_2",
online: true,
ports: {
DI1: {
value: false
}
}
}]
}
};
}
}
);
assert.equal(result.exitCode, 0, result.stderr);
const body = JSON.parse(result.stdout);
assert.equal(body.route, HWLAB_M3_STATUS_API_ROUTE);
assert.equal(body.method, "GET");
assert.equal(body.readback.value, false);
assert.equal(body.command.includes("hwlab-cloud-api.hwlab-dev"), false);
assert.equal(calls.length, 1);
});
test("repo hwlab-cli m3 io blocks direct hardware targets before request", async () => {
const result = await captureCli(
[
"m3",
"io",
"--action",
"di.read",
"--api-base-url",
"http://hwlab-patch-panel.hwlab-dev.svc.cluster.local:7301",
"--trace-id",
"trc_hwlab_cli_direct_blocked"
],
{
requestJson: async () => {
throw new Error("direct hardware target must not be called");
}
}
);
assert.equal(result.exitCode, 2);
const body = JSON.parse(result.stderr);
assert.equal(body.route, HWLAB_M3_IO_API_ROUTE);
assert.equal(body.blocker.code, "direct_hardware_target_blocked");
assert.equal(body.hwlabApi.redactedUrl, null);
assert.equal(JSON.stringify(body).includes("patch-panel.hwlab-dev"), false);
});