Files
reverse-engineering-anything/tests/acceptance/applications/applicationWorkflowCli.test.ts
T

302 lines
9.2 KiB
TypeScript

import { execFile } from "node:child_process";
import { copyFile, mkdir, rm, writeFile } from "node:fs/promises";
import { join } from "node:path";
import { promisify } from "node:util";
import { afterEach, describe, expect, it } from "vitest";
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
import {
JAVASCRIPT_FEATURE_TRACE_FULL_EVIDENCE_EXAMPLE,
JAVASCRIPT_VERSION_COMPARISON_FULL_EVIDENCE_EXAMPLE,
SOURCE_TO_BUNDLE_COMPARISON_EXAMPLE,
} from "../../../src/contracts/javascriptApplicationWorkflowExamples.js";
import { analyzeJavaScriptApplication } from "../../../src/application/JavaScriptApplicationService.js";
import { REPLAY_MACHINE_RUN_EXAMPLE } from "../../../src/contracts/replayMachineExample.js";
import { javascriptApplicationAnalysisResultSchema } from "../../../src/domain/javascriptApplicationAnalysis.js";
const execute = promisify(execFile);
const temporary: string[] = [];
afterEach(async () => {
await Promise.all(
temporary
.splice(0)
.map(async (path) => rm(path, { recursive: true, force: true })),
);
});
describe("application workflow CLI parity", () => {
it("accepts inline trace JSON and file-backed comparison JSON", async () => {
const traced = await runCli([
"trace-application-feature",
JSON.stringify(JAVASCRIPT_FEATURE_TRACE_FULL_EVIDENCE_EXAMPLE),
"--json",
]);
expect(traced).toMatchObject({
operation: "trace_application_feature",
normalized_result: { source_evidence_id: expect.any(String) },
});
const root = await createTestTempDirectory("rea-application-cli-");
temporary.push(root);
const comparisonPath = join(root, "comparison.json");
await writeFile(
comparisonPath,
JSON.stringify(JAVASCRIPT_VERSION_COMPARISON_FULL_EVIDENCE_EXAMPLE),
);
const compared = await runCli([
"compare-application-versions",
comparisonPath,
"--json",
]);
expect(compared).toMatchObject({
operation: "compare_application_versions",
normalized_result: {
summary: { unknown: expect.any(Number) },
},
});
const sourceCompared = await runCli([
"compare-source-to-bundle",
JSON.stringify({
reference: SOURCE_TO_BUNDLE_COMPARISON_EXAMPLE.reference,
application: JAVASCRIPT_FEATURE_TRACE_FULL_EVIDENCE_EXAMPLE.application,
}),
"--json",
]);
expect(sourceCompared).toMatchObject({
operation: "compare_source_to_bundle",
normalized_result: { reference: expect.any(Object) },
});
}, 20_000);
it("traces the same authenticated semantic graph through the CLI", async () => {
const root = await createTestTempDirectory("rea-semantic-cli-");
temporary.push(root);
await writeFile(
join(root, "app.js"),
"function add(value) { return value + 1; } add(2);",
);
const analyzed = await analyzeJavaScriptApplication({
input_path: root,
});
if (!analyzed.ok) throw analyzed.error;
const result = javascriptApplicationAnalysisResultSchema.parse(
analyzed.value.normalized_result,
);
const seed = result.semantic_graph.relations[0]?.source_node_id;
if (seed === undefined)
throw new TypeError("Expected at least one semantic relation");
const traced = await runCli([
"trace-javascript-semantics",
JSON.stringify({
application: analyzed.value,
query: {
seed: { kind: "semantic-node", node_id: seed },
direction: "forward-influence",
include_ambiguous_dynamic_edges: true,
},
}),
"--json",
]);
expect(traced).toMatchObject({
operation: "trace_javascript_semantics",
predicate_type: "rea.javascript-semantic-trace",
normalized_result: {
source_evidence_id: analyzed.value.evidence_id,
source_graph_id: result.semantic_graph.graph_id,
},
});
}, 20_000);
});
describe("application workflow CLI input", () => {
it("rejects Evidence ID-only workflow inputs", async () => {
const result = await runCli([
"compare-application-versions",
JSON.stringify({
left: JAVASCRIPT_VERSION_COMPARISON_FULL_EVIDENCE_EXAMPLE.left
.evidence_id,
right:
JAVASCRIPT_VERSION_COMPARISON_FULL_EVIDENCE_EXAMPLE.right.evidence_id,
}),
"--json",
]);
expect(result).toMatchObject({ code: "invalid_request" });
});
});
describe("application workflow CLI export Evidence", () => {
it("compares exact export shapes from inline Evidence", async () => {
const root = await createTestTempDirectory("rea-export-shape-cli-");
temporary.push(root);
const leftRoot = join(root, "left");
const rightRoot = join(root, "right");
await Promise.all([mkdir(leftRoot), mkdir(rightRoot)]);
await Promise.all([
copyFile(
join(process.cwd(), "tests/fixtures/replay/parser.mjs"),
join(leftRoot, "parser.mjs"),
),
copyFile(
join(process.cwd(), "tests/fixtures/replay/parser-v2.mjs"),
join(rightRoot, "parser.mjs"),
),
]);
const [left, right] = await Promise.all([
analyzeJavaScriptApplication({
input_path: leftRoot,
}),
analyzeJavaScriptApplication({
input_path: rightRoot,
}),
]);
if (!left.ok) throw left.error;
if (!right.ok) throw right.error;
const compared = await runCli([
"compare-javascript-export-shapes",
JSON.stringify({
left: left.value,
right: right.value,
left_module_path: "parser.mjs",
left_export_name: "default",
right_module_path: "parser.mjs",
right_export_name: "default",
}),
"--json",
]);
expect(compared).toMatchObject({
operation: "compare_javascript_export_shapes",
predicate_type: "rea.javascript-export-shape-comparison",
normalized_result: {
summary: { added: 1, removed: 0, changed: 0, unknown: 0 },
changes: [
{
status: "added",
path: "/depth",
right: { availability: "literal", value: 1 },
},
],
},
});
}, 20_000);
});
describe("application workflow CLI validation and replay", () => {
it("returns safe actionable JSON validation details", async () => {
const malformed = await runCli([
"trace-application-feature",
"{not-json",
"--json",
]);
expect(malformed).toMatchObject({
code: "invalid_request",
retryable: true,
details: {
issues: [{ path: [], reason: "invalid_format", expected: "JSON" }],
},
});
expect(JSON.stringify(malformed)).not.toContain("not-json");
const missing = await runCli(["trace-application-feature", "{}", "--json"]);
expect(missing).toMatchObject({
code: "invalid_request",
details: {
issues: expect.arrayContaining([
expect.objectContaining({
path: ["seed"],
reason: "missing_argument",
}),
]),
},
});
}, 20_000);
it("runs a replay machine from inline and file-backed JSON", async () => {
const input = REPLAY_MACHINE_RUN_EXAMPLE;
const inline = await runCli([
"run-replay-machine",
JSON.stringify(input),
"--json",
]);
expect(inline).toMatchObject({
final_state: "complete",
decisions: [{ outcome: "matched" }],
transition_journal: [
{ captured_aliases: [{ name: "token", sensitive: true }] },
],
});
expect(JSON.stringify(inline)).not.toContain("opaque");
const root = await createTestTempDirectory("rea-replay-machine-cli-");
temporary.push(root);
const inputPath = join(root, "run.json");
await writeFile(inputPath, JSON.stringify(input));
const fileBacked = await runCli([
"run-replay-machine",
inputPath,
"--json",
]);
expect(fileBacked).toEqual(inline);
const invalid = await runCli([
"run-replay-machine",
JSON.stringify({
machine: input.machine,
events: [
{
protocol: "http",
connection: "not_applicable",
at_ms: 0,
method: null,
path: "/callback",
headers: {},
body: "private-cli-body",
},
],
}),
"--json",
]);
expect(invalid).toMatchObject({
code: "invalid_request",
details: {
issues: expect.arrayContaining([
expect.objectContaining({
path: ["events", 0, "method"],
}),
]),
},
});
expect(JSON.stringify(invalid)).not.toContain("private-cli-body");
}, 20_000);
});
const runCli = async (
arguments_: readonly string[],
environment: Readonly<Record<string, string>> = {},
): Promise<unknown> => {
try {
const { stdout } = await execute(
process.execPath,
["scripts/rea.mjs", ...arguments_],
{
cwd: process.cwd(),
env: { ...process.env, ...environment },
maxBuffer: 16 * 1_024 * 1_024,
},
);
return JSON.parse(stdout);
} catch (cause: unknown) {
if (
typeof cause === "object" &&
cause !== null &&
"stdout" in cause &&
typeof cause.stdout === "string"
)
return JSON.parse(cause.stdout);
throw cause;
}
};