Files
pikasTech-unidesk/scripts/src/hwlab-node/web-probe-observe-collect.test.ts
T
2026-07-11 17:06:07 +02:00

276 lines
9.7 KiB
TypeScript

import { mkdirSync, mkdtempSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import { tmpdir } from "node:os";
import { describe, expect, test } from "bun:test";
import type { CommandResult } from "../command";
import type { NodeWebProbeObserveOptions } from "./entry";
import { buildNodeWebProbeObserveCollectPayload } from "./web-probe-observe-collect";
import { withWebObserveCollectRendered } from "../hwlab-node-web-observe-render";
import { nodeWebObserveCollectNodeScript } from "./web-observe-scripts";
describe("web-probe observe collect child JSON recovery", () => {
test("recovers collect JSON from trans stdout dump", () => {
const dir = join(tmpdir(), `unidesk-collect-recovery-${Date.now()}-${process.pid}`);
mkdirSync(dir, { recursive: true });
const dumpPath = join(dir, "stdout.json");
writeFileSync(dumpPath, JSON.stringify({
ok: true,
command: "web-probe-observe collect",
view: "performance-summary",
stateDir: "/remote/state",
renderedText: "WEB PERFORMANCE SUMMARY\npayloads=2 events=3 groups=1",
valuesRedacted: true,
}));
const summary = {
stdout: {
stream: "stdout",
truncated: true,
dumpPath,
valuesRedacted: true,
},
valuesRedacted: true,
};
const payload = buildNodeWebProbeObserveCollectPayload(collectOptions(), { workspace: "/workspace" }, {
command: ["trans", "JD01:/workspace", "sh"],
cwd: "/repo",
exitCode: 0,
stdout: "tail only WEB PERFORMANCE SUMMARY",
stderr: `UNIDESK_SSH_TRUNCATION_SUMMARY ${JSON.stringify(summary)}\n`,
signal: null,
timedOut: false,
} satisfies CommandResult);
expect(payload.ok).toBe(true);
expect(payload.status).toBe("collected");
const collect = payload.collect as Record<string, unknown>;
expect(collect.view).toBe("performance-summary");
expect(collect.renderedText).toContain("payloads=2");
const recovery = payload.stdoutRecovery as Record<string, unknown>;
expect(recovery.stdoutKind).toBe("ssh-truncation-summary");
expect(recovery.dumpPath).toBe(dumpPath);
expect(recovery.source).toBe("dump");
});
});
test("propagates structured turn-summary not-found status without treating it as collected", () => {
const options = collectOptions();
options.collectView = "turn-summary";
options.collectCommandId = "cmd-missing";
const payload = buildNodeWebProbeObserveCollectPayload(options, { workspace: "/workspace" }, {
command: ["trans", "JD01:/workspace", "sh"],
cwd: "/repo",
exitCode: 0,
stdout: JSON.stringify({
ok: false,
command: "web-probe-observe collect",
view: "turn-summary",
status: "not-found",
stateDir: "/remote/state",
filter: { mode: "exact-command-id", requestedCommandId: "cmd-missing", matchedCount: 0 },
error: { code: "turn-summary-command-not-found" },
rows: [],
renderedText: "NOT_FOUND turn-summary-command-not-found; unrelated turns were not returned",
valuesRedacted: true,
}),
stderr: "",
signal: null,
timedOut: false,
} satisfies CommandResult);
expect(payload.ok).toBe(false);
expect(payload.status).toBe("not-found");
const collect = payload.collect as Record<string, unknown>;
expect(collect.status).toBe("not-found");
expect(collect.rows).toEqual([]);
});
test("collects an exact failed command artifact without treating artifact failure as collect failure", () => {
const options = collectOptions();
options.collectView = "files";
options.collectFile = "commands/failed/cmd-fixture.json";
const failedArtifact = {
ok: false,
commandId: "cmd-fixture",
type: "validateRealtimeFanout",
failedAt: "2026-07-10T12:00:00.000Z",
error: {
name: "Error",
message: "terminal event arrived before subscriber disconnect",
details: {
reportPath: "artifacts/realtime-fanout/cmd-fixture/report.json",
reportSha256: "sha256:fixture-report",
sessionToken: "must-not-render",
},
},
failureSample: { ok: true, sampleSeq: 22, valuesRedacted: true },
};
const payload = buildNodeWebProbeObserveCollectPayload(options, { workspace: "/workspace" }, {
command: ["trans", "NC01:/workspace", "sh"],
cwd: "/repo",
exitCode: 0,
stdout: JSON.stringify(failedArtifact),
stderr: "",
signal: null,
timedOut: false,
} satisfies CommandResult);
expect(payload.ok).toBe(true);
expect(payload.status).toBe("collected");
expect(payload.degradedReason).toBeNull();
const recovery = payload.stdoutRecovery as Record<string, unknown>;
expect(recovery.source).toBe("stdout");
expect(recovery.stdoutContractAccepted).toBe(true);
const collect = payload.collect as Record<string, any>;
expect(collect.ok).toBe(true);
expect(collect.artifactOutcome).toEqual({
ok: false,
commandId: "cmd-fixture",
type: "validateRealtimeFanout",
bucket: "failed",
valuesRedacted: true,
});
expect(collect.file.jsonContent.commandId).toBe("cmd-fixture");
expect(collect.file.jsonContent.error.message).toBe("terminal event arrived before subscriber disconnect");
expect(collect.file.jsonContent.error.details.sessionToken).toBe("[redacted]");
const rendered = withWebObserveCollectRendered(payload);
expect(String(rendered.renderedText)).toContain("cmd-fixture");
expect(String(rendered.renderedText)).toContain("terminal event arrived before subscriber disconnect");
});
test("accepts the standard file collect envelope without requiring a summary view", () => {
const options = collectOptions();
options.collectView = "files";
options.collectFile = "commands/failed/cmd-fixture.json";
const payload = buildNodeWebProbeObserveCollectPayload(options, { workspace: "/workspace" }, {
command: ["trans", "NC01:/workspace", "sh"],
cwd: "/repo",
exitCode: 0,
stdout: JSON.stringify({
ok: true,
command: "web-probe-observe collect",
stateDir: "/remote/state",
mode: "file",
requestedFile: options.collectFile,
resolvedFile: options.collectFile,
file: {
relative: options.collectFile,
byteCount: 21885,
sha256: "sha256:fixture",
jsonSummary: { topLevelKeys: ["ok", "commandId", "type", "failedAt", "error"] },
},
valuesRedacted: true,
}),
stderr: "",
signal: null,
timedOut: false,
} satisfies CommandResult);
expect(payload.ok).toBe(true);
expect(payload.status).toBe("collected");
expect(payload.degradedReason).toBeNull();
const collect = payload.collect as Record<string, any>;
expect(collect.mode).toBe("file");
expect(collect.file.byteCount).toBe(21885);
});
test("accepts the structured files listing envelope when --file is absent", () => {
const options = collectOptions();
options.collectView = "files";
const stateDir = mkdtempSync(join(tmpdir(), "unidesk-web-observe-files-list-"));
writeFileSync(join(stateDir, "manifest.json"), "{}\n");
const child = Bun.spawnSync(["bash", "-lc", nodeWebObserveCollectNodeScript(options.maxFiles, null, null, null)], {
env: { ...process.env, state_dir: stateDir },
stdout: "pipe",
stderr: "pipe",
});
expect(child.exitCode).toBe(0);
const payload = buildNodeWebProbeObserveCollectPayload(options, { workspace: "/workspace" }, {
command: ["trans", "NC01:/workspace", "sh"], cwd: "/repo", exitCode: 0,
stdout: Buffer.from(child.stdout).toString("utf8"),
stderr: Buffer.from(child.stderr).toString("utf8"), signal: null, timedOut: false,
} satisfies CommandResult);
expect(payload.ok).toBe(true);
expect(payload.status).toBe("collected");
expect((payload.collect as Record<string, unknown>).mode).toBe("list");
expect((payload.collect as Record<string, unknown>).view).toBe("files");
});
function collectOptions(): NodeWebProbeObserveOptions {
return {
action: "observe",
observeAction: "collect",
id: "webobs-test",
node: "JD01",
lane: "v03",
url: "https://example.test",
targetPath: "/",
viewport: "1920x1080",
browserProxyMode: "auto",
sampleIntervalMs: 1000,
screenshotIntervalMs: 0,
observerRefreshIntervalMs: 1000,
maxSamples: 10,
maxRunSeconds: 60,
commandTimeoutSeconds: 30,
waitMs: 0,
tailLines: 20,
maxFiles: 100,
gcKeepHours: 24,
gcLimit: 10,
confirm: false,
dryRun: false,
collectView: "performance-summary",
collectFile: null,
collectFinding: null,
collectGrep: null,
collectTraceId: null,
collectSampleSeq: null,
collectTimestamp: null,
collectTurn: null,
collectCommandId: null,
collectWindowMs: null,
analyzeArchivePrefix: null,
analyzeTailSamples: null,
full: false,
raw: false,
compactRaw: false,
stateDir: null,
jobId: null,
force: false,
commandType: null,
commandText: null,
commandSecondText: null,
commandPath: null,
commandLabel: null,
commandSessionId: null,
commandProvider: null,
commandProfile: null,
commandAfterRound: null,
commandSeverity: null,
commandAlternateSessionStrategy: null,
commandExpectedSentinelRange: null,
commandExpectedActionWaitMs: null,
commandDurationMs: null,
commandRequireComposerReady: false,
commandWaitProjectManagementReady: false,
commandFindingId: null,
commandBlocking: null,
commandAccountId: null,
commandFromAccountId: null,
commandToAccountId: null,
commandSourceId: null,
commandFileRef: null,
commandFilename: null,
commandTaskRef: null,
commandTaskId: null,
commandField: null,
commandLink: null,
commandTitle: null,
commandBody: null,
commandStatus: null,
commandHwpodId: null,
commandNodeId: null,
commandWorkspaceRoot: null,
commandRoot: null,
};
}