677 lines
28 KiB
TypeScript
677 lines
28 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { mkdtemp, readFile } from "node:fs/promises";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
import { test } from "bun:test";
|
|
|
|
import { runHwlabCli } from "../src/hwlab-cli-lib.ts";
|
|
|
|
test("hwlab-cli client login uses Cloud Web auth and stores only cookie session", async () => {
|
|
const cwd = await mkdtemp(path.join(os.tmpdir(), "hwlab-cli-client-"));
|
|
const calls: any[] = [];
|
|
const result = await runHwlabCli([
|
|
"client",
|
|
"auth",
|
|
"login",
|
|
"--base-url",
|
|
"http://web.test",
|
|
"--username",
|
|
"admin",
|
|
"--password-env",
|
|
"HWLAB_PASSWORD"
|
|
], {
|
|
cwd,
|
|
env: { HWLAB_PASSWORD: "secret-password" },
|
|
fetchImpl: async (url, init) => {
|
|
calls.push({ url: String(url), init, body: JSON.parse(String(init?.body ?? "{}")) });
|
|
return new Response(JSON.stringify({ authenticated: true, user: { id: "usr_admin", username: "admin", role: "admin" }, expiresAt: "2026-05-30T12:00:00.000Z" }), {
|
|
status: 200,
|
|
headers: { "content-type": "application/json", "set-cookie": "hwlab_session=session-a; Path=/; HttpOnly" }
|
|
});
|
|
},
|
|
now: () => "2026-05-30T00:00:00.000Z"
|
|
});
|
|
|
|
assert.equal(result.exitCode, 0);
|
|
assert.equal(result.payload.action, "client.auth.login");
|
|
assert.equal(result.payload.cookieStored, true);
|
|
assert.equal(calls[0].url, "http://web.test/auth/login");
|
|
assert.deepEqual(calls[0].body, { username: "admin", password: "secret-password" });
|
|
assert.equal(JSON.stringify(result.payload).includes("secret-password"), false);
|
|
|
|
const session = JSON.parse(await readFile(path.join(cwd, ".state/hwlab-cli/session.json"), "utf8"));
|
|
assert.equal(session.baseUrl, "http://web.test");
|
|
assert.equal(session.cookie, "hwlab_session=session-a");
|
|
assert.equal(JSON.stringify(session).includes("secret-password"), false);
|
|
});
|
|
|
|
test("hwlab-cli client device-pods uses saved cookie and same Web API paths", async () => {
|
|
const cwd = await mkdtemp(path.join(os.tmpdir(), "hwlab-cli-client-"));
|
|
await runHwlabCli(["client", "auth", "login", "--base-url", "http://web.test", "--username", "admin", "--password", "pw"], {
|
|
cwd,
|
|
fetchImpl: async () => new Response(JSON.stringify({ authenticated: true, user: { username: "admin" } }), { status: 200, headers: { "set-cookie": "hwlab_session=session-a; Path=/" } })
|
|
});
|
|
const seen: any[] = [];
|
|
const result = await runHwlabCli(["client", "device-pods", "status", "device-pod-71-freq", "--base-url", "http://web.test"], {
|
|
cwd,
|
|
fetchImpl: async (url, init) => {
|
|
seen.push({ url: String(url), headers: init?.headers });
|
|
return new Response(JSON.stringify({ ok: true, devicePodId: "device-pod-71-freq", contractVersion: "device-pod-authority-v1", profileHash: "sha256:abc" }), { status: 200 });
|
|
}
|
|
});
|
|
|
|
assert.equal(result.exitCode, 0);
|
|
assert.equal(seen[0].url, "http://web.test/v1/device-pods/device-pod-71-freq/status");
|
|
assert.equal(seen[0].headers.cookie, "hwlab_session=session-a");
|
|
assert.equal(result.payload.route.path, "/v1/device-pods/device-pod-71-freq/status");
|
|
assert.equal(result.payload.body.devicePodId, "device-pod-71-freq");
|
|
assert.equal(result.payload.body.fullBodyAvailable, true);
|
|
});
|
|
|
|
test("hwlab-cli client auth status exposes local session state and next commands", async () => {
|
|
const cwd = await mkdtemp(path.join(os.tmpdir(), "hwlab-cli-client-auth-status-"));
|
|
const missing = await runHwlabCli(["client", "auth", "status", "--base-url", "http://web.test"], { cwd });
|
|
assert.equal(missing.exitCode, 0);
|
|
assert.equal(missing.payload.action, "client.auth.status");
|
|
assert.equal(missing.payload.localSession.exists, false);
|
|
assert.equal(missing.payload.localSession.usable, false);
|
|
assert.equal(missing.payload.localSession.ignoredReason, "state_file_missing");
|
|
assert.match(missing.payload.nextCommands[1], /client auth login/u);
|
|
|
|
await runHwlabCli(["client", "auth", "login", "--base-url", "http://old-web.test", "--username", "admin", "--password", "pw"], {
|
|
cwd,
|
|
fetchImpl: async () => new Response(JSON.stringify({ authenticated: true, user: { username: "admin" } }), { status: 200, headers: { "set-cookie": "hwlab_session=session-old; Path=/" } })
|
|
});
|
|
const mismatch = await runHwlabCli(["client", "auth", "status", "--base-url", "http://web.test"], { cwd });
|
|
assert.equal(mismatch.exitCode, 0);
|
|
assert.equal(mismatch.payload.localSession.exists, true);
|
|
assert.equal(mismatch.payload.localSession.usable, false);
|
|
assert.equal(mismatch.payload.localSession.ignoredReason, "base_url_mismatch");
|
|
assert.equal(JSON.stringify(mismatch.payload).includes("session-old"), false);
|
|
});
|
|
|
|
test("hwlab-cli client device-pods events compacts event stream by default", async () => {
|
|
const result = await runHwlabCli(["client", "device-pods", "events", "pod-1", "--base-url", "http://web.test", "--cookie", "hwlab_session=session-a"], {
|
|
fetchImpl: async () => new Response(JSON.stringify({
|
|
ok: true,
|
|
status: "ok",
|
|
events: [{ eventId: "evt-1", level: "info", scope: "job", intent: "debug.chip-id", summary: "running", refs: { traceId: "trc_1" } }],
|
|
lines: ["line-1"]
|
|
}), { status: 200 })
|
|
});
|
|
|
|
assert.equal(result.exitCode, 0);
|
|
assert.equal(result.payload.body.eventsCount, 1);
|
|
assert.equal(result.payload.body.events[0].eventId, "evt-1");
|
|
assert.equal(result.payload.body.lines[0], "line-1");
|
|
assert.equal(result.payload.body.fullBodyAvailable, true);
|
|
});
|
|
|
|
test("hwlab-cli client runtime routes reports UniDesk pod passthrough route", async () => {
|
|
const result = await runHwlabCli(["client", "runtime", "routes", "--base-url", "http://web.test", "--service-id", "hwlab-cloud-api"], {
|
|
fetchImpl: async () => new Response(JSON.stringify({
|
|
ok: true,
|
|
status: "ok",
|
|
services: [{
|
|
serviceId: "hwlab-cloud-api",
|
|
runtime: { pod: { name: "hwlab-cloud-api-abc", namespace: "hwlab-v02", container: "hwlab-cloud-api" } }
|
|
}]
|
|
}), { status: 200 })
|
|
});
|
|
|
|
assert.equal(result.exitCode, 0);
|
|
assert.equal(result.payload.items[0].unideskRoute, "G14:k3s:hwlab-v02:pod:hwlab-cloud-api-abc:hwlab-cloud-api");
|
|
assert.match(result.payload.items[0].useWith, /bun scripts\/cli\.ts ssh/u);
|
|
assert.equal(result.payload.discovery.ok, true);
|
|
assert.equal(result.payload.discovery.source, "cloud-web:/v1/live-builds");
|
|
assert.equal(result.payload.discovery.readyRouteCount, 1);
|
|
});
|
|
|
|
test("hwlab-cli client gateway sessions uses direct Cloud API short connection", async () => {
|
|
const calls: any[] = [];
|
|
const result = await runHwlabCli([
|
|
"client",
|
|
"gateway",
|
|
"sessions",
|
|
"--api-base-url",
|
|
"http://api.test",
|
|
"--no-auth",
|
|
"--full"
|
|
], {
|
|
fetchImpl: async (url, init) => {
|
|
calls.push({ url: String(url), init });
|
|
return new Response(JSON.stringify({ ok: true, sessions: [{ gatewaySessionId: "gws_D601_F103", inflightCount: 0 }] }), { status: 200 });
|
|
}
|
|
});
|
|
|
|
assert.equal(result.exitCode, 0);
|
|
assert.equal(calls[0].url, "http://api.test/v1/gateway/sessions");
|
|
assert.equal(calls[0].init.headers.cookie, undefined);
|
|
assert.equal(result.payload.action, "client.gateway.sessions");
|
|
assert.equal(result.payload.baseUrl, "http://api.test");
|
|
assert.equal(result.payload.body.sessions[0].gatewaySessionId, "gws_D601_F103");
|
|
});
|
|
|
|
test("hwlab-cli client gateway pressure reports byte/truncation visibility", async () => {
|
|
const calls: any[] = [];
|
|
const result = await runHwlabCli([
|
|
"client",
|
|
"gateway",
|
|
"pressure",
|
|
"--api-base-url",
|
|
"http://api.test",
|
|
"--gateway-session-id",
|
|
"gws_test",
|
|
"--large-bytes",
|
|
"8192",
|
|
"--parallel",
|
|
"2",
|
|
"--request-timeout-ms",
|
|
"10000",
|
|
"--timeout-scenario-ms",
|
|
"500"
|
|
], {
|
|
fetchImpl: async (url, init) => {
|
|
const callIndex = calls.length;
|
|
const body = JSON.parse(String(init?.body ?? "{}"));
|
|
calls.push({ url: String(url), init, body });
|
|
const dispatch: any = { dispatchStatus: "succeeded", shellExecuted: true, exitCode: 0, stdout: "", stderr: "" };
|
|
if (callIndex === 0) dispatch.stdout = "hwlab-gateway-pressure-small-ok\r\n";
|
|
else if (callIndex === 1 || callIndex === 2) Object.assign(dispatch, { stdout: "O".repeat(65536), stdoutTruncated: true });
|
|
else if (callIndex === 3) Object.assign(dispatch, { stderr: "E".repeat(65536), stderrTruncated: true });
|
|
else if (callIndex === 4) Object.assign(dispatch, { dispatchStatus: "timed_out", timedOut: true, exitCode: null });
|
|
else dispatch.stdout = `parallel-${callIndex - 4}-ok\r\n`;
|
|
return new Response(JSON.stringify({ ok: true, result: { status: "succeeded", dispatch } }), { status: 200 });
|
|
}
|
|
});
|
|
|
|
assert.equal(result.exitCode, 0);
|
|
assert.equal(result.payload.action, "client.gateway.pressure");
|
|
assert.equal(result.payload.status, "succeeded");
|
|
assert.equal(result.payload.scenarioCount, 7);
|
|
assert.equal(result.payload.failedCount, 0);
|
|
assert.equal(calls[0].url, "http://api.test/v1/rpc/hardware.invoke.shell");
|
|
assert.equal(calls[0].init.headers.cookie, undefined);
|
|
assert.equal(calls[0].body.gatewaySessionId, "gws_test");
|
|
assert.match(calls[1].body.input.command, /-EncodedCommand/u);
|
|
assert.equal(result.payload.results.find((item: any) => item.name === "large-stdout").stdoutTruncated, true);
|
|
assert.equal(result.payload.results.find((item: any) => item.name === "stderr-flood").stderrTruncated, true);
|
|
assert.equal(result.payload.results.find((item: any) => item.name === "timeout-kill").reason, "timeout_structured");
|
|
assert.match(result.payload.results.find((item: any) => item.name === "large-stdout").stdoutSha256, /^[a-f0-9]{64}$/u);
|
|
});
|
|
|
|
test("hwlab-cli client agent send submits async and polls result", async () => {
|
|
const calls: any[] = [];
|
|
const result = await runHwlabCli([
|
|
"client",
|
|
"agent",
|
|
"send",
|
|
"--base-url",
|
|
"http://web.test",
|
|
"--cookie",
|
|
"hwlab_session=session-a",
|
|
"--message",
|
|
"hello",
|
|
"--trace-id",
|
|
"trc_test",
|
|
"--conversation-id",
|
|
"cnv_test",
|
|
"--poll-interval-ms",
|
|
"1",
|
|
"--timeout-ms",
|
|
"1000"
|
|
], {
|
|
fetchImpl: async (url, init) => {
|
|
calls.push({ url: String(url), init, body: init?.body ? JSON.parse(String(init.body)) : null });
|
|
if (String(url).endsWith("/v1/agent/chat")) {
|
|
return new Response(JSON.stringify({ accepted: true, status: "running", traceId: "trc_test", resultUrl: "/v1/agent/chat/result/trc_test" }), { status: 202 });
|
|
}
|
|
return new Response(JSON.stringify({ status: "completed", traceId: "trc_test", conversationId: "cnv_test", reply: { role: "assistant", content: "hi" } }), { status: 200 });
|
|
},
|
|
sleep: async () => {}
|
|
});
|
|
|
|
assert.equal(result.exitCode, 0);
|
|
assert.equal(calls[0].url, "http://web.test/v1/agent/chat");
|
|
assert.equal(calls[0].init.headers.prefer, "respond-async");
|
|
assert.equal(calls[0].body.shortConnection, true);
|
|
assert.equal(calls[1].url, "http://web.test/v1/agent/chat/result/trc_test");
|
|
assert.equal(result.payload.traceId, "trc_test");
|
|
assert.equal(result.payload.result.body.status, "completed");
|
|
assert.equal(result.payload.result.body.assistantText, "hi");
|
|
assert.equal(result.payload.result.body.reply.content, "hi");
|
|
});
|
|
|
|
test("hwlab-cli client agent trace shows assistant stream text in compact output", async () => {
|
|
const result = await runHwlabCli(["client", "agent", "trace", "trc_test", "--base-url", "http://web.test", "--cookie", "hwlab_session=session-a"], {
|
|
fetchImpl: async () => new Response(JSON.stringify({
|
|
status: "completed",
|
|
traceId: "trc_test",
|
|
assistantStreams: [{ status: "streaming", text: "hello from agent", chunkCount: 3 }],
|
|
events: [{ status: "completed" }]
|
|
}), { status: 200 })
|
|
});
|
|
|
|
assert.equal(result.exitCode, 0);
|
|
assert.equal(result.payload.body.assistantText, "hello from agent");
|
|
assert.equal(result.payload.body.assistantStreams[0].text, "hello from agent");
|
|
assert.equal(result.payload.body.eventsCount, 1);
|
|
});
|
|
|
|
test("hwlab-cli client agent trace auto logs in when protected trace has no local session", async () => {
|
|
const cwd = await mkdtemp(path.join(os.tmpdir(), "hwlab-cli-client-auth-"));
|
|
const calls: any[] = [];
|
|
const result = await runHwlabCli([
|
|
"client",
|
|
"agent",
|
|
"trace",
|
|
"trc_protected",
|
|
"--base-url",
|
|
"http://web.test",
|
|
"--username",
|
|
"admin"
|
|
], {
|
|
cwd,
|
|
env: { HWLAB_PASSWORD: "secret-password" },
|
|
fetchImpl: async (url, init) => {
|
|
calls.push({ url: String(url), init, body: init?.body ? JSON.parse(String(init.body)) : null });
|
|
if (String(url).endsWith("/auth/login")) {
|
|
return new Response(JSON.stringify({ authenticated: true, user: { username: "admin" } }), {
|
|
status: 200,
|
|
headers: { "set-cookie": "hwlab_session=session-auto; Path=/; HttpOnly" }
|
|
});
|
|
}
|
|
return new Response(JSON.stringify({ status: "completed", traceId: "trc_protected", events: [] }), { status: 200 });
|
|
},
|
|
now: () => "2026-05-31T07:20:00.000Z"
|
|
});
|
|
|
|
assert.equal(result.exitCode, 0);
|
|
assert.deepEqual(calls.map((call) => call.url), [
|
|
"http://web.test/auth/login",
|
|
"http://web.test/v1/agent/chat/trace/trc_protected"
|
|
]);
|
|
assert.equal(calls[1].init.headers.cookie, "hwlab_session=session-auto");
|
|
assert.equal(JSON.stringify(result.payload).includes("secret-password"), false);
|
|
const session = JSON.parse(await readFile(path.join(cwd, ".state/hwlab-cli/session.json"), "utf8"));
|
|
assert.equal(session.cookie, "hwlab_session=session-auto");
|
|
});
|
|
|
|
test("hwlab-cli client agent result refreshes expired session after 401 and reports compact result", async () => {
|
|
const cwd = await mkdtemp(path.join(os.tmpdir(), "hwlab-cli-client-auth-refresh-"));
|
|
await runHwlabCli(["client", "auth", "login", "--base-url", "http://web.test", "--username", "admin", "--password", "old"], {
|
|
cwd,
|
|
fetchImpl: async () => new Response(JSON.stringify({ authenticated: true, user: { username: "admin" } }), { status: 200, headers: { "set-cookie": "hwlab_session=session-old; Path=/" } })
|
|
});
|
|
const calls: any[] = [];
|
|
const result = await runHwlabCli([
|
|
"client",
|
|
"agent",
|
|
"result",
|
|
"trc_refresh",
|
|
"--base-url",
|
|
"http://web.test",
|
|
"--username",
|
|
"admin"
|
|
], {
|
|
cwd,
|
|
env: { HWLAB_PASSWORD: "new-password" },
|
|
fetchImpl: async (url, init) => {
|
|
calls.push({ url: String(url), init, body: init?.body ? JSON.parse(String(init.body)) : null });
|
|
if (String(url).endsWith("/auth/login")) {
|
|
return new Response(JSON.stringify({ authenticated: true, user: { username: "admin" } }), {
|
|
status: 200,
|
|
headers: { "set-cookie": "hwlab_session=session-new; Path=/" }
|
|
});
|
|
}
|
|
if (init?.headers?.cookie === "hwlab_session=session-old") {
|
|
return new Response(JSON.stringify({ error: { code: "auth_required" } }), { status: 401 });
|
|
}
|
|
return new Response(JSON.stringify({ status: "completed", traceId: "trc_refresh", reply: { role: "assistant", content: "done" } }), { status: 200 });
|
|
},
|
|
sleep: async () => {},
|
|
now: () => "2026-05-31T07:21:00.000Z"
|
|
});
|
|
|
|
assert.equal(result.exitCode, 0);
|
|
assert.deepEqual(calls.map((call) => call.url), [
|
|
"http://web.test/v1/agent/chat/result/trc_refresh",
|
|
"http://web.test/auth/login",
|
|
"http://web.test/v1/agent/chat/result/trc_refresh"
|
|
]);
|
|
assert.equal(calls[0].init.headers.cookie, "hwlab_session=session-old");
|
|
assert.equal(calls[2].init.headers.cookie, "hwlab_session=session-new");
|
|
assert.equal(result.payload.action, "client.agent.result");
|
|
assert.equal(result.payload.body.assistantText, "done");
|
|
});
|
|
|
|
test("hwlab-cli client protected agent commands return structured auth diagnosis for forbidden trace", async () => {
|
|
const result = await runHwlabCli(["client", "agent", "trace", "trc_forbidden", "--base-url", "http://web.test", "--cookie", "hwlab_session=session-a"], {
|
|
fetchImpl: async () => new Response(JSON.stringify({ error: { code: "agent_session_owner_required" } }), { status: 403 })
|
|
});
|
|
|
|
assert.equal(result.exitCode, 1);
|
|
assert.equal(result.payload.httpStatus, 403);
|
|
assert.equal(result.payload.authDiagnosis.code, "auth_forbidden");
|
|
assert.equal(result.payload.authDiagnosis.cookieSource, "explicit");
|
|
assert.match(result.payload.authDiagnosis.message, /无权访问/u);
|
|
assert.equal(result.payload.request.url, "http://web.test/v1/agent/chat/trace/trc_forbidden");
|
|
assert.match(result.payload.authDiagnosis.nextCommands[1], /client auth login/u);
|
|
});
|
|
|
|
test("hwlab-cli client protected agent command reports missing credentials with state visibility", async () => {
|
|
const cwd = await mkdtemp(path.join(os.tmpdir(), "hwlab-cli-client-auth-missing-"));
|
|
const result = await runHwlabCli(["client", "agent", "result", "trc_missing_auth", "--base-url", "http://web.test"], {
|
|
cwd,
|
|
fetchImpl: async () => new Response(JSON.stringify({ ok: false, error: { code: "auth_required" } }), { status: 401 })
|
|
});
|
|
|
|
assert.equal(result.exitCode, 1);
|
|
assert.equal(result.payload.action, "client.agent.result");
|
|
assert.equal(result.payload.auth.required, true);
|
|
assert.equal(result.payload.auth.localSession.exists, false);
|
|
assert.equal(result.payload.authDiagnosis.code, "auth_credentials_missing");
|
|
assert.equal(result.payload.authDiagnosis.localSession.ignoredReason, "state_file_missing");
|
|
assert.match(result.payload.authDiagnosis.nextCommands[1], /--password-env HWLAB_PASSWORD/u);
|
|
assert.equal(result.payload.request.httpStatus, 401);
|
|
});
|
|
|
|
test("hwlab-cli client agent inspect builds protected inspect query", async () => {
|
|
const calls: any[] = [];
|
|
const result = await runHwlabCli([
|
|
"client",
|
|
"agent",
|
|
"inspect",
|
|
"--trace-id",
|
|
"trc_inspect",
|
|
"--conversation-id",
|
|
"cnv_inspect",
|
|
"--session-id",
|
|
"ses_inspect",
|
|
"--thread-id",
|
|
"thread-1",
|
|
"--base-url",
|
|
"http://web.test",
|
|
"--cookie",
|
|
"hwlab_session=session-a"
|
|
], {
|
|
fetchImpl: async (url, init) => {
|
|
calls.push({ url: String(url), init });
|
|
return new Response(JSON.stringify({ ok: true, status: "found", latestTraceId: "trc_inspect" }), { status: 200 });
|
|
}
|
|
});
|
|
|
|
assert.equal(result.exitCode, 0);
|
|
assert.equal(calls[0].url, "http://web.test/v1/agent/chat/inspect?traceId=trc_inspect&conversationId=cnv_inspect&sessionId=ses_inspect&threadId=thread-1");
|
|
assert.equal(calls[0].init.headers.cookie, "hwlab_session=session-a");
|
|
assert.equal(result.payload.action, "client.agent.inspect");
|
|
assert.equal(result.payload.body.status, "found");
|
|
});
|
|
|
|
test("hwlab-cli client harness submits waits and audits trace friction", async () => {
|
|
const calls: any[] = [];
|
|
const result = await runHwlabCli([
|
|
"client",
|
|
"harness",
|
|
"submit",
|
|
"--base-url",
|
|
"http://web.test",
|
|
"--cookie",
|
|
"hwlab_session=session-a",
|
|
"--message",
|
|
"hello",
|
|
"--trace-id",
|
|
"trc_harness",
|
|
"--conversation-id",
|
|
"cnv_harness"
|
|
], {
|
|
fetchImpl: async (url, init) => {
|
|
calls.push({ url: String(url), init, body: JSON.parse(String(init?.body ?? "{}")) });
|
|
return new Response(JSON.stringify({
|
|
accepted: true,
|
|
status: "running",
|
|
resultUrl: "/v1/agent/chat/result/trc_harness",
|
|
traceUrl: "/v1/agent/chat/trace/trc_harness"
|
|
}), { status: 202 });
|
|
}
|
|
});
|
|
assert.equal(result.exitCode, 0);
|
|
assert.equal(result.payload.action, "client.harness.submit");
|
|
assert.equal(calls[0].url, "http://web.test/v1/agent/chat");
|
|
assert.equal(calls[0].init.headers.prefer, "respond-async");
|
|
assert.equal(calls[0].body.shortConnection, true);
|
|
|
|
let polls = 0;
|
|
const wait = await runHwlabCli([
|
|
"client",
|
|
"harness-opt",
|
|
"wait",
|
|
"trc_harness",
|
|
"--base-url",
|
|
"http://web.test",
|
|
"--cookie",
|
|
"hwlab_session=session-a",
|
|
"--poll-interval-ms",
|
|
"1",
|
|
"--timeout-ms",
|
|
"1000"
|
|
], {
|
|
fetchImpl: async () => {
|
|
polls += 1;
|
|
return new Response(JSON.stringify(polls === 1
|
|
? { status: "running", traceId: "trc_harness" }
|
|
: { status: "completed", traceId: "trc_harness", reply: { content: "done" } }), { status: 200 });
|
|
},
|
|
sleep: async () => {}
|
|
});
|
|
assert.equal(wait.exitCode, 0);
|
|
assert.equal(wait.payload.timedOut, false);
|
|
assert.equal(wait.payload.timeoutMsRequested, 1000);
|
|
assert.equal(wait.payload.timeoutMsEffective, 1000);
|
|
assert.equal(wait.payload.waitPolicy.remotePassthroughSafe, true);
|
|
assert.equal(wait.payload.body.assistantText, "done");
|
|
|
|
const cappedWait = await runHwlabCli([
|
|
"client",
|
|
"harness",
|
|
"wait",
|
|
"trc_harness",
|
|
"--base-url",
|
|
"http://web.test",
|
|
"--cookie",
|
|
"hwlab_session=session-a",
|
|
"--poll-interval-ms",
|
|
"1000",
|
|
"--timeout-ms",
|
|
"60000"
|
|
], {
|
|
fetchImpl: async () => new Response(JSON.stringify({ status: "completed", traceId: "trc_harness", reply: { content: "done" } }), { status: 200 }),
|
|
sleep: async () => {}
|
|
});
|
|
assert.equal(cappedWait.exitCode, 0);
|
|
assert.equal(cappedWait.payload.status, "succeeded");
|
|
assert.equal(cappedWait.payload.timeoutMsRequested, 60000);
|
|
assert.equal(cappedWait.payload.timeoutMsEffective, 50000);
|
|
assert.equal(cappedWait.payload.waitPolicy.capped, true);
|
|
assert.ok(cappedWait.payload.waitPolicy.nextCommands.some((command) => command.includes("harness result trc_harness")));
|
|
|
|
const audit = await runHwlabCli([
|
|
"client",
|
|
"harness",
|
|
"audit",
|
|
"trc_harness",
|
|
"--require-bootsharp",
|
|
"--base-url",
|
|
"http://web.test",
|
|
"--cookie",
|
|
"hwlab_session=session-a"
|
|
], {
|
|
fetchImpl: async () => new Response(JSON.stringify({
|
|
traceId: "trc_harness",
|
|
events: [
|
|
{
|
|
type: "tool_call",
|
|
command: "node /app/tools/device-pod-cli.mjs device-pod-a:workspace:/ put User/main.c"
|
|
}
|
|
]
|
|
}), { status: 200 })
|
|
});
|
|
assert.equal(audit.exitCode, 0);
|
|
assert.equal(audit.payload.status, "friction_detected");
|
|
assert.ok(audit.payload.frictionSignals.some((signal) => signal.kind === "long_device_pod_cli_path"));
|
|
assert.ok(audit.payload.frictionSignals.some((signal) => signal.kind === "workspace_put_text_edit"));
|
|
assert.ok(audit.payload.frictionSignals.some((signal) => signal.kind === "missing_bootsharp"));
|
|
});
|
|
|
|
test("hwlab-cli client harness audit treats hwpod bootsharp flow as clean", async () => {
|
|
const audit = await runHwlabCli([
|
|
"client",
|
|
"harness",
|
|
"audit",
|
|
"trc_hwpod",
|
|
"--require-bootsharp",
|
|
"--base-url",
|
|
"http://web.test",
|
|
"--cookie",
|
|
"hwlab_session=session-a"
|
|
], {
|
|
fetchImpl: async () => new Response(JSON.stringify({
|
|
traceId: "trc_hwpod",
|
|
events: [
|
|
{ type: "tool_call", command: "hwpod bootsharp --pod-id D601-F103-V2 --api-base-url http://api.test --session-token session-a" },
|
|
{ type: "tool_call", command: "hwpod D601-F103-V2:workspace:/ build status job_123 --api-base-url http://api.test --session-token session-a" }
|
|
]
|
|
}), { status: 200 })
|
|
});
|
|
assert.equal(audit.exitCode, 0);
|
|
assert.equal(audit.payload.status, "clean");
|
|
assert.equal(audit.payload.summary.hasHwpod, true);
|
|
assert.equal(audit.payload.summary.hasBootsharp, true);
|
|
});
|
|
|
|
test("hwlab-cli client workbench summary probes Cloud Web non-visual surfaces", async () => {
|
|
const seen: string[] = [];
|
|
const result = await runHwlabCli(["client", "workbench", "summary", "--base-url", "http://web.test", "--cookie", "session-a", "--pod-id", "device-pod-71-freq"], {
|
|
fetchImpl: async (url) => {
|
|
seen.push(String(url));
|
|
return new Response(JSON.stringify({ ok: true, status: "ok" }), { status: 200 });
|
|
}
|
|
});
|
|
|
|
assert.equal(result.exitCode, 0);
|
|
assert.equal(seen.includes("http://web.test/health/live"), true);
|
|
assert.equal(seen.includes("http://web.test/v1"), true);
|
|
assert.equal(seen.includes("http://web.test/v1/device-pods"), true);
|
|
assert.equal(seen.includes("http://web.test/v1/device-pods/device-pod-71-freq/status"), true);
|
|
assert.equal(result.payload.serviceRuntime, undefined);
|
|
});
|
|
|
|
test("hwlab-cli client request covers arbitrary Cloud Web same-origin API routes", async () => {
|
|
const calls: any[] = [];
|
|
const result = await runHwlabCli([
|
|
"client",
|
|
"request",
|
|
"GET",
|
|
"/v1/access/status",
|
|
"--base-url",
|
|
"http://web.test",
|
|
"--cookie",
|
|
"hwlab_session=session-a",
|
|
"--trace-id",
|
|
"trc_request"
|
|
], {
|
|
fetchImpl: async (url, init) => {
|
|
calls.push({ url: String(url), init });
|
|
return new Response(JSON.stringify({ ok: true, authenticated: true, actor: { username: "admin" }, roles: ["admin"] }), { status: 200 });
|
|
}
|
|
});
|
|
|
|
assert.equal(result.exitCode, 0);
|
|
assert.equal(calls[0].url, "http://web.test/v1/access/status");
|
|
assert.equal(calls[0].init.headers.cookie, "hwlab_session=session-a");
|
|
assert.equal(calls[0].init.headers["x-trace-id"], "trc_request");
|
|
assert.equal(result.payload.action, "client.request");
|
|
assert.equal(result.payload.route.path, "/v1/access/status");
|
|
assert.equal(result.payload.body.actor.username, "admin");
|
|
assert.equal(result.payload.body.fullBodyAvailable, true);
|
|
});
|
|
|
|
test("hwlab-cli client request compacts array responses by default", async () => {
|
|
const result = await runHwlabCli([
|
|
"client",
|
|
"request",
|
|
"GET",
|
|
"/v1/device-pods",
|
|
"--base-url",
|
|
"http://web.test",
|
|
"--cookie",
|
|
"hwlab_session=session-a"
|
|
], {
|
|
fetchImpl: async () => new Response(JSON.stringify([{ id: "pod-1", status: "ready" }, { id: "pod-2", status: "ready" }]), { status: 200 })
|
|
});
|
|
|
|
assert.equal(result.exitCode, 0);
|
|
assert.equal(result.payload.body.itemCount, 2);
|
|
assert.equal(result.payload.body.items[0].id, "pod-1");
|
|
assert.equal(result.payload.body.fullBodyAvailable, true);
|
|
});
|
|
|
|
test("hwlab-cli client rpc mirrors Cloud Web JSON-RPC envelope metadata", async () => {
|
|
const calls: any[] = [];
|
|
const result = await runHwlabCli([
|
|
"client",
|
|
"rpc",
|
|
"system.health",
|
|
"--base-url",
|
|
"http://web.test",
|
|
"--cookie",
|
|
"hwlab_session=session-a",
|
|
"--trace-id",
|
|
"trc_rpc",
|
|
"--id",
|
|
"req_rpc",
|
|
"--params-json",
|
|
"{}"
|
|
], {
|
|
fetchImpl: async (url, init) => {
|
|
calls.push({ url: String(url), init, body: JSON.parse(String(init?.body ?? "{}")) });
|
|
return new Response(JSON.stringify({ jsonrpc: "2.0", id: "req_rpc", result: { status: "ok" }, meta: { traceId: "trc_rpc", serviceId: "hwlab-cloud-api", environment: "dev" } }), { status: 200 });
|
|
}
|
|
});
|
|
|
|
assert.equal(result.exitCode, 0);
|
|
assert.equal(calls[0].url, "http://web.test/json-rpc");
|
|
assert.equal(calls[0].init.headers.cookie, "hwlab_session=session-a");
|
|
assert.equal(calls[0].body.method, "system.health");
|
|
assert.deepEqual(calls[0].body.params, {});
|
|
assert.deepEqual(calls[0].body.meta, { traceId: "trc_rpc", serviceId: "hwlab-cloud-web", environment: "dev" });
|
|
assert.equal(result.payload.action, "client.rpc");
|
|
assert.equal(result.payload.rpcMethod, "system.health");
|
|
assert.equal(result.payload.body.result.status, "ok");
|
|
assert.equal(result.payload.body.fullBodyAvailable, true);
|
|
});
|
|
|
|
test("hwlab-cli client request rejects absolute URLs", async () => {
|
|
const result = await runHwlabCli(["client", "request", "GET", "http://internal.test/v1", "--base-url", "http://web.test"], {
|
|
fetchImpl: async () => new Response("{}", { status: 200 })
|
|
});
|
|
|
|
assert.equal(result.exitCode, 1);
|
|
assert.equal(result.payload.error.code, "invalid_request_path");
|
|
});
|
|
|
|
test("hwlab-cli client request keeps command visibility on transport failure", async () => {
|
|
const result = await runHwlabCli(["client", "request", "GET", "/v1/access/status", "--base-url", "http://web.test", "--public"], {
|
|
fetchImpl: async () => {
|
|
throw Object.assign(new Error("connect ECONNREFUSED"), { name: "FetchError" });
|
|
}
|
|
});
|
|
|
|
assert.equal(result.exitCode, 1);
|
|
assert.equal(result.payload.action, "client.request");
|
|
assert.equal(result.payload.httpStatus, 0);
|
|
assert.equal(result.payload.request.url, "http://web.test/v1/access/status");
|
|
assert.equal(result.payload.request.transportError.code, "request_failed");
|
|
assert.equal(result.payload.body.error.code, "request_failed");
|
|
});
|