import assert from "node:assert/strict"; import { test } from "bun:test"; import { hwlabRuntimeLaneSpecForNode } from "../hwlab-node-lanes"; import { parseNodeWebProbeObserveOptions } from "./web-probe-observe"; const spec = hwlabRuntimeLaneSpecForNode("v03", "NC01"); function parseRealtimeCommand(extra: string[]) { return parseNodeWebProbeObserveOptions( ["command", "--type", "validateRealtimeFanout", ...extra], "NC01", "v03", spec, "webobs-fixture", null, ); } test("validateRealtimeFanout parses the YAML profile and two independent prompts", () => { const options = parseRealtimeCommand([ "--profile", "pure-kafka-live", "--provider", "gpt.pika", "--text", "first turn", "--second-text", "second turn", ]); assert.equal(options.commandType, "validateRealtimeFanout"); assert.equal(options.commandProfile, "pure-kafka-live"); assert.equal(options.commandProvider, "gpt.pika"); assert.equal(options.commandText, "first turn"); assert.equal(options.commandSecondText, "second turn"); assert.deepEqual(spec.webProbe?.realtimeFanoutProfiles?.["pure-kafka-live"]?.debugStreams, ["stdio", "agentrun", "hwlab"]); assert.equal(spec.webProbe?.realtimeFanoutProfiles?.["pure-kafka-live"]?.fromBeginning, false); assert.deepEqual(spec.webProbe?.realtimeFanoutProfiles?.["pure-kafka-live"]?.expectedProductSse, { deliverySemantics: "kafka-retention-then-live", liveOnly: false, replay: true, replaySupported: true, lossPossible: false, refreshHandoff: true, replayPriorTraceOnReconnect: true, }); assert.deepEqual(spec.webProbe?.realtimeFanoutProfiles?.["pure-kafka-live"]?.requiredEventTypes, { agentrun: ["user_message", "assistant_message", "terminal_status"], hwlab: ["user", "assistant", "terminal"], }); assert.deepEqual(spec.webProbe?.realtimeFanoutProfiles?.["pure-kafka-live"]?.conditionalEventTypePairs, { "tool-call": { agentrun: "tool_call", hwlab: "tool" }, "command-output": { agentrun: "command_output", hwlab: "status" }, }); }); test("validateRealtimeFanout refuses body as a substitute for the second prompt", () => { assert.throws( () => parseRealtimeCommand([ "--profile", "pure-kafka-live", "--provider", "gpt.pika", "--text", "first turn", "--body", "not a second prompt", ]), /requires --second-text/u, ); }); test("validateRealtimeFanout rejects profiles absent from the owning YAML", () => { assert.throws( () => parseRealtimeCommand([ "--profile", "projection-mode", "--provider", "gpt.pika", "--text", "first turn", "--second-text", "second turn", ]), /profile is not declared by the owning YAML/u, ); }); test("validateExistingSessionRefresh parses an existing session without prompts", () => { const options = parseNodeWebProbeObserveOptions( ["command", "--type", "validateExistingSessionRefresh", "--profile", "pure-kafka-live", "--session-id", "ses_existing"], "NC01", "v03", spec, "webobs-fixture", null, ); assert.equal(options.commandType, "validateExistingSessionRefresh"); assert.equal(options.commandProfile, "pure-kafka-live"); assert.equal(options.commandSessionId, "ses_existing"); assert.equal(options.commandText, null); }); test("validateWorkbenchKafkaDebugReplay is a YAML-enabled argument-free typed command", () => { const options = parseNodeWebProbeObserveOptions( ["command", "--type", "validateWorkbenchKafkaDebugReplay"], "NC01", "v03", spec, "webobs-fixture", null, ); assert.equal(options.commandType, "validateWorkbenchKafkaDebugReplay"); assert.deepEqual(spec.webProbe?.workbenchKafkaDebugReplay, { enabled: true, topic: "hwlab.event.debug.v1", groupPrefix: "hwlab-v03-workbench-isolated-debug", completionTimeoutMs: 30_000, }); }); test("validateWorkbenchTraceReadability 是由独立 YAML 启用的无参数 typed command", () => { const options = parseNodeWebProbeObserveOptions( ["command", "--type", "validateWorkbenchTraceReadability"], "NC01", "v03", spec, "webobs-fixture", null, ); assert.equal(options.commandType, "validateWorkbenchTraceReadability"); assert.equal(spec.webProbe?.workbenchTraceReadability?.terminalTimeoutMs, 480_000); assert.equal(spec.webProbe?.workbenchTraceReadability?.runningCardTimeoutMs, 15_000); }); test("observe start selects the YAML public origin semantically", () => { const options = parseNodeWebProbeObserveOptions( ["start", "--origin", "public"], "NC01", "v03", spec, null, null, ); assert.equal(options.originName, "public"); assert.equal(options.originMode, "public"); assert.equal(options.url, spec.publicWebUrl); assert.equal(options.browserProxyMode, "auto"); assert.equal(options.browserProxyModeSource, "yaml-origin"); }); test("observe start refuses URL-based internal/public selection ambiguity", () => { assert.throws( () => parseNodeWebProbeObserveOptions( ["start", "--origin", "public", "--url", "http://127.0.0.1:4173"], "NC01", "v03", spec, null, null, ), /accepts --origin or --url, not both/u, ); }); test("observe command inherits the indexed origin and rejects replacement origins", () => { const indexed = { id: "webobs-fixture", node: "NC01", lane: "v03", workspace: spec.workspace, stateDir: ".state/web-observe/NC01/v03/2026/07/10/20260710T000000Z_workbench_webobs-fixture", url: "http://10.43.99.7:8080", originName: "internal" as const, originMode: "k8s-service-cluster-ip" as const, originConfigPath: "config/hwlab-node-lanes.yaml#webProbe.origins.internal", browserProxyMode: "direct" as const, browserProxyModeSource: "yaml-origin" as const, targetPath: "/workbench", status: "running", pid: 42, startedAt: "2026-07-10T00:00:00.000Z", updatedAt: "2026-07-10T00:00:01.000Z", }; const inherited = parseNodeWebProbeObserveOptions( ["command", "--type", "mark", "--label", "checkpoint"], "NC01", "v03", spec, "webobs-fixture", indexed, ); assert.equal(inherited.originName, "internal"); assert.equal(inherited.url, indexed.url); assert.equal(inherited.browserProxyMode, "direct"); assert.throws( () => parseNodeWebProbeObserveOptions( ["command", "--origin", "public", "--type", "mark", "--label", "checkpoint"], "NC01", "v03", spec, "webobs-fixture", indexed, ), /later actions inherit the observer origin/u, ); });