98 lines
4.8 KiB
TypeScript
98 lines
4.8 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { mkdtemp, mkdir, writeFile } from "node:fs/promises";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
import { test } from "bun:test";
|
|
|
|
import { runDevicePodCli } from "./src/device-pod-cli-lib.ts";
|
|
|
|
test("device-pod-cli lists cloud-api authority and ignores local profile routes", async () => {
|
|
const root = await mkdtemp(path.join(os.tmpdir(), "hwlab-device-pod-cli-"));
|
|
const profileDir = path.join(root, ".device-pod");
|
|
await mkdir(profileDir, { recursive: true });
|
|
await writeFile(path.join(profileDir, "device-pod-71-freq.json"), JSON.stringify({ gatewaySessionId: "gws_local_should_not_be_read", hostWorkspaceRoot: "F:\\legacy" }), "utf8");
|
|
const seen: any[] = [];
|
|
const result = await runDevicePodCli(["--api-base-url", "http://cloud.test", "--session-token", "session-a", "profile", "list"], {
|
|
env: { DEVICE_POD_PROFILE_DIR: profileDir },
|
|
fetchImpl: async (url, init) => {
|
|
seen.push({ url: String(url), init });
|
|
return jsonResponse(200, { ok: true, contractVersion: "device-pod-authority-v1", devicePods: [{ devicePodId: "device-pod-71-freq", profileHash: "sha256:abc", profile: { route: { gatewaySessionId: "redacted" } } }] });
|
|
},
|
|
now: () => "2026-05-29T00:00:00.000Z"
|
|
});
|
|
assert.equal(result.exitCode, 0);
|
|
assert.equal(result.payload.localProfileAuthority, false);
|
|
assert.equal(result.payload.localProfileRead, false);
|
|
assert.equal(seen[0].url, "http://cloud.test/v1/device-pods");
|
|
assert.equal(seen[0].init.headers["x-hwlab-session-token"], "session-a");
|
|
assert.equal(JSON.stringify(result.payload).includes("gws_local_should_not_be_read"), false);
|
|
});
|
|
|
|
test("device-pod-cli selector creates REST job instead of gateway RPC", async () => {
|
|
const seen: any[] = [];
|
|
const result = await runDevicePodCli(["--api-base-url", "http://cloud.test", "--session-token", "session-a", "device-pod-71-freq:workspace:/src", "ls"], {
|
|
fetchImpl: async (url, init) => {
|
|
seen.push({ url: String(url), init, body: JSON.parse(String(init?.body ?? "{}")) });
|
|
return jsonResponse(202, { ok: true, status: "running", job: { id: "job_1", status: "running" }, profileHash: "sha256:abc" });
|
|
},
|
|
now: () => "2026-05-29T00:00:00.000Z"
|
|
});
|
|
assert.equal(result.exitCode, 0);
|
|
assert.equal(seen[0].url, "http://cloud.test/v1/device-pods/device-pod-71-freq/jobs");
|
|
assert.deepEqual(seen[0].body, { intent: "workspace.ls", args: { path: "src" } });
|
|
assert.equal(JSON.stringify(seen[0].body).includes("gatewaySessionId"), false);
|
|
});
|
|
|
|
test("device-pod-cli sends reason and lease token for mutating jobs", async () => {
|
|
const seen: any[] = [];
|
|
const result = await runDevicePodCli(["--api-base-url", "http://cloud.test", "--session-token", "session-a", "--lease-token", "lease-a", "--reason", "reset smoke", "device-pod-71-freq:debug-probe", "reset"], {
|
|
fetchImpl: async (url, init) => {
|
|
seen.push({ url: String(url), body: JSON.parse(String(init?.body ?? "{}")) });
|
|
return jsonResponse(202, { ok: true, status: "running", job: { id: "job_reset", status: "running" } });
|
|
},
|
|
now: () => "2026-05-29T00:00:00.000Z"
|
|
});
|
|
assert.equal(result.exitCode, 0);
|
|
assert.deepEqual(seen[0].body, { intent: "debug.reset", args: {}, reason: "reset smoke", leaseToken: "lease-a" });
|
|
});
|
|
|
|
test("device-pod-cli preserves common v0.1 hardware options in REST job args", async () => {
|
|
const seen: any[] = [];
|
|
const result = await runDevicePodCli([
|
|
"--api-base-url", "http://cloud.test",
|
|
"--session-token", "session-a",
|
|
"--reason", "boot log",
|
|
"--lease-token", "lease-a",
|
|
"--capture-uart", "uart/1",
|
|
"--capture-duration-ms", "8000",
|
|
"--port", "COM4",
|
|
"--baud-rate", "921600",
|
|
"device-pod-71-freq:debug-probe",
|
|
"download",
|
|
"start"
|
|
], {
|
|
fetchImpl: async (url, init) => {
|
|
seen.push({ url: String(url), body: JSON.parse(String(init?.body ?? "{}")) });
|
|
return jsonResponse(202, { ok: true, status: "running", job: { id: "job_download", status: "running" } });
|
|
},
|
|
now: () => "2026-05-29T00:00:00.000Z"
|
|
});
|
|
assert.equal(result.exitCode, 0);
|
|
assert.deepEqual(seen[0].body, {
|
|
intent: "debug.download",
|
|
args: { action: "start", captureUart: "uart/1", captureDurationMs: 8000, port: "COM4", baudRate: 921600 },
|
|
reason: "boot log",
|
|
leaseToken: "lease-a"
|
|
});
|
|
});
|
|
|
|
test("device-pod-cli rejects legacy local profile create in formal mode", async () => {
|
|
const result = await runDevicePodCli(["profile", "create", "--pod-id", "device-pod-71-freq"], { now: () => "2026-05-29T00:00:00.000Z" });
|
|
assert.equal(result.exitCode, 1);
|
|
assert.equal(result.payload.error.code, "legacy_profile_create_removed");
|
|
});
|
|
|
|
function jsonResponse(status: number, body: unknown) {
|
|
return new Response(JSON.stringify(body), { status, headers: { "content-type": "application/json" } });
|
|
}
|