181 lines
7.4 KiB
TypeScript
181 lines
7.4 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { mkdtemp, writeFile } from "node:fs/promises";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import { test } from "bun:test";
|
|
|
|
import { resolveCliChildJsonCommandResult, resolveCliChildJsonObject } from "./cli-child-json-recovery";
|
|
|
|
test("child JSON recovery reads full JSON from CLI stdout dump wrapper", async () => {
|
|
const dir = await mkdtemp(join(tmpdir(), "unidesk-cli-child-json-dump-"));
|
|
const dumpPath = join(dir, "dump.json");
|
|
await writeFile(dumpPath, JSON.stringify({ ok: true, data: { contract: "full-json" } }) + "\n");
|
|
|
|
const wrapper = {
|
|
ok: true,
|
|
command: "fixture",
|
|
data: {
|
|
outputTruncated: true,
|
|
reason: "stdout-json-bytes-exceeded-threshold",
|
|
dump: { path: dumpPath },
|
|
},
|
|
};
|
|
const resolved = resolveCliChildJsonObject({
|
|
stdout: JSON.stringify(wrapper),
|
|
requestedStdoutType: "fixture JSON",
|
|
});
|
|
|
|
assert.equal(resolved.source, "dump");
|
|
assert.equal(resolved.parsed?.ok, true);
|
|
assert.deepEqual(resolved.parsed?.data, { contract: "full-json" });
|
|
assert.equal(resolved.diagnostics.stdoutKind, "dump-wrapper");
|
|
assert.equal(resolved.diagnostics.dumpPath, dumpPath);
|
|
assert.equal(resolved.diagnostics.dumpReadOk, true);
|
|
assert.equal(resolved.diagnostics.dumpJsonOk, true);
|
|
});
|
|
|
|
test("child JSON recovery command-result helper keeps dump diagnostics", async () => {
|
|
const dir = await mkdtemp(join(tmpdir(), "unidesk-cli-child-json-result-dump-"));
|
|
const dumpPath = join(dir, "dump.json");
|
|
await writeFile(dumpPath, JSON.stringify({ ok: true, verified: true, bytes: 123 }) + "\n");
|
|
const resolved = resolveCliChildJsonCommandResult({
|
|
result: {
|
|
stdout: JSON.stringify({
|
|
ok: true,
|
|
data: {
|
|
outputTruncated: true,
|
|
reason: "stdout-json-bytes-exceeded-threshold",
|
|
dump: { path: dumpPath },
|
|
},
|
|
}),
|
|
stderr: "",
|
|
exitCode: 0,
|
|
timedOut: false,
|
|
},
|
|
requestedStdoutType: "trans download verification JSON",
|
|
acceptParsed: (value) => value.ok === true && value.verified === true,
|
|
});
|
|
|
|
assert.equal(resolved.source, "dump");
|
|
assert.equal(resolved.parsed?.verified, true);
|
|
assert.equal(resolved.diagnostics.requestedStdoutType, "trans download verification JSON");
|
|
assert.equal(resolved.diagnostics.dumpPath, dumpPath);
|
|
});
|
|
|
|
test("child JSON recovery falls back to artifact after trans truncation summary", () => {
|
|
const summary = {
|
|
code: "ssh-truncation-summary",
|
|
exitCode: 0,
|
|
timedOut: false,
|
|
stdout: {
|
|
stream: "stdout",
|
|
thresholdBytes: 10240,
|
|
observedBytesAtTruncation: 165000,
|
|
forwardedBytes: 10240,
|
|
dumpPath: null,
|
|
dumpError: null,
|
|
},
|
|
};
|
|
const resolved = resolveCliChildJsonObject({
|
|
stdout: `UNIDESK_SSH_TRUNCATION_SUMMARY ${JSON.stringify(summary)}\n`,
|
|
requestedStdoutType: "web-probe observe analyze compact JSON",
|
|
artifactFallback: {
|
|
path: "analysis/report.json",
|
|
nextCommand: "bun scripts/cli.ts web-probe observe collect webobs-fixture --view files --file analysis/report.json",
|
|
read: () => ({ ok: true, value: { ok: true, reportJsonPath: "analysis/report.json" }, reason: "analysis-artifact-contract" }),
|
|
},
|
|
});
|
|
|
|
assert.equal(resolved.source, "artifact");
|
|
assert.equal(resolved.parsed?.ok, true);
|
|
assert.equal(resolved.diagnostics.stdoutKind, "ssh-truncation-summary");
|
|
assert.equal(resolved.diagnostics.fallbackReason, "stdout-trans-truncated");
|
|
const artifact = resolved.diagnostics.artifact as Record<string, unknown>;
|
|
assert.equal(artifact.ok, true);
|
|
assert.equal(artifact.nextCommand, "bun scripts/cli.ts web-probe observe collect webobs-fixture --view files --file analysis/report.json");
|
|
});
|
|
|
|
test("child JSON recovery falls back to artifact when stdout is not JSON", () => {
|
|
const resolved = resolveCliChildJsonObject({
|
|
stdout: "remote helper wrote a bounded text summary instead of JSON",
|
|
requestedStdoutType: "web-probe observe analyze compact JSON",
|
|
artifactFallback: {
|
|
path: "analysis/report.json",
|
|
nextCommand: "collect report",
|
|
read: () => ({ ok: true, value: { ok: true, counts: { network: 9 }, reportJsonPath: "analysis/report.json" } }),
|
|
},
|
|
});
|
|
|
|
assert.equal(resolved.source, "artifact");
|
|
assert.equal(resolved.parsed?.ok, true);
|
|
assert.deepEqual(resolved.parsed?.counts, { network: 9 });
|
|
assert.equal(resolved.diagnostics.stdoutKind, "non-json");
|
|
assert.equal(resolved.diagnostics.fallbackReason, "stdout-not-json");
|
|
});
|
|
|
|
test("child JSON recovery rejects ok-only stdout contract and uses artifact", () => {
|
|
const resolved = resolveCliChildJsonObject({
|
|
stdout: JSON.stringify({ ok: true }),
|
|
requestedStdoutType: "web-probe observe analyze compact JSON",
|
|
acceptParsed: (value) => typeof value.reportJsonPath === "string" || typeof value.counts === "object",
|
|
artifactFallback: {
|
|
path: "analysis/report.json",
|
|
nextCommand: "collect report",
|
|
read: () => ({ ok: true, value: { ok: true, reportJsonPath: "analysis/report.json", counts: { network: 9 } } }),
|
|
},
|
|
});
|
|
|
|
assert.equal(resolved.source, "artifact");
|
|
assert.equal(resolved.parsed?.reportJsonPath, "analysis/report.json");
|
|
assert.deepEqual(resolved.parsed?.counts, { network: 9 });
|
|
assert.equal(resolved.diagnostics.stdoutKind, "json");
|
|
assert.equal(resolved.diagnostics.fallbackReason, "stdout-json-contract-invalid");
|
|
assert.equal(resolved.diagnostics.stdoutContractAccepted, false);
|
|
});
|
|
|
|
test("child JSON recovery drops invalid ok-only stdout when artifact fallback is missing", () => {
|
|
const resolved = resolveCliChildJsonObject({
|
|
stdout: JSON.stringify({ ok: true }),
|
|
requestedStdoutType: "web-probe observe analyze compact JSON",
|
|
acceptParsed: (value) => typeof value.reportJsonPath === "string" || typeof value.counts === "object",
|
|
artifactFallback: {
|
|
path: "analysis/report.json",
|
|
nextCommand: "collect report",
|
|
read: () => ({ ok: false, reason: "artifact-missing", path: "analysis/report.json" }),
|
|
},
|
|
});
|
|
|
|
assert.equal(resolved.parsed, null);
|
|
assert.equal(resolved.source, null);
|
|
assert.equal(resolved.diagnostics.stdoutKind, "json");
|
|
assert.equal(resolved.diagnostics.fallbackReason, "stdout-json-contract-invalid");
|
|
assert.equal(resolved.diagnostics.stdoutContractAccepted, false);
|
|
const artifact = resolved.diagnostics.artifact as Record<string, unknown>;
|
|
assert.equal(artifact.ok, false);
|
|
assert.equal(artifact.reason, "artifact-missing");
|
|
assert.equal(artifact.path, "analysis/report.json");
|
|
assert.equal(artifact.nextCommand, "collect report");
|
|
});
|
|
|
|
test("child JSON recovery reports artifact fallback failure when stdout is unusable and artifact is missing", () => {
|
|
const resolved = resolveCliChildJsonObject({
|
|
stdout: "not json",
|
|
requestedStdoutType: "web-probe observe analyze compact JSON",
|
|
artifactFallback: {
|
|
path: "analysis/report.json",
|
|
nextCommand: "collect report",
|
|
read: () => ({ ok: false, reason: "artifact-missing", path: "analysis/report.json" }),
|
|
},
|
|
});
|
|
|
|
assert.equal(resolved.parsed, null);
|
|
assert.equal(resolved.source, null);
|
|
assert.equal(resolved.diagnostics.stdoutKind, "non-json");
|
|
assert.equal(resolved.diagnostics.fallbackReason, "stdout-not-json");
|
|
const artifact = resolved.diagnostics.artifact as Record<string, unknown>;
|
|
assert.equal(artifact.ok, false);
|
|
assert.equal(artifact.reason, "artifact-missing");
|
|
assert.equal(artifact.path, "analysis/report.json");
|
|
assert.equal(artifact.nextCommand, "collect report");
|
|
});
|