187 lines
6.1 KiB
JavaScript
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);
|
|
});
|