Files
pikasTech-unidesk/scripts/src/hwlab-native-development.test.ts
T
2026-07-23 14:12:14 +02:00

152 lines
9.3 KiB
TypeScript

import { expect, test } from "bun:test";
import { hwlabHwpodNativeDevelopmentEnvironment, hwlabNativeDevelopmentEnvironment, hwlabNativeDevelopmentHelp, hwlabNativeDevelopmentSourceCommitRef, hwlabNativeWorkbenchClientEnvironment, parseHwlabNativeCliPayload } from "./hwlab-native-development";
import { hwlabRuntimeLaneSpecForNode } from "./hwlab-node-lanes";
import { resolveConfigRef } from "./ops/config-refs";
test("L1 Workbench exposure uses YAML HTTPS origin and separate bind/probe addresses", () => {
const spec = hwlabRuntimeLaneSpecForNode("v03", "NC01");
const native = spec.nativeDevelopment?.workbench;
expect(native).toBeDefined();
const env = hwlabNativeDevelopmentEnvironment(native!, spec, "0123456789abcdef0123456789abcdef01234567");
expect(env.WORKBENCH_NATIVE_SERVICE_STATE_DIR).toBe(native!.stateDir);
expect(env.WORKBENCH_NATIVE_STATE_FILE).toBe(`${spec.workspace}/${native!.stateFile}`);
expect(env.WORKBENCH_MODE).toBe("agentrun-native");
expect(env.WORKBENCH_CLOUD_API_URL).toBeUndefined();
expect(env.WORKBENCH_CLOUD_API_AUTHORIZATION).toBeUndefined();
expect(env.WORKBENCH_AGENTRUN_TERMINAL_TIMEOUT_MS).toBeUndefined();
expect(env.WORKBENCH_AGENTRUN_POLL_INTERVAL_MS).toBeUndefined();
expect(env.AGENTRUN_MGR_URL).toBe(native!.agentRun.managerUrl);
expect(env.AGENTRUN_API_KEY).toMatch(/^hwl_live_/u);
expect(env.HWLAB_CODE_AGENT_DEFAULT_PROVIDER_PROFILE).toBe("gpt.pika");
expect(env.HWLAB_CODE_AGENT_AGENTRUN_SECRET_NAMESPACE).toBe("agentrun-v02");
expect(env.HWLAB_CODE_AGENT_AGENTRUN_GPT_PIKA_SECRET_NAME).toBe("agentrun-v02-provider-gpt-pika");
expect(env.HWLAB_CODE_AGENT_AGENTRUN_CODEX_SECRET_NAME).toBe("agentrun-v01-provider-codex");
expect(env.HWLAB_CODE_AGENT_AGENTRUN_GITHUB_TOOL_SECRET_NAME).toBe("agentrun-v01-tool-github-pr");
expect(env.HWLAB_CODE_AGENT_AGENTRUN_UNIDESK_SSH_TOOL_SECRET_NAME).toBe("agentrun-v01-tool-unidesk-ssh");
expect(env.HWLAB_CODE_AGENT_AGENTRUN_SOURCE_COMMIT).toBe("0123456789abcdef0123456789abcdef01234567");
expect(env.HWLAB_KAFKA_DIRECT_PUBLISH_ENABLED).toBe("true");
expect(env.HWLAB_WORKBENCH_LIVE_KAFKA_SSE_ENABLED).toBe("true");
expect(env.HWLAB_WORKBENCH_KAFKA_REFRESH_REPLAY_ENABLED).toBe("true");
expect(env.HWLAB_WORKBENCH_KAFKA_DEBUG_REPLAY_ENABLED).toBe("false");
expect(env.HWLAB_WORKBENCH_EMPTY_SESSION_GC_ENABLED).toBe("false");
expect(env.HWLAB_USER_BILLING_CODE_AGENT_ENABLED).toBe("0");
expect(env.HWLAB_WORKBENCH_PROJECTION_REALTIME_ENABLED).toBe("false");
expect(env.HWLAB_KAFKA_BOOTSTRAP_SERVERS).toBe(native!.kafka.bootstrapServers);
expect(native!.kafka.dnsServers).toEqual(["10.43.0.10"]);
expect(env.HWLAB_KAFKA_DNS_SERVERS).toBe("10.43.0.10");
expect(native!.kafka.dnsSearchDomains).toEqual(["cluster.local"]);
expect(env.HWLAB_KAFKA_DNS_SEARCH_DOMAINS).toBe("cluster.local");
expect(env.HWLAB_KAFKA_AGENTRUN_EVENT_TOPIC).toBe("agentrun.event.native-l1.v1");
expect(env.HWLAB_KAFKA_EVENT_TOPIC).toBe("hwlab.event.native-l1.v1");
expect(env.HWLAB_KAFKA_AGENTRUN_EVENT_GROUP_ID).toBe(native!.kafka.directPublishConsumerGroupId);
expect(env.HWLAB_KAFKA_AGENTRUN_EVENT_GROUP_ID).not.toBe(spec.codeAgentRuntime!.kafkaEventBridge!.directPublishConsumerGroupId);
expect(env.HWLAB_KAFKA_CLIENT_ID).toBe(native!.kafka.clientId);
expect(env.HWLAB_KAFKA_HWLAB_EVENT_GROUP_ID).toBe(native!.kafka.hwlabEventConsumerGroupId);
expect(env.HWLAB_KAFKA_HWLAB_EVENT_GROUP_ID).not.toBe(spec.codeAgentRuntime!.kafkaEventBridge!.hwlabEventConsumerGroupId);
expect(env.HWLAB_WORKBENCH_KAFKA_REFRESH_REPLAY_GROUP_PREFIX).toBe(native!.kafka.refreshReplay.groupIdPrefix);
expect(env.HWLAB_WORKBENCH_KAFKA_REFRESH_REPLAY_TIMEOUT_MS).toBe("30000");
expect(env.HWLAB_WORKBENCH_KAFKA_REFRESH_INDEX_ENABLED).toBe("true");
expect(env.HWLAB_WORKBENCH_KAFKA_REFRESH_INDEX_MAX_EVENTS).toBe("100000");
expect(env.HWLAB_WORKBENCH_KAFKA_REFRESH_INDEX_MAX_EVENTS_PER_SESSION).toBe("10000");
expect(env.HWLAB_WORKBENCH_KAFKA_REFRESH_INDEX_BOOTSTRAP_TIMEOUT_MS).toBe("30000");
expect(env.HWLAB_WORKBENCH_KAFKA_REFRESH_INDEX_REBUILD_INTERVAL_MS).toBe("300000");
expect(env.HWLAB_WORKBENCH_KAFKA_REFRESH_INDEX_REBUILD_COOLDOWN_MS).toBe("30000");
expect(native!.runtime).toEqual(spec.codeAgentRuntime!.workbench);
expect(hwlabRuntimeLaneSpecForNode("production", "NC01").codeAgentRuntime!.workbench).toEqual(native!.runtime);
expect(spec.codeAgentRuntime!.kafkaEventBridge!.refreshReplay).toEqual({
groupIdPrefix: "hwlab-v03-cloud-api-sse",
...native!.runtime.refreshReplay,
});
expect(env.WORKBENCH_API_BIND_HOST).toBe(native!.api.bindHost);
expect(env.WORKBENCH_API_PROBE_HOST).toBe(native!.api.probeHost);
expect(env.WORKBENCH_PUBLIC_BASE_URL).toBe("https://lab-dev-workbench.hwpod.com");
expect(env.WORKBENCH_API_IDLE_TIMEOUT_SECONDS).toBe(String(native!.api.idleTimeoutSeconds));
expect(JSON.parse(env.WORKBENCH_WEB_RUNTIME_CONFIG!)).toEqual(native!.web.runtimeConfig);
expect(native!.web.runtimeConfig.workbench.realtimeFeatures).toEqual({ liveKafkaSse: true, kafkaRefreshReplay: true, projectionRealtime: false });
expect(hwlabNativeDevelopmentHelp()).toMatchObject({
ok: true,
command: "hwlab nodes native-development workbench",
usage: expect.arrayContaining([
"bun scripts/cli.ts hwlab nodes native-development workbench start|stop|restart|status|logs --node <node> --lane <lane>",
"bun scripts/cli.ts hwlab nodes native-development hwpod start|stop|restart|status|logs --node <node> --lane <lane>",
]),
});
});
test("L1 Workbench runner source follows the YAML-declared remote branch", () => {
const spec = hwlabRuntimeLaneSpecForNode("v03", "NC01");
expect(hwlabNativeDevelopmentSourceCommitRef(spec)).toBe(`refs/remotes/${spec.sourceWorkspace!.git!.remoteName}/${spec.sourceBranch}`);
expect(hwlabNativeDevelopmentSourceCommitRef(spec)).toBe("refs/remotes/origin/v0.3");
});
test("L1 Workbench client uses the YAML native Web and API endpoints", () => {
const env = hwlabNativeWorkbenchClientEnvironment({
WORKBENCH_NATIVE_API_URL: "http://127.0.0.1:6677",
WORKBENCH_WEB_PROBE_HOST: "127.0.0.1",
WORKBENCH_WEB_PORT: "5174",
HWLAB_RUNTIME_API_URL: "http://agentrun-runtime.invalid",
});
expect(env.HWLAB_RUNTIME_API_URL).toBe("http://127.0.0.1:6677");
expect(env.HWLAB_RUNTIME_WEB_URL).toBe("http://127.0.0.1:5174");
expect(env.HWLAB_RUNTIME_ENDPOINT_LOCKED).toBe("1");
});
test("L1 native CLI preserves typed evidence for empty and incomplete child JSON", () => {
expect(parseHwlabNativeCliPayload(commandResult(""))).toMatchObject({
ok: false,
error: { code: "hwlab_native_cli_output_empty", stdoutKind: "empty", stdoutBytes: 0 },
});
expect(parseHwlabNativeCliPayload(commandResult('{"ok":true'))).toMatchObject({
ok: false,
error: { code: "hwlab_native_cli_output_invalid", stdoutKind: "non-json" },
});
expect(parseHwlabNativeCliPayload(commandResult("", { timedOut: true, exitCode: null, stderr: "timed out" }))).toMatchObject({
ok: false,
error: { code: "hwlab_native_cli_timeout", timedOut: true, stderrTail: "timed out" },
});
expect(parseHwlabNativeCliPayload(commandResult('{"ok":true,"status":"passed"}'))).toEqual({ ok: true, status: "passed" });
});
function commandResult(stdout: string, overrides: Partial<{ stderr: string; exitCode: number | null; signal: NodeJS.Signals | null; timedOut: boolean }> = {}) {
return {
stdout,
stderr: overrides.stderr ?? "",
exitCode: overrides.exitCode ?? 0,
signal: overrides.signal ?? null,
timedOut: overrides.timedOut ?? false,
};
}
test("L1 HWPOD spec registry resolves PostgreSQL authority and frozen built-ins from owning YAML", () => {
const native = hwlabRuntimeLaneSpecForNode("v03", "NC01").nativeDevelopment?.hwpod;
expect(native).toBeDefined();
expect(native!.specRegistry.database).toEqual({
sourceRef: "hwlab/nc01-v03-cloud-api-db.env",
sourceKey: "DATABASE_URL",
schema: "hwpod",
table: "runtime_specs",
connectionTimeoutMs: 5000,
});
expect(native!.healthTimeoutMs).toBe(6000);
expect(native!.nodeAuth).toEqual({ sourceRef: "hwlab/nc01-v03-hwpod-node.env", sourceKey: "HWLAB_HWPOD_NODE_WS_TOKEN" });
expect(native!.web.accessProfile).toEqual({ id: "hwpod-native-l1", allowedNavIds: ["admin.hwpodGroups"], startPath: "/hwpods/devices" });
const env = hwlabHwpodNativeDevelopmentEnvironment(native!);
expect(env.HWPOD_NODE_WS_TOKEN).toBeTruthy();
expect(env.HWPOD_NODE_OPS_API_URL).toBe("http://127.0.0.1:6681");
expect(env.HWPOD_RUNTIME_API_URL).toBeUndefined();
expect(env.HWPOD_RUNTIME_API_AUTHORIZATION).toBeUndefined();
expect(JSON.parse(env.HWPOD_WEB_RUNTIME_CONFIG!)).toEqual(native!.web.runtimeConfig);
expect(native!.web.runtimeConfig).toEqual(hwlabRuntimeLaneSpecForNode("v03", "NC01").nativeDevelopment!.workbench.web.runtimeConfig);
expect(JSON.parse(env.HWPOD_WEB_ACCESS_PROFILE_JSON!)).toEqual(native!.web.accessProfile);
expect(native!.specRegistry.builtInConfigRefs).toHaveLength(2);
const resolved = resolveConfigRef(native!.specRegistry.builtInConfigRefs[0], "HWPOD built-in spec");
expect(resolved.value).toMatchObject({ kind: "Hwpod", metadata: { name: "constart-71freq-c" } });
const qemu = resolveConfigRef(native!.specRegistry.builtInConfigRefs[1], "QEMU HWPOD built-in spec");
expect(qemu.value).toMatchObject({
kind: "Hwpod",
metadata: { name: "d601-vm-stm32f405-qemu" },
spec: { nodeBinding: { nodeId: "node-d601-vm-hwpod-qemu" }, targetDevice: { simulation: true } },
});
});