rea/tests/boundary/cli/jsonInputStatus.test.ts

160 lines
5.8 KiB
TypeScript

import { writeFile } from "node:fs/promises";
import { join } from "node:path";
import { describe, expect } from "vitest";
import { parseCliJsonInput } from "../../../src/cliJsonInput.js";
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
import { cliTest } from "../../support/cli/cliFixture.js";
const JSON_COMMANDS = [
["trace-application-feature", "trace-application-feature"],
["trace-javascript-semantics", "trace-javascript-semantics"],
["compare-application-versions", "compare-application-versions"],
["compare-source-to-bundle", "compare-source-to-bundle"],
["compare-javascript-export-shapes", "compare-javascript-export-shapes"],
[
"build-reconstruction-obligation-ledger",
"build-reconstruction-obligation-ledger",
],
["evaluate-reconstruction-coverage", "evaluate-reconstruction-coverage"],
["import-managed-reconstruction", "import-managed-reconstruction"],
["verify-managed-native-boundaries", "verify-managed-native-boundaries"],
["project-managed-application-graph", "project-managed-application-graph"],
["capture-browser-scenario", "capture_browser_scenario"],
["capture-electron-scenario", "capture_electron_scenario"],
["reconcile-javascript-runtime", "reconcile_javascript_runtime"],
] as const;
describe("compiled CLI JSON input failure status", () => {
for (const [command, operation] of JSON_COMMANDS)
for (const kind of ["inline", "file", "missing"] as const)
cliTest(
`${command} rejects malformed or unreadable ${kind} input before dispatch`,
async ({ cli }) => {
const root = await createTestTempDirectory("rea-cli-json-status-");
const input = kind === "inline" ? "{" : join(root, "input.json");
if (kind === "file") await writeFile(input, "{");
const parsed = await parseCliJsonInput(input, operation);
if (parsed.ok)
throw new Error("Expected JSON input to fail before dispatch");
const result = await cli.run({
arguments: [command, input, "--json"],
environment: {
HOME: root,
XDG_CONFIG_HOME: root,
XDG_CACHE_HOME: root,
},
});
expect(result.stdout).toBe(
`${JSON.stringify(parsed.error, null, 2)}\n`,
);
expect(result.json).toMatchObject({
code: "invalid_request",
category: "invalid_input",
...(kind === "missing" ? {} : { details: { operation } }),
...(kind === "inline"
? {}
: {
input_path: input,
input_reason:
kind === "file" ? "invalid-json" : "read-failed",
}),
});
expect(result.stderr).toBe("");
expect(result.exitCode).toBe(1);
},
);
for (const [format, flags] of [
["JSON", ["--json"]],
["JSONL", ["--format", "jsonl"]],
["YAML", ["--format", "yaml"]],
["default", []],
] as const)
cliTest(
`keeps the failure status with ${format} output`,
async ({ cli }) => {
const root = await createTestTempDirectory("rea-cli-json-format-");
const input = join(root, "missing.json");
const result = await cli.run({
arguments: ["compare-javascript-export-shapes", input, ...flags],
environment: {
HOME: root,
XDG_CONFIG_HOME: root,
XDG_CACHE_HOME: root,
},
});
for (const diagnostic of [
"invalid_request",
"invalid_input",
input,
"read-failed",
])
expect(result.stdout).toContain(diagnostic);
expect(result.stderr).toBe("");
expect(result.exitCode).toBe(1);
},
);
cliTest(
"rejects an empty file path without losing its diagnostic",
async ({ cli }) => {
const root = await createTestTempDirectory("rea-cli-json-empty-");
const result = await cli.run({
arguments: ["compare-javascript-export-shapes", "", "--json"],
environment: {
HOME: root,
XDG_CONFIG_HOME: root,
XDG_CACHE_HOME: root,
},
});
expect(result.json).toMatchObject({
code: "invalid_request",
input_path: "",
input_reason: "read-failed",
});
expect(result.exitCode).toBe(1);
},
);
});
describe("compiled JSON parser and logger success seam", () => {
for (const kind of ["inline", "file"] as const)
cliTest(
`preserves valid ${kind} data and a zero exit status without running a workflow`,
async ({ processes }) => {
const root = await createTestTempDirectory("rea-cli-json-success-");
const value = {
error: "source data",
code: "invalid_request",
values: [null, false, 0],
};
const json = JSON.stringify(value);
const input = kind === "inline" ? json : join(root, "input.json");
if (kind === "file") await writeFile(input, json);
const result = await processes.run(
process.execPath,
[
"--input-type=module",
"-e",
`import { parseCliJsonInput } from "./dist/cliJsonInput.js";
import { logCliCommand } from "./dist/cliLogging.js";
import { silentLogger } from "./dist/logger.js";
const parsed = await parseCliJsonInput(process.argv[1], "test-input");
const output = await logCliCommand(silentLogger, "test-input", async () =>
parsed.ok ? parsed.value : parsed.error);
console.log(JSON.stringify(output));`,
input,
],
{ env: { HOME: root, XDG_CONFIG_HOME: root, XDG_CACHE_HOME: root } },
);
expect(result.stdout).toBe(`${json}\n`);
expect(result.stderr).toBe("");
expect(result.exitCode).toBe(0);
},
);
});