diff --git a/docs/development/testing.md b/docs/development/testing.md index e53403fcfe..9ed2876ac9 100644 --- a/docs/development/testing.md +++ b/docs/development/testing.md @@ -360,11 +360,17 @@ The process restarts when the environment changes, after 32 commands, or when the worker reports a resident set of at least 1 GiB. A fatal worker failure or a broken session frame retires that process and waits for it to exit before the next command starts. -`cli.run` still starts a fresh process, including stdin and explicit deadlines. -`runFileCli` uses the file session for commands that have neither. Go binary, -IL2CPP metadata, and dylib-resolution commands use that session. File teardown -stops the shared CLI process, including startup still in flight, and does not -open another after close. +In a `cliTest` file, `cli.run` uses that shared process when it reports the +command exactly as a fresh `rea` process would. Both run the same one-shot +owner (`src/cliOneShot.ts`), and the shared process presents the launcher's +entrypoint. A command starts a fresh process instead when it supplies stdin or +an explicit deadline, enters MCP or the MCP doctor (which the launcher routes +first), sets `NODE_OPTIONS`, or enables logging, which writes outside the +per-command capture. Pass `freshProcess: true` for process-level facts such as +startup, signals, or exit, and keep one fresh check beside reused cases where +dispatch itself is under test. `createTestCli` drivers always start a fresh +process. File teardown stops the shared CLI process, including startup still in +flight, and does not open another after close. `exportedFunctionBindingsCli.test.ts` uses the shared compiled CLI session for its file and compares its JSON command results with the MCP session. It selects 20-second startup and command deadlines; other callers retain their existing diff --git a/src/cli/environmentCommands.ts b/src/cli/environmentCommands.ts index 6a21af1a38..080b08d418 100644 --- a/src/cli/environmentCommands.ts +++ b/src/cli/environmentCommands.ts @@ -11,7 +11,10 @@ import type { ServedState } from "../application/environment/environmentState.js import { z } from "incur"; import type { Logger } from "pino"; -import { CLI_COMMANDS } from "../cliCommandNames.js"; +import { + CLI_COMMANDS, + DEFAULT_INFO_LOGGING_COMMANDS, +} from "../cliCommandNames.js"; import { parseCliJsonInput } from "../cliJsonInput.js"; import { logCliCommand } from "../cliLogging.js"; import { parseEnvironmentName } from "../config/environmentName.js"; @@ -48,10 +51,12 @@ const cliError = (error: AnalysisError): JsonValue => ({ /** The listener announces its URL, and a build its stages, at info like `rea mcp --http`; an explicit REA_LOG_LEVEL still wins. */ const infoLogger = ( + command: string, environment: Readonly, logger: Logger, ): Logger => - environment.REA_LOG_LEVEL === undefined + environment.REA_LOG_LEVEL === undefined && + DEFAULT_INFO_LOGGING_COMMANDS.has(command) ? createLogger("cli", "info") : logger; @@ -179,7 +184,11 @@ const serveEnvironmentCommand = async ( source, served: { state, report }, } = inputs.value; - const commandLogger = infoLogger(environment, logger); + const commandLogger = infoLogger( + CLI_COMMANDS.environmentServe, + environment, + logger, + ); if (!report.served && report.reason === "unavailable") commandLogger.warn( { code: report.code, error: report.message }, @@ -411,7 +420,11 @@ const buildEnvironmentCommand = async ( ], ), ); - const commandLogger = infoLogger(environment, logger); + const commandLogger = infoLogger( + CLI_COMMANDS.environmentBuild, + environment, + logger, + ); const progress = (stage: string): void => { commandLogger.info({ stage }, "Building environment"); }; diff --git a/src/cliCommandNames.ts b/src/cliCommandNames.ts index 892978fa7b..34602b4962 100644 --- a/src/cliCommandNames.ts +++ b/src/cliCommandNames.ts @@ -201,3 +201,12 @@ export const CLI_COMMANDS = Object.freeze({ /** Ordered primary CLI inventory; aliases are intentionally excluded. */ export const CLI_COMMAND_NAMES = Object.freeze(Object.values(CLI_COMMANDS)); + +/** + * Commands that announce their progress at info level when REA_LOG_LEVEL is + * unset, like `rea mcp --http`. Their logger writes to stderr directly. + */ +export const DEFAULT_INFO_LOGGING_COMMANDS: ReadonlySet = new Set([ + CLI_COMMANDS.environmentServe, + CLI_COMMANDS.environmentBuild, +]); diff --git a/tests/acceptance/apkZip64FramingCli.test.ts b/tests/acceptance/apkZip64FramingCli.test.ts index d098d8c806..9c8faecb4c 100644 --- a/tests/acceptance/apkZip64FramingCli.test.ts +++ b/tests/acceptance/apkZip64FramingCli.test.ts @@ -12,7 +12,7 @@ import { createTestTempDirectory } from "../fixtures/temporaryDirectory.js"; import { cliTest, type TestCli } from "../support/cli/cliFixture.js"; const inspectBoth = async (cli: TestCli, path: string) => { - const output = await cli.sharedCliRun!({ + const output = await cli.run({ arguments: [ "inspect-apk-signature", path, diff --git a/tests/acceptance/applications/adbListingNumbers.test.ts b/tests/acceptance/applications/adbListingNumbers.test.ts index 0fcaeab621..971dd7a34f 100644 --- a/tests/acceptance/applications/adbListingNumbers.test.ts +++ b/tests/acceptance/applications/adbListingNumbers.test.ts @@ -6,7 +6,7 @@ import { expect } from "vitest"; import { toolContract } from "../../../src/contracts/toolContracts.js"; import { connectLocalToolsMcp } from "../../fixtures/localToolsMcp.js"; import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js"; -import { cliTest, runFileCli } from "../../support/cli/cliFixture.js"; +import { cliTest } from "../../support/cli/cliFixture.js"; const SERIAL = "listing-numbers-device"; const UNSAFE = "9007199254740993"; @@ -224,9 +224,10 @@ process.stdout.write(require('node:fs').readFileSync(path)); arguments: [operation.replaceAll("_", "-"), SERIAL, "--json"], environment, }; - const output = await (variant === "safe" - ? cli.run(invocation) - : runFileCli(cli, invocation)); + const output = await cli.run({ + ...invocation, + freshProcess: variant === "safe", + }); expect(output.exitCode, output.stderr).toBe(0); const evidence = contract.outputSchema.parse(output.json); expect(evidence.parameters).toMatchObject({ serial: SERIAL }); diff --git a/tests/acceptance/applications/adbNumericDeclarations.test.ts b/tests/acceptance/applications/adbNumericDeclarations.test.ts index 4c1af433ec..10c2e376ac 100644 --- a/tests/acceptance/applications/adbNumericDeclarations.test.ts +++ b/tests/acceptance/applications/adbNumericDeclarations.test.ts @@ -9,7 +9,7 @@ import { adbDisplayPartialObservationSchema } from "../../../src/domain/adb/adbD import { connectLocalToolsMcp } from "../../fixtures/localToolsMcp.js"; import { parseMcpToolError } from "../../fixtures/mcpToolError.js"; import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js"; -import { cliTest, runFileCli } from "../../support/cli/cliFixture.js"; +import { cliTest } from "../../support/cli/cliFixture.js"; const SERIAL = "numeric-declarations-device"; const PACKAGE = "com.example.numeric"; @@ -224,9 +224,10 @@ cliTest.skipIf(process.platform === "win32")( arguments: ["inspect-adb-display", SERIAL, "--json"], environment: fixture.environment, }; - const output = await (scenario === displayScenarios[0] - ? cli.run(invocation) - : runFileCli(cli, invocation)); + const output = await cli.run({ + ...invocation, + freshProcess: scenario === displayScenarios[0], + }); expect(output.exitCode, `${scenario.name}: ${output.stderr}`).toBe(0); const evidence = contract.outputSchema.parse(output.json); expect(evidence.parameters).toEqual({ serial: SERIAL }); @@ -281,9 +282,10 @@ cliTest.skipIf(process.platform === "win32")( arguments: ["inspect-adb-display", SERIAL, "--json"], environment: fixture.environment, }; - const output = await (value === null - ? cli.run(invocation) - : runFileCli(cli, invocation)); + const output = await cli.run({ + ...invocation, + freshProcess: value === null, + }); expect(output.exitCode, output.stderr).toBe(1); const projected = analysisErrorProjectionSchema.parse(output.json); expect(projected.code).toBe("unreadable_output"); @@ -454,9 +456,10 @@ cliTest.skipIf(process.platform === "win32")( arguments: ["inspect-adb-package", SERIAL, PACKAGE, "--json"], environment: fixture.environment, }; - const output = await (scenario === packageScenarios[0] - ? cli.run(invocation) - : runFileCli(cli, invocation)); + const output = await cli.run({ + ...invocation, + freshProcess: scenario === packageScenarios[0], + }); expect(output.exitCode, `${scenario.name}: ${output.stderr}`).toBe(0); const evidence = contract.outputSchema.parse(output.json); expect(evidence.parameters).toEqual({ serial: SERIAL, package: PACKAGE }); diff --git a/tests/acceptance/applications/adbPackageInventoryCoverage.test.ts b/tests/acceptance/applications/adbPackageInventoryCoverage.test.ts index 78179e780f..8ce5535b63 100644 --- a/tests/acceptance/applications/adbPackageInventoryCoverage.test.ts +++ b/tests/acceptance/applications/adbPackageInventoryCoverage.test.ts @@ -8,7 +8,7 @@ import { toolContract } from "../../../src/contracts/toolContracts.js"; import { parseEvidence } from "../../../src/domain/evidence.js"; import { connectLocalToolsMcp } from "../../fixtures/localToolsMcp.js"; import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js"; -import { cliTest, runFileCli } from "../../support/cli/cliFixture.js"; +import { cliTest } from "../../support/cli/cliFixture.js"; const PACKAGE_LINE = "package:/data/app/example/base.apk=com.example.app"; @@ -139,9 +139,10 @@ process.exit(91); arguments: [command, "emulator-5554", ...tail, "--json"], environment, }; - const output = await (count === 0 - ? cli.run(invocation) - : runFileCli(cli, invocation)); + const output = await cli.run({ + ...invocation, + freshProcess: count === 0, + }); expect(output.exitCode, output.stderr).toBe(0); const evidence = parseEvidence(output.json); expect( diff --git a/tests/acceptance/applications/apiContract.test.ts b/tests/acceptance/applications/apiContract.test.ts index 3f67f1c393..0302a05e2d 100644 --- a/tests/acceptance/applications/apiContract.test.ts +++ b/tests/acceptance/applications/apiContract.test.ts @@ -131,7 +131,7 @@ const checkCliPublication = async ( }, ) => { const { root, path, parent, summary, selected } = context; - const run = cli.sharedCliRun?.bind(cli) ?? cli.run.bind(cli); + const run = cli.run.bind(cli); const complete = await run({ arguments: ["inspect-api-contract", path, "--json"], }); @@ -395,7 +395,7 @@ cliTest( coverage: { total: 3 }, source_coverage: { status: "partial" }, }); - const run = cli.sharedCliRun?.bind(cli) ?? cli.run.bind(cli); + const run = cli.run.bind(cli); const cliResult = await run({ arguments: ["inspect-api-contract", path, "--json"], }); @@ -442,7 +442,7 @@ cliTest( details: { partial_observation: snapshot }, }); expect(JSON.stringify(response)).not.toContain("u:p@h"); - const run = cli.sharedCliRun?.bind(cli) ?? cli.run.bind(cli); + const run = cli.run.bind(cli); const result = await run({ arguments: ["inspect-api-contract", path, "--json"], }); diff --git a/tests/acceptance/applications/applicationVersionMatchingCli.test.ts b/tests/acceptance/applications/applicationVersionMatchingCli.test.ts index 787eb4ff03..5197b8d00d 100644 --- a/tests/acceptance/applications/applicationVersionMatchingCli.test.ts +++ b/tests/acceptance/applications/applicationVersionMatchingCli.test.ts @@ -7,7 +7,7 @@ import { parseEvidence } from "../../../src/domain/evidence.js"; import { javascriptApplicationAnalysisResultSchema } from "../../../src/domain/javascript/javascriptApplicationAnalysis.js"; import { applicationVersionComparisonResultSchema } from "../../../src/domain/javascript/javascriptApplicationVersionComparisonSchemas.js"; import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js"; -import { cliTest, runFileCli } from "../../support/cli/cliFixture.js"; +import { cliTest } from "../../support/cli/cliFixture.js"; cliTest( "keeps unavailable source-selection scopes unknown in version comparisons", @@ -72,7 +72,7 @@ cliTest( ) => { const inputPath = join(root, `${name}.json`); await writeFile(inputPath, JSON.stringify({ left, right })); - const response = await runFileCli(cli, { + const response = await cli.run({ arguments: ["compare-application-versions", inputPath, "--json"], environment: { REA_LOG_LEVEL: "silent" }, }); @@ -492,7 +492,7 @@ cliTest( const compare = async (left: unknown, right: unknown) => { const inputPath = join(root, `comparison-${Math.random()}.json`); await writeFile(inputPath, JSON.stringify({ left, right })); - const response = await runFileCli(cli, { + const response = await cli.run({ arguments: ["compare-application-versions", inputPath, "--json"], environment: { REA_LOG_LEVEL: "silent" }, }); diff --git a/tests/acceptance/applications/erlangBeamViews.test.ts b/tests/acceptance/applications/erlangBeamViews.test.ts index be76c49e6f..02bb9ea33a 100644 --- a/tests/acceptance/applications/erlangBeamViews.test.ts +++ b/tests/acceptance/applications/erlangBeamViews.test.ts @@ -107,7 +107,7 @@ cliTest( const root = await createTestTempDirectory("rea-beam-views-"); const path = join(root, "app.beam"); await writeFile(path, erlangBeamFixture("views")); - const run = cli.sharedCliRun?.bind(cli) ?? cli.run.bind(cli); + const run = cli.run.bind(cli); const complete = await run({ arguments: ["inspect-erlang-beam", path, "--json"], }); @@ -312,7 +312,7 @@ cliTest( async ({ cli }) => { const root = await createTestTempDirectory("rea-beam-invalid-view-"); const output = join(root, "unused.json"); - const run = cli.sharedCliRun?.bind(cli) ?? cli.run.bind(cli); + const run = cli.run.bind(cli); const view = JSON.stringify({ kind: "beam", collection: "exports" }); for (const [flags, key] of [ [["--detail", "summary"], "evidence_output"], diff --git a/tests/acceptance/applications/firmwareComparison.test.ts b/tests/acceptance/applications/firmwareComparison.test.ts index 6eaab8ea01..e3dc9fb2fc 100644 --- a/tests/acceptance/applications/firmwareComparison.test.ts +++ b/tests/acceptance/applications/firmwareComparison.test.ts @@ -137,7 +137,7 @@ cliTest( }, }), ); - const run = cli.sharedCliRun?.bind(cli) ?? cli.run.bind(cli); + const run = cli.run.bind(cli); const result = await run({ arguments: ["compare-artifacts", requestPath, "--json"], }); diff --git a/tests/acceptance/applications/graphqlContract.test.ts b/tests/acceptance/applications/graphqlContract.test.ts index c3c26063d5..cc21b4c963 100644 --- a/tests/acceptance/applications/graphqlContract.test.ts +++ b/tests/acceptance/applications/graphqlContract.test.ts @@ -35,7 +35,7 @@ const checkCliPublication = async ( }, ) => { const { root, parent, selected } = context; - const run = cli.sharedCliRun?.bind(cli) ?? cli.run.bind(cli); + const run = cli.run.bind(cli); const args = [ "inspect-api-contract", "./SDL 雪.graphql", @@ -271,7 +271,7 @@ cliTest( }, }), ); - const run = cli.sharedCliRun?.bind(cli) ?? cli.run.bind(cli); + const run = cli.run.bind(cli); const offline = await run({ arguments: ["inspect-analysis-view", queryPath, "--json"], }); @@ -339,7 +339,7 @@ cliTest( source_coverage: { status: "partial", operations: "unavailable" }, }, }); - const run = cli.sharedCliRun?.bind(cli) ?? cli.run.bind(cli); + const run = cli.run.bind(cli); const cliResult = await run({ arguments: [ "inspect-api-contract", @@ -392,7 +392,7 @@ cliTest( items: [{ code: "schema-parse-refused", source: { role: "schema" } }], }); await writeFile(path, text); - const run = cli.sharedCliRun?.bind(cli) ?? cli.run.bind(cli); + const run = cli.run.bind(cli); const result = await run({ arguments: [ "inspect-api-contract", diff --git a/tests/acceptance/applications/interfaceBuilderPlistNumbers.test.ts b/tests/acceptance/applications/interfaceBuilderPlistNumbers.test.ts index 942c1b43ff..03b5fd8acc 100644 --- a/tests/acceptance/applications/interfaceBuilderPlistNumbers.test.ts +++ b/tests/acceptance/applications/interfaceBuilderPlistNumbers.test.ts @@ -9,11 +9,7 @@ import { interfaceBuilderAnalysisSchema } from "../../../src/domain/apple/interf import { thinMach } from "../../../src/domain/binaryTarget.fixture.js"; import { parseEvidence } from "../../../src/domain/evidence.js"; import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js"; -import { - cliTest, - runFileCli, - type TestCli, -} from "../../support/cli/cliFixture.js"; +import { cliTest, type TestCli } from "../../support/cli/cliFixture.js"; const unknownMixedInteger = { $plist_type: "unknown", @@ -39,7 +35,7 @@ const inspectArchive = (cli: TestCli, app: string, fresh: boolean) => { arguments: ["decode-interface-builder", app, "--json"], environment: { REA_LOG_LEVEL: "silent", REA_ANALYSIS_PROVIDER: "auto" }, }; - return fresh ? cli.run(invocation) : runFileCli(cli, invocation); + return cli.run({ ...invocation, freshProcess: fresh }); }; cliTest.for(scenarios)( diff --git a/tests/acceptance/applications/javascriptExportBindingModuleView.test.ts b/tests/acceptance/applications/javascriptExportBindingModuleView.test.ts index e335d525c5..f496b35277 100644 --- a/tests/acceptance/applications/javascriptExportBindingModuleView.test.ts +++ b/tests/acceptance/applications/javascriptExportBindingModuleView.test.ts @@ -6,7 +6,7 @@ import { expect } from "vitest"; import { analysisViewResultSchema } from "../../../src/domain/analysisView/analysisView.js"; import { parseEvidence } from "../../../src/domain/evidence.js"; import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js"; -import { cliTest, runFileCli } from "../../support/cli/cliFixture.js"; +import { cliTest } from "../../support/cli/cliFixture.js"; cliTest( "pages only module units for a CommonJS file that exports symbols", @@ -23,7 +23,7 @@ cliTest( "exports.beta = function beta() { return 2; };", ].join("\n"), ); - const analyzed = await runFileCli(cli, { + const analyzed = await cli.run({ arguments: ["analyze-javascript-application", root, "--format", "json"], }); expect(analyzed.exitCode, analyzed.stdout + analyzed.stderr).toBe(0); @@ -54,7 +54,7 @@ cliTest( traceInput, JSON.stringify({ application: evidence, seed }), ); - const traced = await runFileCli(cli, { + const traced = await cli.run({ arguments: ["trace-application-feature", traceInput, "--json"], }); expect(traced.exitCode, traced.stdout + traced.stderr).toBe(0); @@ -86,7 +86,7 @@ cliTest( view, }), ); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-analysis-view", queryPath, "--json"], }); expect(result.exitCode, result.stdout + result.stderr).toBe(0); diff --git a/tests/acceptance/applications/javascriptFeatureTraceSeedSelectionCli.test.ts b/tests/acceptance/applications/javascriptFeatureTraceSeedSelectionCli.test.ts index b69d75dcca..dd46c698f7 100644 --- a/tests/acceptance/applications/javascriptFeatureTraceSeedSelectionCli.test.ts +++ b/tests/acceptance/applications/javascriptFeatureTraceSeedSelectionCli.test.ts @@ -13,7 +13,6 @@ import { findApplicationFeatureSeeds } from "../../../src/domain/javascript/java import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js"; import { cliTest, - runFileCli, type SharedMcpCall, type TestCli, } from "../../support/cli/cliFixture.js"; @@ -170,7 +169,7 @@ const traceThroughCli = async ( const invocation = { arguments: ["trace-application-feature", input, "--json"], }; - const response = await runFileCli(cli, invocation); + const response = await cli.run(invocation); expect(response.exitCode, `${scenario.name} via CLI`).toBe(0); const evidence = toolContract("trace_application_feature").outputSchema.parse( response.json, diff --git a/tests/acceptance/applications/keyedArchiveTextUnicode.test.ts b/tests/acceptance/applications/keyedArchiveTextUnicode.test.ts index 27873a7e8d..40c0341594 100644 --- a/tests/acceptance/applications/keyedArchiveTextUnicode.test.ts +++ b/tests/acceptance/applications/keyedArchiveTextUnicode.test.ts @@ -8,7 +8,7 @@ import { afterAll, expect } from "vitest"; import { parse as parseYaml } from "yaml"; import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js"; -import { cliTest, runFileCli } from "../../support/cli/cliFixture.js"; +import { cliTest } from "../../support/cli/cliFixture.js"; afterAll(() => disposeMdItInstance()); @@ -45,9 +45,10 @@ cliTest.for([ const invocation = { arguments: ["inspect-keyed-archive", path, ...args], }; - return label === "high" || label === "pair" - ? cli.run(invocation) - : runFileCli(cli, invocation); + return cli.run({ + ...invocation, + freshProcess: label === "high" || label === "pair", + }); }; for (const format of ["json", "yaml"] as const) { const output = await run(["--format", format]); @@ -120,9 +121,7 @@ cliTest( const invocation = { arguments: ["inspect-keyed-archive", path, ...args], }; - return label === "nul" - ? cli.run(invocation) - : runFileCli(cli, invocation); + return cli.run({ ...invocation, freshProcess: label === "nul" }); }; const json = await run(["--json"]); expect(json.exitCode, json.stderr).toBe(0); diff --git a/tests/acceptance/applications/keyedArchiveXmlNumbers.test.ts b/tests/acceptance/applications/keyedArchiveXmlNumbers.test.ts index 0c10ff8019..252169987d 100644 --- a/tests/acceptance/applications/keyedArchiveXmlNumbers.test.ts +++ b/tests/acceptance/applications/keyedArchiveXmlNumbers.test.ts @@ -8,7 +8,7 @@ import { expect } from "vitest"; import { keyedArchiveResultSchema } from "../../../src/domain/apple/keyedArchive.js"; import { parseEvidence } from "../../../src/domain/evidence.js"; import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js"; -import { cliTest, runFileCli } from "../../support/cli/cliFixture.js"; +import { cliTest } from "../../support/cli/cliFixture.js"; cliTest.for([ { name: "plain real", element: "1.25", expected: 1.25 }, @@ -74,7 +74,7 @@ cliTest.for([ await writeFile(path, bytes); const call = await cli.sharedMcpCall?.(); if (call === undefined) throw new Error("Missing file-scoped MCP session"); - const output = await runFileCli(cli, { + const output = await cli.run({ arguments: [ "inspect-keyed-archive", path, @@ -155,7 +155,7 @@ cliTest( }).replace("__VALUE__", element), ); await writeFile(path, bytes); - const output = await runFileCli(cli, { + const output = await cli.run({ arguments: ["inspect-keyed-archive", path, "--json"], environment: { REA_LOG_LEVEL: "silent" }, }); diff --git a/tests/acceptance/applications/mobileDirectoryComponents.test.ts b/tests/acceptance/applications/mobileDirectoryComponents.test.ts index 7e5917251c..d7e60bb985 100644 --- a/tests/acceptance/applications/mobileDirectoryComponents.test.ts +++ b/tests/acceptance/applications/mobileDirectoryComponents.test.ts @@ -19,7 +19,7 @@ import { parseEvidence } from "../../../src/domain/evidence.js"; import { harmonyApplicationProjectionResultSchema } from "../../../src/domain/harmony/harmonyApplication.js"; import { connectLocalToolsMcp } from "../../fixtures/localToolsMcp.js"; import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js"; -import { cliTest, runFileCli } from "../../support/cli/cliFixture.js"; +import { cliTest, type CliInvocation } from "../../support/cli/cliFixture.js"; const harmony = { family: "HarmonyOS", @@ -146,10 +146,8 @@ cliTest.for(fixtures)( const { call } = await connectLocalToolsMcp(environment); for (const mode of ["directories", "files", "nested"] as const) { // Keep one fresh dispatcher check per family; reuse later commands. - const run = - mode === "directories" - ? cli.run.bind(cli) - : runFileCli.bind(undefined, cli); + const run = (invocation: CliInvocation) => + cli.run({ ...invocation, freshProcess: mode === "directories" }); const writer = new ZipWriter(new Uint8ArrayWriter()); for (const path of fixture.paths) { if (mode !== "files") @@ -324,9 +322,8 @@ cliTest.for([ const environment = { HOME: root, USERPROFILE: root }; const { call } = await connectLocalToolsMcp(environment); for (const encrypted of [false, true]) { - const run = encrypted - ? runFileCli.bind(undefined, cli) - : cli.run.bind(cli); + const run = (invocation: CliInvocation) => + cli.run({ ...invocation, freshProcess: !encrypted }); const writer = new ZipWriter(new Uint8ArrayWriter()); for (const member of fixture.paths) await writer.add( diff --git a/tests/acceptance/applications/nativeExportsViews.test.ts b/tests/acceptance/applications/nativeExportsViews.test.ts index 8adf5b7eaf..d249d36a9c 100644 --- a/tests/acceptance/applications/nativeExportsViews.test.ts +++ b/tests/acceptance/applications/nativeExportsViews.test.ts @@ -40,7 +40,7 @@ cliTest.for(["pe", "elf"] as const)( versionDefinitions: ["fixture", "V1"], }); await writeFile(path, bytes); - const run = cli.sharedCliRun?.bind(cli) ?? cli.run.bind(cli); + const run = cli.run.bind(cli); const complete = await run({ arguments: ["inspect-native-exports", path, "--json"], }); diff --git a/tests/acceptance/applications/peResources.test.ts b/tests/acceptance/applications/peResources.test.ts index 1be23fd4a8..29560a42c8 100644 --- a/tests/acceptance/applications/peResources.test.ts +++ b/tests/acceptance/applications/peResources.test.ts @@ -9,11 +9,7 @@ import { peResourceFixture } from "../../../src/native/pe/PeResources.fixture.js import { connectLocalToolsMcp } from "../../fixtures/localToolsMcp.js"; import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js"; import { parseMcpToolError } from "../../fixtures/mcpToolError.js"; -import { - cliTest, - runFileCli, - type TestCli, -} from "../../support/cli/cliFixture.js"; +import { cliTest, type TestCli } from "../../support/cli/cliFixture.js"; type PeResourceCall = ( arguments_: Record, @@ -26,7 +22,7 @@ const expectPeInputFailureAcrossAdapters = async (input: { readonly reason: "invalid_format" | "invalid_value"; }) => { const { cli, call, path, reason } = input; - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-pe-resources", path, "--json"], }); expect(result.exitCode).toBe(1); @@ -117,7 +113,7 @@ const expectPeWordAlignedRecordsAcrossAdapters = async (input: { ); const path = join(root, `word-aligned-${plus}-${index}.exe`); await writeFile(path, fixture.bytes); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-pe-resources", path, "--json"], }); expect(result.exitCode, result.stderr).toBe(0); @@ -208,7 +204,7 @@ const expectPeMappingOwnershipAcrossAdapters = async ( }); continue; } - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-pe-resources", path, "--json"], }); expect(result.exitCode, name).toBe(0); @@ -266,9 +262,7 @@ const expectPeInventoriesAcrossAdapters = async ( }; // Keep an independent public-wrapper process for both PE32 and PE32+. // The empty-payload variants reuse only compiled-command startup. - const result = await (empty - ? runFileCli(cli, invocation) - : cli.run(invocation)); + const result = await (empty ? cli.run(invocation) : cli.run(invocation)); expect(result.exitCode).toBe(0); const evidence = contract.outputSchema.parse(result.json); const report = peResourcesSchema.parse(evidence.normalized_result); @@ -348,7 +342,7 @@ const expectPeEmptyBranchesAcrossAdapters = async ( shared ? "shared-empty64.exe" : "empty-branches32.exe", ); await writeFile(path, fixture.bytes); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-pe-resources", path, "--json"], }); expect(result.exitCode, result.stderr).toBe(0); diff --git a/tests/acceptance/applications/playwrightTrace.test.ts b/tests/acceptance/applications/playwrightTrace.test.ts index 2f1c64fd5f..10a5b21e82 100644 --- a/tests/acceptance/applications/playwrightTrace.test.ts +++ b/tests/acceptance/applications/playwrightTrace.test.ts @@ -73,7 +73,7 @@ cliTest( (await call("inspect_playwright_trace", { path: fixture.path, view })) .structuredContent, ).toEqual(selected); - const run = cli.sharedCliRun?.bind(cli) ?? cli.run.bind(cli); + const run = cli.run.bind(cli); const result = await run({ arguments: ["inspect-playwright-trace", fixture.path, "--json"], }); @@ -102,7 +102,7 @@ const checkCliPublication = async ({ call: McpCall; }) => { const source = { kind: "retained-evidence", evidence_id: parent.evidence_id }; - const run = cli.sharedCliRun?.bind(cli) ?? cli.run.bind(cli); + const run = cli.run.bind(cli); const fullCli = await run({ arguments: ["inspect-playwright-trace", fixture.path, "--json"], }); @@ -420,7 +420,7 @@ cliTest( expect( parseEvidence(full.structuredContent).normalized_result, ).toMatchObject({ items: [{ text: message }] }); - const run = cli.sharedCliRun?.bind(cli) ?? cli.run.bind(cli); + const run = cli.run.bind(cli); const result = await run({ arguments: ["inspect-playwright-trace", fixture.path, "--json"], }); diff --git a/tests/acceptance/applications/pythonPycViews.test.ts b/tests/acceptance/applications/pythonPycViews.test.ts index a816ebdb24..486420d166 100644 --- a/tests/acceptance/applications/pythonPycViews.test.ts +++ b/tests/acceptance/applications/pythonPycViews.test.ts @@ -74,7 +74,7 @@ cliTest( const root = await createTestTempDirectory("rea-pyc-views-"); const path = join(root, "module.pyc"); await writeSameNamePyc(path); - const run = cli.sharedCliRun?.bind(cli) ?? cli.run.bind(cli); + const run = cli.run.bind(cli); const complete = await run({ arguments: ["inspect-python-pyc", path, "--json"], }); @@ -222,7 +222,7 @@ cliTest( async ({ cli }) => { const root = await createTestTempDirectory("rea-pyc-invalid-delivery-"); const output = join(root, "unused.json"); - const run = cli.sharedCliRun?.bind(cli) ?? cli.run.bind(cli); + const run = cli.run.bind(cli); const view = JSON.stringify({ kind: "python-pyc", collection: "code_objects", diff --git a/tests/acceptance/applications/staticObservedEndpoints.test.ts b/tests/acceptance/applications/staticObservedEndpoints.test.ts index 83b458b2e5..b5581fc496 100644 --- a/tests/acceptance/applications/staticObservedEndpoints.test.ts +++ b/tests/acceptance/applications/staticObservedEndpoints.test.ts @@ -47,7 +47,7 @@ const prepareInputs = async (cli: TestCli) => { "f0e7d2c869c811d4272a7b2916c6531c8c6cc7938336a35575e2a6b7b6346714", ); await writeFile(harPath, harBytes); - const run = cli.sharedCliRun?.bind(cli) ?? cli.run.bind(cli); + const run = cli.run.bind(cli); const analyzed = await run({ arguments: [ "analyze-javascript-application", diff --git a/tests/acceptance/artifactDmgAttachmentRecoveryCli.test.ts b/tests/acceptance/artifactDmgAttachmentRecoveryCli.test.ts index 1510de190b..127d28339e 100644 --- a/tests/acceptance/artifactDmgAttachmentRecoveryCli.test.ts +++ b/tests/acceptance/artifactDmgAttachmentRecoveryCli.test.ts @@ -77,6 +77,8 @@ cliTest.skipIf(process.platform !== "darwin")( expect(before.length).toBeGreaterThan(0); expect(await attachedImagePaths()).toContain(resolvedImage); const busy = await cli.run({ + // Each command must release its own attachment when its process ends. + freshProcess: true, arguments: ["inspect-artifact", image, "--json"], }); expect(busy.exitCode, busy.stdout).toBe(1); @@ -115,6 +117,7 @@ cliTest.skipIf(process.platform !== "darwin")( } expect(await mountedDevices()).toEqual([]); const control = await cli.run({ + freshProcess: true, arguments: ["inspect-artifact", image, "--json"], }); expect(control.exitCode, control.stdout).toBe(0); diff --git a/tests/acceptance/cliBuiltinDocumentTokens.test.ts b/tests/acceptance/cliBuiltinDocumentTokens.test.ts index e84901de20..97669593ab 100644 --- a/tests/acceptance/cliBuiltinDocumentTokens.test.ts +++ b/tests/acceptance/cliBuiltinDocumentTokens.test.ts @@ -2,7 +2,6 @@ import { estimateTokenCount } from "tokenx"; import { expect } from "vitest"; import { workspaceCliTest } from "../support/cli/workspaceCliFixture.js"; -import { runFileCli } from "../support/cli/cliFixture.js"; workspaceCliTest( "applies token controls to SDK manifest and schema documents", @@ -12,12 +11,15 @@ workspaceCliTest( const freshTokenCount = arguments_.join(" ") === "--llms --token-count"; const freshTokenLimit = arguments_.join(" ") === "--llms-full --token-limit 1"; - const result = await (!freshTokenCount && - !freshTokenLimit && - (arguments_.includes("--token-count") || - arguments_.includes("--token-limit")) - ? runFileCli(cli, invocation) - : cli.run(invocation)); + const result = await cli.run({ + ...invocation, + freshProcess: !( + !freshTokenCount && + !freshTokenLimit && + (arguments_.includes("--token-count") || + arguments_.includes("--token-limit")) + ), + }); expect(result.exitCode, result.stderr).toBe(0); expect(result.stderr).toBe(""); return result.stdout; diff --git a/tests/acceptance/elfNoteCoreProfile.test.ts b/tests/acceptance/elfNoteCoreProfile.test.ts index bb91121501..6d36d98d48 100644 --- a/tests/acceptance/elfNoteCoreProfile.test.ts +++ b/tests/acceptance/elfNoteCoreProfile.test.ts @@ -36,7 +36,7 @@ cliTest.for([ bytes[8] = version; await writeFile(path, bytes); const hash = createHash("sha256").update(bytes).digest("hex"); - const output = await cli.sharedCliRun!({ + const output = await cli.run({ arguments: ["inspect-elf-notes", path, "--json"], }); expect(output.exitCode, output.stderr).toBe(0); diff --git a/tests/acceptance/elfNotesAbiEndian.test.ts b/tests/acceptance/elfNotesAbiEndian.test.ts index 819b11051b..c971da4ea2 100644 --- a/tests/acceptance/elfNotesAbiEndian.test.ts +++ b/tests/acceptance/elfNotesAbiEndian.test.ts @@ -51,7 +51,7 @@ for (const { bits, ownership, machine } of [ }); await writeFile(path, bytes); - const output = await cli.sharedCliRun!({ + const output = await cli.run({ arguments: ["inspect-elf-notes", path, "--json"], }); expect(output.exitCode, output.stderr).toBe(0); diff --git a/tests/acceptance/elfNotesAndroid.test.ts b/tests/acceptance/elfNotesAndroid.test.ts index 34289625e4..ae9f37cff1 100644 --- a/tests/acceptance/elfNotesAndroid.test.ts +++ b/tests/acceptance/elfNotesAndroid.test.ts @@ -65,7 +65,7 @@ for (const target of [ }), ); - const cliOut = await cli.sharedCliRun!({ + const cliOut = await cli.run({ arguments: ["inspect-elf-notes", path, "--json"], }); expect(cliOut.exitCode, cliOut.stderr).toBe(0); diff --git a/tests/acceptance/elfNotesCore.test.ts b/tests/acceptance/elfNotesCore.test.ts index 7f9076cd25..d2c7fadcd5 100644 --- a/tests/acceptance/elfNotesCore.test.ts +++ b/tests/acceptance/elfNotesCore.test.ts @@ -152,7 +152,7 @@ cliTest( }); await writeFile(path, bytes); - const output = await cli.sharedCliRun!({ + const output = await cli.run({ arguments: ["inspect-elf-notes", path, "--json"], }); expect(output.exitCode, output.stderr).toBe(0); diff --git a/tests/acceptance/goBinaryCli.test.ts b/tests/acceptance/goBinaryCli.test.ts index cd3800e17e..63a8296e7a 100644 --- a/tests/acceptance/goBinaryCli.test.ts +++ b/tests/acceptance/goBinaryCli.test.ts @@ -21,7 +21,7 @@ import { } from "../fixtures/go/image.js"; import { createGoRodataStringFixture } from "../fixtures/go/rodataStringImage.js"; import { createTestTempDirectory } from "../fixtures/temporaryDirectory.js"; -import { cliTest, runFileCli } from "../support/cli/cliFixture.js"; +import { cliTest } from "../support/cli/cliFixture.js"; import { parseEvidence } from "../../src/domain/evidence.js"; import { connectLocalToolsMcp } from "../fixtures/localToolsMcp.js"; import { expectCliMcpEvidenceParity } from "../fixtures/cliMcpEvidenceParity.js"; @@ -47,7 +47,7 @@ cliTest( ] as const) { const path = join(root, `${item.name}.macho`); await writeFile(path, item.bytes); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-go-binary", path, "--json"], }); const response = await call("inspect_go_binary", { path }); @@ -130,7 +130,7 @@ cliTest( ); const path = join(root, `${item.name}.elf`); await writeFile(path, item.fixture.bytes); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-go-binary", path, "--json"], }); const response = await call("inspect_go_binary", { path }); @@ -199,12 +199,12 @@ cliTest( const path = join(root, "application.elf"); const output = join(root, "source.json"); await writeFile(path, createGoBinaryFixture().bytes); - const complete = await runFileCli(cli, { + const complete = await cli.run({ arguments: ["inspect-go-binary", path, "--json"], }); expect(complete.exitCode, complete.stderr).toBe(0); const full = evidenceResultOf(goBinarySchema).parse(complete.json); - const summary = await runFileCli(cli, { + const summary = await cli.run({ arguments: [ "inspect-go-binary", path, @@ -233,7 +233,7 @@ cliTest( .map((record) => parseEvidence(record)) .find((record) => record.evidence_id === full.evidence_id), ).toEqual(full); - const refused = await runFileCli(cli, { + const refused = await cli.run({ arguments: [ "inspect-go-binary", join(root, "missing"), @@ -256,7 +256,7 @@ cliTest( const root = await createTestTempDirectory("rea-go-overlap-"); const path = join(root, "overlapping.elf"); await writeFile(path, createGoOverlappingElfFixture()); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-go-binary", path, "--json"], }); expect(result.exitCode).toBe(1); @@ -289,7 +289,7 @@ cliTest( const fixture = createGoSplitElfFixture({ sectionless }); const path = join(root, sectionless ? "sectionless.elf" : "original.elf"); await writeFile(path, fixture.bytes); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-go-binary", path, "--json"], }); expect(result.exitCode, result.stderr).toBe(0); @@ -324,7 +324,7 @@ for (const bits of [32, 64] as const) { ] as const) { const path = join(root, `${label}.exe`); await writeFile(path, bytes); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-go-binary", path, "--json"], }); expect(result.exitCode, result.stderr).toBe(0); @@ -379,7 +379,7 @@ cliTest( path, machoImageWithUnknownCommand(fixture.wide, fixture.size), ); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-go-binary", path, "--json"], }); const response = await call("inspect_go_binary", { path }); @@ -418,7 +418,7 @@ for (const expectedErrno of ["ELOOP", "ENAMETOOLONG"] as const) { root, expectedErrno, ); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-go-binary", path, "--json"], }); expect(result.exitCode).toBe(1); @@ -449,7 +449,7 @@ cliTest( const path = join(root, "byte-strings.elf"); const fixture = createGoBinaryByteStringFixture(); await writeFile(path, fixture.bytes); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-go-binary", path, "--json"], }); expect(result.exitCode, result.stderr).toBe(0); @@ -467,7 +467,7 @@ for (const fixture of createGoBinaryDiagnosticFixtures()) { const root = await createTestTempDirectory("rea-go-cli-mz-"); const path = join(root, `${fixture.name}.exe`); await writeFile(path, fixture.bytes); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-go-binary", path, "--json"], }); expect(result.exitCode).toBe(1); @@ -488,7 +488,7 @@ cliTest( const root = await createTestTempDirectory("rea-go-cli-"); const path = join(root, "text.bin"); await writeFile(path, "This is not an executable container."); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-go-binary", path, "--json"], }); expect(result.exitCode).toBe(1); @@ -508,7 +508,7 @@ for (const format of ["elf", "pe", "macho"] as const) { const fixture = createGoBinaryFixture({ format, encoding }); const path = join(root, "application.bin"); await writeFile(path, fixture.bytes); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-go-binary", "application.bin", "--json"], cwd: root, }); @@ -582,7 +582,7 @@ cliTest("preserves MIPS identity in Go CLI Evidence", async ({ cli }) => { fixture.bytes.writeUInt16LE(8, 18); // ELF EM_MIPS. const path = join(root, "application.elf"); await writeFile(path, fixture.bytes); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-go-binary", path, "--json"], }); expect(result.exitCode, result.stderr).toBe(0); @@ -601,7 +601,7 @@ cliTest( const root = await createTestTempDirectory("rea-go-cli-"); const path = join(root, "metadata-free.elf"); await writeFile(path, createGoBinaryFixture({ omitBuildInfo: true }).bytes); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-go-binary", path, "--json"], }); expect(result.exitCode).toBe(0); @@ -625,7 +625,7 @@ cliTest( const root = await createTestTempDirectory("rea-go-gopath-cli-"); const path = join(root, "application.elf"); await writeFile(path, createGoBinaryFixture({ moduleText }).bytes); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-go-binary", path, "--json"], }); expect(result.exitCode, result.stderr).toBe(0); @@ -662,7 +662,7 @@ cliTest( }); await writeFile(path, fixture.bytes); - const standaloneCap = await runFileCli(cli, { + const standaloneCap = await cli.run({ arguments: [ "inspect-go-binary", path, @@ -680,7 +680,7 @@ cliTest( }, }); - const cliResult = await runFileCli(cli, { + const cliResult = await cli.run({ arguments: [ "inspect-go-binary", path, diff --git a/tests/acceptance/goBinaryP32PointerCli.test.ts b/tests/acceptance/goBinaryP32PointerCli.test.ts index e5b87b5391..632bb35f44 100644 --- a/tests/acceptance/goBinaryP32PointerCli.test.ts +++ b/tests/acceptance/goBinaryP32PointerCli.test.ts @@ -5,7 +5,7 @@ import { evidenceResultOf } from "../../src/contracts/toolOutputSchemaPrimitives import { goBinarySchema } from "../../src/domain/go/goBinary.js"; import { createGoBinaryFixture, GO_MODULE_TEXT } from "../fixtures/go/image.js"; import { createTestTempDirectory } from "../fixtures/temporaryDirectory.js"; -import { cliTest, runFileCli } from "../support/cli/cliFixture.js"; +import { cliTest } from "../support/cli/cliFixture.js"; cliTest( "inspects ELF64 Go metadata with four-byte amd64p32 pointers through CLI", @@ -20,7 +20,7 @@ cliTest( }); const path = join(root, "nacl-amd64p32.nexe"); await writeFile(path, fixture.bytes); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-go-binary", path, "--json"], }); diff --git a/tests/acceptance/goBinaryPointerMappingsCli.test.ts b/tests/acceptance/goBinaryPointerMappingsCli.test.ts index bc092902d5..92a293e0d2 100644 --- a/tests/acceptance/goBinaryPointerMappingsCli.test.ts +++ b/tests/acceptance/goBinaryPointerMappingsCli.test.ts @@ -12,7 +12,7 @@ import { connectLocalToolsMcp } from "../fixtures/localToolsMcp.js"; import { expectCliMcpEvidenceParity } from "../fixtures/cliMcpEvidenceParity.js"; import { parseMcpToolError } from "../fixtures/mcpToolError.js"; import { createTestTempDirectory } from "../fixtures/temporaryDirectory.js"; -import { cliTest, runFileCli } from "../support/cli/cliFixture.js"; +import { cliTest } from "../support/cli/cliFixture.js"; cliTest( "retains header-backed PE and cross-load ELF pointer metadata through CLI and MCP", @@ -34,7 +34,7 @@ cliTest( for (const { name, fixture } of cases) { const path = join(root, name); await writeFile(path, fixture.bytes); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-go-binary", path, "--json"], }); expect(result.exitCode, result.stderr).toBe(0); @@ -64,7 +64,7 @@ cliTest( damaged.bytes.writeBigUInt64LE(0x11001n, 200); // A one-byte virtual gap. const path = join(root, "elf-cross-load-gap"); await writeFile(path, damaged.bytes); - const rejected = await runFileCli(cli, { + const rejected = await cli.run({ arguments: ["inspect-go-binary", path, "--json"], }); expect(rejected.exitCode).toBe(1); diff --git a/tests/acceptance/gpuCodeObjectsCli.test.ts b/tests/acceptance/gpuCodeObjectsCli.test.ts index a5f952775c..2967daf08f 100644 --- a/tests/acceptance/gpuCodeObjectsCli.test.ts +++ b/tests/acceptance/gpuCodeObjectsCli.test.ts @@ -13,7 +13,7 @@ import { parseEvidence, createEvidence } from "../../src/domain/evidence.js"; import { createTestTempDirectory } from "../fixtures/temporaryDirectory.js"; import { connectLocalToolsMcp } from "../fixtures/localToolsMcp.js"; import { parseMcpToolError } from "../fixtures/mcpToolError.js"; -import { cliTest, runFileCli } from "../support/cli/cliFixture.js"; +import { cliTest } from "../support/cli/cliFixture.js"; import { cudaFixture, peGpuFixture, @@ -38,7 +38,7 @@ cliTest( ] as const) { const path = join(root, name); await writeFile(path, bytes); - const response = await runFileCli(cli, { + const response = await cli.run({ arguments: [ "inspect-gpu-code-objects", path, @@ -76,7 +76,7 @@ cliTest( const bytes = cubinFixture({ name: "k".repeat(65_536) }); const path = join(root, "long-symbol.cubin"); await writeFile(path, bytes); - const response = await runFileCli(cli, { + const response = await cli.run({ arguments: [ "inspect-gpu-code-objects", path, @@ -124,7 +124,7 @@ cliTest( const bytes = Buffer.concat([header, compressed]), path = join(root, `v${version}`); await writeFile(path, bytes); - const response = await runFileCli(cli, { + const response = await cli.run({ arguments: [ "inspect-gpu-code-objects", path, @@ -183,7 +183,7 @@ cliTest( const bytes = hipFixture(payload, bundleId), path = join(root, name); await writeFile(path, bytes); - const response = await runFileCli(cli, { + const response = await cli.run({ arguments: [ "inspect-gpu-code-objects", path, @@ -233,7 +233,7 @@ cliTest( } const path = join(root, "raw-bitcode"); await writeFile(path, bitcode); - const raw = await runFileCli(cli, { + const raw = await cli.run({ arguments: [ "inspect-gpu-code-objects", path, @@ -271,7 +271,7 @@ cliTest( ]), path = join(root, name); await writeFile(path, bytes); - const response = await runFileCli(cli, { + const response = await cli.run({ arguments: [ "inspect-gpu-code-objects", path, @@ -355,7 +355,7 @@ cliTest( ]), path = join(root, name); await writeFile(path, bytes); - const response = await runFileCli(cli, { + const response = await cli.run({ arguments: [ "inspect-gpu-code-objects", path, @@ -396,7 +396,7 @@ cliTest( ]), path = join(root, "large-parameter.cubin"); await writeFile(path, bytes); - const response = await runFileCli(cli, { + const response = await cli.run({ arguments: [ "inspect-gpu-code-objects", path, @@ -433,7 +433,7 @@ cliTest( const bytes = hipFixture(payload, "hip-amdgcn-amd-amdhsa--gfx906+xnack"), path = join(root, "feature.bundle"); await writeFile(path, bytes); - const response = await runFileCli(cli, { + const response = await cli.run({ arguments: [ "inspect-gpu-code-objects", path, @@ -478,7 +478,7 @@ cliTest( root = await createTestTempDirectory("rea-ptx-callprototype-"), path = join(root, "prototype.ptx"); await writeFile(path, source); - const response = await runFileCli(cli, { + const response = await cli.run({ arguments: [ "inspect-gpu-code-objects", path, @@ -528,7 +528,7 @@ cliTest( const bytes = cubinFixture({ extendedSectionIndex: true }); const path = join(root, "extended.cubin"); await writeFile(path, bytes); - const response = await runFileCli(cli, { + const response = await cli.run({ arguments: [ "inspect-gpu-code-objects", path, @@ -576,7 +576,7 @@ cliTest( malformedBytes.writeBigUInt64LE(4n, sectionTable + indexTable! * 64 + 32); const malformedPath = join(root, "short-shndx.cubin"); await writeFile(malformedPath, malformedBytes); - const malformed = await runFileCli(cli, { + const malformed = await cli.run({ arguments: [ "inspect-gpu-code-objects", malformedPath, @@ -607,7 +607,7 @@ cliTest( const text = `.version 7.0\n.target sm_80\n.address_size 64\n.visible .entry kernel() {\n.reg .b32 ${register};\nmov.u32 ${register}, 0;\nret;\n}\n`; const path = join(root, `${register.slice(1)}.ptx`); await writeFile(path, text); - const response = await runFileCli(cli, { + const response = await cli.run({ arguments: [ "inspect-gpu-code-objects", path, @@ -644,7 +644,7 @@ cliTest( const text = `.version 8.8\n.target sm_80\n.address_size 64\n.entry %kernel(.param .u32 input) {\nret;\n}\n`; const path = join(root, "identifiers.ptx"); await writeFile(path, text); - const response = await runFileCli(cli, { + const response = await cli.run({ arguments: [ "inspect-gpu-code-objects", path, @@ -707,7 +707,7 @@ cliTest( const text = `${preamble}${declaration}\n`, path = join(root, `${name}.ptx`); await writeFile(path, text); - const response = await runFileCli(cli, { + const response = await cli.run({ arguments: [ "inspect-gpu-code-objects", path, @@ -757,7 +757,7 @@ cliTest( const text = `.version 8.8\n.target compute_80\n.address_size 64\n.entry kernel() {\nret;\n}\n`; const path = join(root, "target.ptx"); await writeFile(path, text); - const response = await runFileCli(cli, { + const response = await cli.run({ arguments: [ "inspect-gpu-code-objects", path, @@ -787,7 +787,7 @@ cliTest( const text = `.version 1.3\n.target sm_80\n.address_size 64\n.entry kernel {\n.param .u32 input;\nret;\n}\n`; const path = join(root, "legacy.ptx"); await writeFile(path, text); - const response = await runFileCli(cli, { + const response = await cli.run({ arguments: [ "inspect-gpu-code-objects", path, @@ -826,7 +826,7 @@ cliTest( malformedPath, `.version 1.3\n.target sm_80\n.entry kernel {\n.param .u32 input\n`, ); - const malformed = await runFileCli(cli, { + const malformed = await cli.run({ arguments: [ "inspect-gpu-code-objects", malformedPath, @@ -853,7 +853,7 @@ cliTest( "attributes malformed selector JSON to its actual CLI option", async ({ cli }) => { for (const name of ["object_ids", "targets", "view"]) { - const response = await runFileCli(cli, { + const response = await cli.run({ arguments: [ "inspect-gpu-code-objects", "/not-opened-for-invalid-selectors", @@ -893,7 +893,7 @@ cliTest( }, }); // PowerShell's Get-FileHash prints the digest in upper case. - const response = await runFileCli(cli, { + const response = await cli.run({ arguments: [ "inspect-gpu-code-objects", "/not-opened-for-invalid-selectors", @@ -1339,7 +1339,7 @@ cliTest( ] as const) { const path = join(root, name); await writeFile(path, bytes); - const response = await runFileCli(cli, { + const response = await cli.run({ arguments: [ "inspect-gpu-code-objects", path, diff --git a/tests/acceptance/il2cppMetadataCli.test.ts b/tests/acceptance/il2cppMetadataCli.test.ts index 3960d59585..a90187d1ed 100644 --- a/tests/acceptance/il2cppMetadataCli.test.ts +++ b/tests/acceptance/il2cppMetadataCli.test.ts @@ -44,7 +44,7 @@ import { import { connectLocalToolsMcp } from "../fixtures/localToolsMcp.js"; import { parseMcpToolError } from "../fixtures/mcpToolError.js"; import { createTestTempDirectory } from "../fixtures/temporaryDirectory.js"; -import { cliTest, runFileCli } from "../support/cli/cliFixture.js"; +import { cliTest } from "../support/cli/cliFixture.js"; const parseCliMetadata = (value: unknown) => il2cppMetadataSchema.parse( @@ -75,7 +75,7 @@ cliTest( } strings.copy(bytes, 500); await writeFile(path, bytes); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-il2cpp-metadata", path, "--json"], }); expect(result.exitCode).toBe(1); @@ -122,7 +122,7 @@ cliTest( createLinkedIl2cppMetadataFixture({ version: 39 }).bytes, ); await writeFile(binaryPath, createIl2cppBinaryFixture()); - const produced = await runFileCli(cli, { + const produced = await cli.run({ arguments: [ "inspect-il2cpp-metadata", path, @@ -161,7 +161,7 @@ cliTest( }; const query = join(root, `image-${image}.json`); await writeFile(query, JSON.stringify(request)); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-analysis-view", query, "--json"], }); const mcp = await call("inspect_analysis_view", request); @@ -222,7 +222,7 @@ cliTest( }; const query = join(root, `${scenario}.json`); await writeFile(query, JSON.stringify(request)); - const cliResult = await runFileCli(cli, { + const cliResult = await cli.run({ arguments: ["inspect-analysis-view", query, "--json"], }); const mcp = await call("inspect_analysis_view", request); @@ -249,7 +249,7 @@ cliTest( }); const path = join(root, `valid-${count}.dat`); await writeFile(path, bytes); - const output = await runFileCli(cli, { + const output = await cli.run({ arguments: ["inspect-il2cpp-metadata", path, "--json"], }); expect(output.exitCode, output.stderr).toBe(0); @@ -282,7 +282,7 @@ cliTest( ); const path = join(root, `invalid-${declaredCount}-${declaredBytes}.dat`); await writeFile(path, fixture.bytes); - const output = await runFileCli(cli, { + const output = await cli.run({ arguments: ["inspect-il2cpp-metadata", path, "--json"], }); expect(output.exitCode).toBe(1); @@ -321,7 +321,7 @@ cliTest( propertiesAndEvents: true, }); await writeFile(path, bytes); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-il2cpp-metadata", path, "--json"], }); expect(result.exitCode, result.stderr).toBe(0); @@ -472,7 +472,7 @@ cliTest( ] as const) { const path = join(root, name); await writeFile(path, bytes); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-il2cpp-metadata", path, "--json"], }); expect(result.exitCode).toBe(1); @@ -521,7 +521,7 @@ cliTest( }, }, }; - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-il2cpp-metadata", path, "--json"], }); expect(result.exitCode).toBe(1); @@ -828,7 +828,7 @@ cliTest( const root = await createTestTempDirectory("rea-il2cpp-cli-export-"); const path = join(root, "global-metadata.dat"); const bundle = join(root, "evidence.json"); - const refused = await runFileCli(cli, { + const refused = await cli.run({ arguments: [ "inspect-il2cpp-metadata", join(root, "must-not-be-read"), @@ -852,14 +852,14 @@ cliTest( bundle, "--json", ]; - const result = await runFileCli(cli, { arguments: args }); + const result = await cli.run({ arguments: args }); expect(result.exitCode, result.stderr).toBe(0); expect(result.json).toMatchObject({ evidence_bundle: { path: bundle, bytes: expect.any(Number) }, summary: { normalized_result: { kind: "summary" } }, }); const before = await readFile(bundle); - const overwrite = await runFileCli(cli, { arguments: args }); + const overwrite = await cli.run({ arguments: args }); expect(overwrite.exitCode).toBe(1); expect(Buffer.compare(await readFile(bundle), before)).toBe(0); const saved = await readEvidenceBundle(bundle); @@ -887,7 +887,7 @@ cliTest( }, }), ); - const selected = await runFileCli(cli, { + const selected = await cli.run({ arguments: ["inspect-analysis-view", query, "--json"], }); expect(selected.exitCode, selected.stderr).toBe(0); @@ -913,7 +913,7 @@ cliTest.skipIf(process.platform === "win32")( const decoy = join(root, "evidence.json"); await writeFile(decoy, "unrelated caller data"); const selected = `${root}/link/../evidence.json`; - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: [ "inspect-il2cpp-metadata", path, @@ -932,7 +932,7 @@ cliTest.skipIf(process.platform === "win32")( expect( JSON.parse(await readFile(join(actual, "evidence.json"), "utf8")), ).toEqual(saved.value); - const read = await runFileCli(cli, { + const read = await cli.run({ arguments: [ "inspect-evidence-bundle", JSON.stringify({ path: selected, detail: "summary" }), @@ -966,7 +966,7 @@ cliTest( rva: `0x${(address - FIXTURE_BINARY_BASE).toString(16)}`, }, ); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: [ "inspect-il2cpp-metadata", path, @@ -1044,7 +1044,7 @@ cliTest( await writeFile(path, createLinkedIl2cppMetadataFixture().bytes); await writeFile(binaryPath, createIl2cppDyldBindFixture(0n)); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: [ "inspect-il2cpp-metadata", path, @@ -1089,7 +1089,7 @@ cliTest( await writeFile(path, createLinkedIl2cppMetadataFixture().bytes); const inspect = async (bytes: Buffer) => { await writeFile(binaryPath, bytes); - const output = await runFileCli(cli, { + const output = await cli.run({ arguments: [ "inspect-il2cpp-metadata", path, @@ -1162,7 +1162,7 @@ cliTest( bytes.fill(0xff, 0x604, 0x608); const binaryPath = join(root, `registration-${tailBytes}.dylib`); await writeFile(binaryPath, bytes); - const output = await runFileCli(cli, { + const output = await cli.run({ arguments: [ "inspect-il2cpp-metadata", path, @@ -1252,7 +1252,7 @@ cliTest( const binaryPath = join(root, `${entry.name}.dylib`); await writeFile(path, entry.metadata.bytes); await writeFile(binaryPath, entry.binary); - const cliResult = await runFileCli(cli, { + const cliResult = await cli.run({ arguments: [ "inspect-il2cpp-metadata", path, @@ -1307,7 +1307,7 @@ cliTest( { machine: 62 }, ), ); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: [ "inspect-il2cpp-metadata", path, @@ -1338,7 +1338,7 @@ cliTest( await writeFile(path, createLinkedIl2cppMetadataFixture().bytes); await writeFile(binaryPath, createElfFixture(createIl2cppBinaryFixture())); const run = (output: string, extra: readonly string[] = []) => - runFileCli(cli, { + cli.run({ arguments: [ "inspect-il2cpp-metadata", path, @@ -1393,7 +1393,7 @@ cliTest( }); expect(await readFile(seeds, "utf8")).toBe("kept\n"); - const unlinked = await runFileCli(cli, { + const unlinked = await cli.run({ arguments: [ "inspect-il2cpp-metadata", path, @@ -1432,7 +1432,7 @@ cliTest( binaryPath, createElfFixture(createIl2cppBinaryFixture({ genericMethods: {} })), ); - const linked = await runFileCli(cli, { + const linked = await cli.run({ arguments: [ "inspect-il2cpp-metadata", path, @@ -1458,7 +1458,7 @@ cliTest( ].join("\n"), ); - const unlinked = await runFileCli(cli, { + const unlinked = await cli.run({ arguments: [ "inspect-il2cpp-metadata", path, @@ -1508,7 +1508,7 @@ cliTest( const inspect = async (binary: Buffer) => { await writeFile(binaryPath, binary); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: [ "inspect-il2cpp-metadata", path, @@ -1551,7 +1551,7 @@ cliTest.skipIf(process.platform === "win32")( await writeFile(path, createLinkedIl2cppMetadataFixture().bytes); await writeFile(binaryPath, createElfFixture(createIl2cppBinaryFixture())); await writeFile(parent, "unchanged"); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: [ "inspect-il2cpp-metadata", path, diff --git a/tests/acceptance/il2cppStructuralExtentsCli.test.ts b/tests/acceptance/il2cppStructuralExtentsCli.test.ts index 6cbe54c3aa..dea00a124b 100644 --- a/tests/acceptance/il2cppStructuralExtentsCli.test.ts +++ b/tests/acceptance/il2cppStructuralExtentsCli.test.ts @@ -5,7 +5,7 @@ import { createIl2cppMetadataFixture } from "../fixtures/il2cpp/metadata.js"; import { connectLocalToolsMcp } from "../fixtures/localToolsMcp.js"; import { parseMcpToolError } from "../fixtures/mcpToolError.js"; import { createTestTempDirectory } from "../fixtures/temporaryDirectory.js"; -import { cliTest, runFileCli } from "../support/cli/cliFixture.js"; +import { cliTest } from "../support/cli/cliFixture.js"; cliTest( "rejects partial IL2CPP table extents through CLI and MCP without a binary", @@ -24,7 +24,7 @@ cliTest( }); const path = join(root, `${version}-${name}.dat`); await writeFile(path, fixture.bytes); - const intact = await runFileCli(cli, { + const intact = await cli.run({ arguments: ["inspect-il2cpp-metadata", path, "--json"], }); expect(intact.exitCode, intact.stderr).toBe(0); @@ -35,7 +35,7 @@ cliTest( fixture.header(name, "size"), ); await writeFile(path, damaged); - const invalid = await runFileCli(cli, { + const invalid = await cli.run({ arguments: ["inspect-il2cpp-metadata", path, "--json"], }); const expected = { diff --git a/tests/acceptance/localPathAdmissionCli.test.ts b/tests/acceptance/localPathAdmissionCli.test.ts index ecc1c46842..84eb2d83f6 100644 --- a/tests/acceptance/localPathAdmissionCli.test.ts +++ b/tests/acceptance/localPathAdmissionCli.test.ts @@ -6,7 +6,7 @@ import { expect } from "vitest"; import { parseEvidence } from "../../src/domain/evidence.js"; import { pythonPycFixture } from "../../src/python/PythonPyc.fixture.js"; import { createTestTempDirectory } from "../fixtures/temporaryDirectory.js"; -import { cliTest, runFileCli } from "../support/cli/cliFixture.js"; +import { cliTest } from "../support/cli/cliFixture.js"; const fileCommands = [ "inspect-sqlite-database", @@ -51,7 +51,7 @@ cliTest( }, ]; for (const selection of selections) { - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: [...selection.arguments, "--json"], cwd, }); @@ -161,7 +161,7 @@ cliTest( }, ]; for (const selection of selections) { - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: [...selection.arguments, "--json"], cwd, }); @@ -183,7 +183,7 @@ cliTest( const bytes = pythonPycFixture(); const path = join(cwd, "selected.pyc"); await writeFile(path, bytes); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-python-pyc", "./selected.pyc", "--json"], cwd, }); diff --git a/tests/acceptance/managedCliOutput.test.ts b/tests/acceptance/managedCliOutput.test.ts index 737e5bc55c..590079ba37 100644 --- a/tests/acceptance/managedCliOutput.test.ts +++ b/tests/acceptance/managedCliOutput.test.ts @@ -7,7 +7,7 @@ import { expect } from "vitest"; import { elf, thinMach } from "../../src/domain/binaryTarget.fixture.js"; import { buildManagedPeFixture } from "../../src/dotnet/ManagedPe.fixture.js"; import { createTestTempDirectory } from "../fixtures/temporaryDirectory.js"; -import { cliTest, runFileCli } from "../support/cli/cliFixture.js"; +import { cliTest } from "../support/cli/cliFixture.js"; cliTest( "recovers the same managed artifact through decimal CLI budgets and retains caller choices", @@ -157,7 +157,7 @@ cliTest( ilBody: Buffer.concat([Buffer.from([(il.length << 2) | 2]), il]), }), ); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-managed-members", path, "--json"], }); expect(result.exitCode, result.stderr).toBe(0); @@ -546,7 +546,7 @@ cliTest( }); const path = join(root, `virtual-${virtualSectionSize}.dll`); await writeFile(path, bytes); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-managed-members", path, "--json"], }); expect(result.exitCode).toBe(0); @@ -647,7 +647,7 @@ cliTest( "inspect-managed-members", "inspect-managed-native-boundaries", ]) { - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: [command, path, "--json"], }); expect(result.exitCode).toBe(1); @@ -682,7 +682,7 @@ cliTest( "inspect-managed-members", "inspect-managed-native-boundaries", ]) { - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: [command, path, "--json"], }); expect(result.exitCode).not.toBe(0); diff --git a/tests/acceptance/managedMetadataDelta.test.ts b/tests/acceptance/managedMetadataDelta.test.ts index ef20016df5..832834a6e1 100644 --- a/tests/acceptance/managedMetadataDelta.test.ts +++ b/tests/acceptance/managedMetadataDelta.test.ts @@ -37,7 +37,7 @@ cliTest( ], ] as const) { const selected = operation === "inspect_managed_members"; - const output = await cli.sharedCliRun!({ + const output = await cli.run({ arguments: [ command, path, diff --git a/tests/acceptance/managedSelectedScope.test.ts b/tests/acceptance/managedSelectedScope.test.ts index 5eb2789289..52a60986b3 100644 --- a/tests/acceptance/managedSelectedScope.test.ts +++ b/tests/acceptance/managedSelectedScope.test.ts @@ -27,7 +27,7 @@ cliTest( ["Inner", "Fixture.Inner"], ["Fixture.Inner"], ]) { - const output = await cli.sharedCliRun!({ + const output = await cli.run({ arguments: [ "inspect-managed-members", path, @@ -99,7 +99,7 @@ cliTest( const sha256 = createHash("sha256").update(content).digest("hex"); await writeFile(path, content); for (const isSelected of [true, false]) { - const output = await cli.sharedCliRun!({ + const output = await cli.run({ arguments: [ "inspect-managed-members", path, diff --git a/tests/acceptance/modelArtifactCli.test.ts b/tests/acceptance/modelArtifactCli.test.ts index 23e766f398..d6d39fc5fd 100644 --- a/tests/acceptance/modelArtifactCli.test.ts +++ b/tests/acceptance/modelArtifactCli.test.ts @@ -22,11 +22,7 @@ import { safetensorsFixture, } from "../fixtures/model/model.js"; import { createTestTempDirectory } from "../fixtures/temporaryDirectory.js"; -import { - cliTest, - createSharedMcpSession, - runFileCli, -} from "../support/cli/cliFixture.js"; +import { cliTest, createSharedMcpSession } from "../support/cli/cliFixture.js"; // Model queries are target-free; share immutable registration and schema setup. // Portable export keeps its separate post-CLI connection below. @@ -113,7 +109,7 @@ cliTest( }); const bytes = Buffer.concat([fixture.bytes, Buffer.alloc(16)]); await writeFile(path, bytes); - const cliResult = await runFileCli(cli, { + const cliResult = await cli.run({ arguments: [ "inspect-model-artifact", path, @@ -365,7 +361,7 @@ cliTest( tensors, }); await writeFile(path, fixture.bytes); - const response = await runFileCli(cli, { + const response = await cli.run({ arguments: [ "inspect-model-artifact", path, @@ -999,7 +995,7 @@ cliTest( ] as const) { const path = join(root, name); await writeFile(path, bytes); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: [ "inspect-model-artifact", path, @@ -1047,7 +1043,7 @@ cliTest( Buffer.alloc(0), ).bytes, ); - const mapCli = await runFileCli(cli, { + const mapCli = await cli.run({ arguments: [ "inspect-model-artifact", mapPath, @@ -1084,7 +1080,7 @@ cliTest( safetensorsFixture(`{"__metadata__":${numeric}}`, Buffer.alloc(0)) .bytes, ); - const malformedCli = await runFileCli(cli, { + const malformedCli = await cli.run({ arguments: ["inspect-model-artifact", malformedPath, "--json"], }); expect(malformedCli.exitCode).toBe(1); diff --git a/tests/acceptance/peCertificatesCli.test.ts b/tests/acceptance/peCertificatesCli.test.ts index 9f01c5328f..e4aa45f812 100644 --- a/tests/acceptance/peCertificatesCli.test.ts +++ b/tests/acceptance/peCertificatesCli.test.ts @@ -12,7 +12,7 @@ import { import { repeatedCertificateSignedData } from "../fixtures/peCertificateAllowance.js"; import { parseMcpToolError } from "../fixtures/mcpToolError.js"; import { createTestTempDirectory } from "../fixtures/temporaryDirectory.js"; -import { cliTest, runFileCli } from "../support/cli/cliFixture.js"; +import { cliTest } from "../support/cli/cliFixture.js"; cliTest.for([0x00, 0x01, 0x7f, 0xfe, 0xff, null])( "requires canonical extension critical encoding %s through CLI and MCP", @@ -41,7 +41,7 @@ cliTest.for([0x00, 0x01, 0x7f, 0xfe, 0xff, null])( }), ); await writeFile(path, bytes); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-pe-certificates", path, "--json"], }); const call = await cli.sharedMcpCall!(); @@ -104,7 +104,7 @@ cliTest.for(["extra-field", "empty-rdn"] as const)( ); const bytes = peCertificateFixture(false, payload); await writeFile(path, bytes); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-pe-certificates", path, "--json"], }); expect.soft(result.exitCode, result.stdout).toBe(1); @@ -138,7 +138,7 @@ cliTest.for(["pubkey", "signature"])( expect(at).toBeGreaterThanOrEqual(0); payload[at + 2] = 1; await writeFile(path, peCertificateFixture(false, payload)); - const invalid = await runFileCli(cli, { + const invalid = await cli.run({ arguments: ["inspect-pe-certificates", path, "--json"], }); expect(invalid.exitCode).toBe(1); @@ -159,7 +159,7 @@ cliTest.for(["pubkey", "signature"])( const last = at + marker.length - 1; payload[last] = payload[last]! & 0xfe; await writeFile(path, peCertificateFixture(false, payload)); - const valid = await runFileCli(cli, { + const valid = await cli.run({ arguments: ["inspect-pe-certificates", path, "--json"], }); expect(valid.exitCode, valid.stderr).toBe(0); @@ -210,7 +210,7 @@ cliTest("reports unsigned attributes through CLI and MCP", async ({ cli }) => { [1, 3, 6, 1, 4, 1, 311, 3, 3, 1], ]); await writeFile(path, peCertificateFixture(true, payload)); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-pe-certificates", path, "--json"], }); expect(result.exitCode, result.stderr).toBe(0); @@ -239,7 +239,7 @@ cliTest( const payload = pkcs7SignedDataIndefiniteEnvelopeFixture(); await writeFile(path, peCertificateFixture(false, payload)); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-pe-certificates", path, "--json"], }); @@ -297,7 +297,7 @@ cliTest("reports a truncated PE as invalid input", async ({ cli }) => { const root = await createTestTempDirectory("rea-pe-cert-cli-bad-"); const path = join(root, "broken.exe"); await writeFile(path, Buffer.from("MZ")); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-pe-certificates", path, "--json"], }); expect(result.exitCode).toBe(1); @@ -318,7 +318,7 @@ cliTest( // Replace an equal-length UTF8String with T.61's acute accent plus 'e'. Buffer.from([0x14, 0x02, 0xc2, 0x65]).copy(payload, nameAt); await writeFile(path, peCertificateFixture(false, payload)); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-pe-certificates", path, "--json"], }); expect(result.exitCode, result.stderr).toBe(0); @@ -348,7 +348,7 @@ cliTest( payload.subarray(4), ]); await writeFile(path, peCertificateFixture(false, malformed)); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-pe-certificates", path, "--json"], }); expect(result.exitCode).toBe(1); @@ -372,7 +372,7 @@ cliTest( // Equal-size replacement keeps the DER lengths and all unrelated fields intact. Buffer.from([0x1e, 0x04, 0xd8, 0, 0xdc, 0]).copy(payload, nameAt); await writeFile(path, peCertificateFixture(false, payload)); - const invalid = await runFileCli(cli, { + const invalid = await cli.run({ arguments: ["inspect-pe-certificates", path, "--json"], }); expect(invalid.exitCode).toBe(1); @@ -382,7 +382,7 @@ cliTest( }); Buffer.from([0x1e, 0x04, 0xd7, 0xff, 0xe0, 0]).copy(payload, nameAt); await writeFile(path, peCertificateFixture(false, payload)); - const valid = await runFileCli(cli, { + const valid = await cli.run({ arguments: ["inspect-pe-certificates", path, "--json"], }); expect(valid.exitCode, valid.stderr).toBe(0); @@ -407,7 +407,7 @@ cliTest( // 2025 has no 29 February; the equal-length edit keeps every DER length. payload.write("250229000000Z", timeAt, "ascii"); await writeFile(path, peCertificateFixture(false, payload)); - const invalid = await runFileCli(cli, { + const invalid = await cli.run({ arguments: ["inspect-pe-certificates", path, "--json"], }); expect(invalid.exitCode).toBe(1); @@ -417,7 +417,7 @@ cliTest( }); payload.write("240229000000Z", timeAt, "ascii"); await writeFile(path, peCertificateFixture(false, payload)); - const valid = await runFileCli(cli, { + const valid = await cli.run({ arguments: ["inspect-pe-certificates", path, "--json"], }); expect(valid.exitCode, valid.stderr).toBe(0); @@ -521,7 +521,7 @@ cliTest.for([ expect(at).toBeGreaterThanOrEqual(0); payload[at] = tag; await writeFile(path, peCertificateFixture(false, payload)); - const invalid = await runFileCli(cli, { + const invalid = await cli.run({ arguments: ["inspect-pe-certificates", path, "--json"], }); expect(invalid.exitCode).toBe(1); @@ -532,7 +532,7 @@ cliTest.for([ // Only restore the string tag: the same bytes are valid UTF8String content. payload[at] = 0x0c; await writeFile(path, peCertificateFixture(false, payload)); - const valid = await runFileCli(cli, { + const valid = await cli.run({ arguments: ["inspect-pe-certificates", path, "--json"], }); expect(valid.exitCode, valid.stderr).toBe(0); diff --git a/tests/acceptance/peImportsCli.test.ts b/tests/acceptance/peImportsCli.test.ts index e2ced905e7..a26015719a 100644 --- a/tests/acceptance/peImportsCli.test.ts +++ b/tests/acceptance/peImportsCli.test.ts @@ -14,7 +14,7 @@ import { } from "../fixtures/binaryDiagnostics/peImports.js"; import { createTestTempDirectory } from "../fixtures/temporaryDirectory.js"; import { parseMcpToolError } from "../fixtures/mcpToolError.js"; -import { cliTest, runFileCli } from "../support/cli/cliFixture.js"; +import { cliTest } from "../support/cli/cliFixture.js"; for (const bits of [32, 64] as const) { cliTest( @@ -263,9 +263,10 @@ cliTest( const invocation = { arguments: ["inspect-pe-imports", path, "--json"], }; - const result = await (rva === 0xffffffec - ? cli.run(invocation) - : runFileCli(cli, invocation)); + const result = await cli.run({ + ...invocation, + freshProcess: rva === 0xffffffec, + }); expect(result.exitCode, result.stderr).toBe(0); const report = toolContract("inspect_pe_imports").outputSchema.parse( result.json, diff --git a/tests/acceptance/pythonPycCli.test.ts b/tests/acceptance/pythonPycCli.test.ts index a8982f5e50..3b2220fad4 100644 --- a/tests/acceptance/pythonPycCli.test.ts +++ b/tests/acceptance/pythonPycCli.test.ts @@ -10,7 +10,7 @@ import { pythonPycHeader, } from "../../src/python/PythonPyc.fixture.js"; import { createTestTempDirectory } from "../fixtures/temporaryDirectory.js"; -import { cliTest, runFileCli } from "../support/cli/cliFixture.js"; +import { cliTest } from "../support/cli/cliFixture.js"; const completePycEvidenceSchema = toolContract( "inspect_python_pyc", @@ -57,7 +57,7 @@ cliTest( }), ]), ); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-python-pyc", path, "--json"], }); expect(result.exitCode, result.stderr).toBe(0); @@ -77,7 +77,7 @@ cliTest( }), ]), ); - const ordinaryInt = await runFileCli(cli, { + const ordinaryInt = await cli.run({ arguments: ["inspect-python-pyc", ordinaryIntPath, "--json"], }); expect(ordinaryInt.exitCode, ordinaryInt.stderr).toBe(0); @@ -95,7 +95,7 @@ cliTest( Buffer.alloc(7), ]), ); - const truncated = await runFileCli(cli, { + const truncated = await cli.run({ arguments: ["inspect-python-pyc", truncatedPath, "--json"], }); expect(truncated.exitCode).toBe(1); @@ -183,7 +183,7 @@ cliTest( const path = join(root, `inline-caches-${magic}.pyc`); await writeFile(path, pyc); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-python-pyc", path, "--json"], }); expect( @@ -278,7 +278,7 @@ cliTest( header[2] = 0x0d; header[3] = 0x0a; await writeFile(path, Buffer.concat([header, marshal])); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-python-pyc", path, "--json"], }); expect(result.exitCode).toBe(1); @@ -295,7 +295,7 @@ cliTest( for (const magic of [3392, 3394]) { const path = join(root, `${magic}.pyc`); await writeFile(path, Buffer.concat([pythonPycHeader(magic), marshal])); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-python-pyc", path, "--json"], }); expect(result.exitCode, result.stderr).toBe(0); @@ -354,7 +354,7 @@ cliTest( path, Buffer.concat([pythonPycHeader(magic), marshalCode(layout, fields)]), ); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-python-pyc", path, "--json"], }); expect(result.exitCode, result.stderr).toBe(0); @@ -413,7 +413,7 @@ cliTest( path, Buffer.concat([pythonPycHeader(magic), marshalCode(layout, fields)]), ); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-python-pyc", path, "--json"], }); expect(result.exitCode).toBe(1); @@ -503,7 +503,7 @@ cliTest( }), ]), ); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-python-pyc", path, "--json"], }); expect(result.exitCode, result.stderr).toBe(0); @@ -553,7 +553,7 @@ cliTest( marshalCode(testCase.layout, { consts: [frozenSet, dict] }), ]), ); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-python-pyc", path, "--json"], }); expect(result.exitCode, result.stderr).toBe(0); @@ -631,7 +631,7 @@ cliTest( marshalCode(testCase.layout, { consts: [frozenSet, dict] }), ]), ); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-python-pyc", path, "--json"], }); expect(result.exitCode, result.stderr).toBe(0); @@ -662,7 +662,7 @@ cliTest( }), ]), ); - const rawResult = await runFileCli(cli, { + const rawResult = await cli.run({ arguments: ["inspect-python-pyc", rawPath, "--json"], }); expect(rawResult.exitCode, rawResult.stderr).toBe(0); diff --git a/tests/acceptance/rizinCommandRejection.test.ts b/tests/acceptance/rizinCommandRejection.test.ts index cfddb0747c..0b2f5e8b3e 100644 --- a/tests/acceptance/rizinCommandRejection.test.ts +++ b/tests/acceptance/rizinCommandRejection.test.ts @@ -113,7 +113,7 @@ cliTest.skipIf(process.platform === "win32")( // Both adapters share the service's classification; the CLI checks one // refusal and one command diagnostic. for (const outcome of [outcomes[0]!, outcomes[5]!]) { - const response = await cli.sharedCliRun!({ + const response = await cli.run({ arguments: [ "execute-rizin-command", artifact, diff --git a/tests/acceptance/rustBinaryCli.test.ts b/tests/acceptance/rustBinaryCli.test.ts index 0be0aa41fb..f9f22e94ca 100644 --- a/tests/acceptance/rustBinaryCli.test.ts +++ b/tests/acceptance/rustBinaryCli.test.ts @@ -16,7 +16,7 @@ import { rustDependencyPayload, } from "../fixtures/rust/image.js"; import { createTestTempDirectory } from "../fixtures/temporaryDirectory.js"; -import { cliTest, runFileCli } from "../support/cli/cliFixture.js"; +import { cliTest } from "../support/cli/cliFixture.js"; import { connectLocalToolsMcp } from "../fixtures/localToolsMcp.js"; import { expectCliMcpEvidenceParity } from "../fixtures/cliMcpEvidenceParity.js"; import { parseMcpToolError } from "../fixtures/mcpToolError.js"; @@ -187,7 +187,7 @@ cliTest( }), ); if (surface === "cli") { - const response = await runFileCli(cli, { + const response = await cli.run({ arguments: ["inspect-rust-binary", path, "--json"], }); expect(response.exitCode, response.stderr).toBe(0); @@ -212,7 +212,7 @@ cliTest( } if (surface === "cli") { - const response = await runFileCli(cli, { + const response = await cli.run({ arguments: ["inspect-rust-binary", malformedPath, "--json"], }); expect(response.exitCode).toBe(1); @@ -245,7 +245,7 @@ cliTest("returns null metadata when .dep-v0 is absent", async ({ cli }) => { const root = await createTestTempDirectory("rea-rust-cli-missing-"); const path = join(root, "plain"); await writeFile(path, createRustImage({ format: "elf", omitSection: true })); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-rust-binary", path, "--json"], }); expect(result.exitCode, result.stderr).toBe(0); @@ -278,7 +278,7 @@ cliTest( fixture.type, ), ); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-rust-binary", path, "--json"], }); expect(result.exitCode, result.stderr).toBe(0); @@ -324,7 +324,7 @@ cliTest( bytes.writeUInt32LE(0x0200000c, 4); await writeFile(path, bytes); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-rust-binary", path, "--json"], }); @@ -356,7 +356,7 @@ cliTest( ], }); await writeFile(path, createRustImage({ format: "elf", payload })); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-rust-binary", path, "--json"], }); expect(result.exitCode, result.stderr).toBe(0); @@ -385,7 +385,7 @@ cliTest("rejects malformed dependency JSON", async ({ cli }) => { includeRustc: false, }), ); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-rust-binary", path, "--json"], }); expect(result.exitCode).toBe(1); @@ -410,7 +410,7 @@ cliTest( }), ); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-rust-binary", path, "--json"], }); expect(result.exitCode).toBe(1); @@ -442,7 +442,7 @@ cliTest( bytes.writeUInt32LE(alignedSize, 264 + 16); // .dep-v0 SizeOfRawData await writeFile(path, bytes); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-rust-binary", path, "--json"], }); expect(result.exitCode, result.stderr).toBe(0); @@ -519,7 +519,7 @@ cliTest( reported_limits: { maximum_bytes: compressed.length * 64 }, }, }; - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-rust-binary", path, "--json"], }); expect(result.exitCode).toBe(1); @@ -625,7 +625,7 @@ cliTest( } const path = join(root, `mixed.${format}`); await writeFile(path, bytes); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-rust-binary", path, "--json"], }); expect(result.exitCode, result.stderr).toBe(0); @@ -656,7 +656,7 @@ cliTest( const response = await call("inspect_rust_binary", { path }); expect(response.isError, JSON.stringify(response)).not.toBe(true); expectCliMcpEvidenceParity(evidence, response.structuredContent, path); - const go = await runFileCli(cli, { + const go = await cli.run({ arguments: ["inspect-go-binary", path, "--json"], }); expect(go.exitCode).toBe(1); diff --git a/tests/acceptance/rustUnnamedSections.test.ts b/tests/acceptance/rustUnnamedSections.test.ts index b101d221f9..4809e3e279 100644 --- a/tests/acceptance/rustUnnamedSections.test.ts +++ b/tests/acceptance/rustUnnamedSections.test.ts @@ -26,7 +26,7 @@ cliTest( if (name === "writable") bytes.writeBigUInt64LE(3n, 384 + 8); const path = join(root, `${name}.elf`); await writeFile(path, bytes); - const output = await cli.sharedCliRun!({ + const output = await cli.run({ arguments: ["inspect-rust-binary", path, "--json"], }); expect(output.exitCode, output.stderr).toBe(0); diff --git a/tests/acceptance/sqliteDatabaseCli.test.ts b/tests/acceptance/sqliteDatabaseCli.test.ts index 25a7e6de34..0e7a0b1834 100644 --- a/tests/acceptance/sqliteDatabaseCli.test.ts +++ b/tests/acceptance/sqliteDatabaseCli.test.ts @@ -33,7 +33,7 @@ import { createTestTempDirectory } from "../fixtures/temporaryDirectory.js"; import { cliTest, createSharedMcpSession, - runFileCli, + type TestCli, } from "../support/cli/cliFixture.js"; const digest = (bytes: Buffer): string => @@ -49,11 +49,8 @@ const parityMcp = createSharedMcpSession(async (registerCleanup, isClosed) => { afterAll(() => parityMcp.close()); /** Run the offset page through the CLI so the option name is covered as well as the MCP input. */ -const pagedCliRows = async ( - cli: Parameters[0], - path: string, -): Promise => { - const result = await runFileCli(cli, { +const pagedCliRows = async (cli: TestCli, path: string): Promise => { + const result = await cli.run({ arguments: [ "inspect-sqlite-database", path, @@ -168,7 +165,7 @@ cliTest.runIf(SQLITE_NATIVE_LIMITS_AVAILABLE)( `${base}[0,1].value,${base}.1.value`, `${base}.1.value,${base}[0,1].value`, ]) { - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: [ "inspect-sqlite-database", path, @@ -187,7 +184,7 @@ cliTest.runIf(SQLITE_NATIVE_LIMITS_AVAILABLE)( }, }); } - const refused = await runFileCli(cli, { + const refused = await cli.run({ arguments: [ "inspect-sqlite-database", path, @@ -220,7 +217,7 @@ cliTest.runIf(SQLITE_NATIVE_LIMITS_AVAILABLE)( } finally { database.close(); } - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: [ "inspect-sqlite-database", path, @@ -506,7 +503,7 @@ cliTest.runIf(SQLITE_NATIVE_LIMITS_AVAILABLE)( }, }, }); - const cliResult = await runFileCli(cli, { + const cliResult = await cli.run({ arguments: [ "inspect-sqlite-database", path, @@ -672,7 +669,7 @@ cliTest.runIf(SQLITE_NATIVE_LIMITS_AVAILABLE)( } const { call } = await connectLocalToolsMcp(); for (const limit of [1001, Number.MAX_SAFE_INTEGER]) { - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: [ "inspect-sqlite-database", path, @@ -896,7 +893,7 @@ cliTest.runIf(SQLITE_NATIVE_LIMITS_AVAILABLE)( const wal = before[1]; expect(db).toBeDefined(); expect(wal).toBeDefined(); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: [ "inspect-sqlite-database", path, @@ -962,7 +959,7 @@ cliTest.runIf(SQLITE_NATIVE_LIMITS_AVAILABLE)( ], }, }; - const corrupt = await runFileCli(cli, { + const corrupt = await cli.run({ arguments: ["inspect-sqlite-database", selected, "--json"], environment: { REA_LOG_LEVEL: "silent" }, }); @@ -998,7 +995,7 @@ cliTest.runIf(SQLITE_NATIVE_LIMITS_AVAILABLE)( }, }, }); - const ambiguous = await runFileCli(cli, { + const ambiguous = await cli.run({ arguments: [ "inspect-sqlite-database", valid, @@ -1018,7 +1015,7 @@ cliTest.runIf(SQLITE_NATIVE_LIMITS_AVAILABLE)( }) ).isError, ).toBe(true); - const missing = await runFileCli(cli, { + const missing = await cli.run({ arguments: [ "inspect-sqlite-database", valid, @@ -1068,7 +1065,7 @@ cliTest.runIf(SQLITE_NATIVE_LIMITS_AVAILABLE)( producer.close(); } const before = await sourceState(path); - const control = await runFileCli(cli, { + const control = await cli.run({ arguments: [ "inspect-sqlite-database", path, @@ -1098,7 +1095,7 @@ cliTest.runIf(SQLITE_NATIVE_LIMITS_AVAILABLE)( const columns = Array.from({ length: selectedColumns }, () => "value"); // The framed fixture executes the compiled public CLI without the host's // process-argument ceiling rejecting this many repeated flags first. - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: [ "inspect-sqlite-database", path, @@ -1155,7 +1152,7 @@ cliTest.runIf(SQLITE_NATIVE_LIMITS_AVAILABLE)( const path = createSqliteApplicationCollationFixture(root); const before = await sourceState(path); const { call } = await connectLocalToolsMcp(); - const schema = await runFileCli(cli, { + const schema = await cli.run({ arguments: [ "inspect-sqlite-database", path, @@ -1194,7 +1191,7 @@ cliTest.runIf(SQLITE_NATIVE_LIMITS_AVAILABLE)( expect(schemaResponse.isError).not.toBe(true); expect(schemaResponse.structuredContent).toMatchObject(expectedSchema); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: [ "inspect-sqlite-database", path, @@ -1269,7 +1266,7 @@ cliTest.runIf(SQLITE_NATIVE_LIMITS_AVAILABLE)( }, }; for (const columns of [["b", "a"], undefined]) { - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: [ "inspect-sqlite-database", path, @@ -1290,7 +1287,7 @@ cliTest.runIf(SQLITE_NATIVE_LIMITS_AVAILABLE)( }); expect(parseMcpToolError(response).error).toMatchObject(expectedError); } - const base = await runFileCli(cli, { + const base = await cli.run({ arguments: [ "inspect-sqlite-database", path, @@ -1347,7 +1344,7 @@ cliTest.runIf(SQLITE_NATIVE_LIMITS_AVAILABLE)( "selected_table", "database", ] as const) { - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: [ "inspect-sqlite-database", path, @@ -1400,7 +1397,7 @@ cliTest.runIf(SQLITE_NATIVE_LIMITS_AVAILABLE)( ["UTF-16be", "database"], ] as const) { const { path, names } = createSqliteLongNameFixture(root, encoding); - const response = await runFileCli(cli, { + const response = await cli.run({ arguments: [ "inspect-sqlite-database", path, @@ -1492,7 +1489,7 @@ cliTest.runIf(SQLITE_NATIVE_LIMITS_AVAILABLE)( native.close(); } - const cliResult = await runFileCli(cli, { + const cliResult = await cli.run({ arguments: [ "inspect-sqlite-database", path, @@ -1571,7 +1568,7 @@ cliTest.runIf(SQLITE_NATIVE_LIMITS_AVAILABLE)( producer.close(); } const original = await readFile(path); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-sqlite-database", path, "--json"], environment: { REA_LOG_LEVEL: "silent" }, }); diff --git a/tests/acceptance/tools/harBoundaryFacts.test.ts b/tests/acceptance/tools/harBoundaryFacts.test.ts index 398c04347f..2bb420c45c 100644 --- a/tests/acceptance/tools/harBoundaryFacts.test.ts +++ b/tests/acceptance/tools/harBoundaryFacts.test.ts @@ -9,7 +9,7 @@ import { historicalHar } from "../../fixtures/historicalHar.js"; import { connectLocalToolsMcp } from "../../fixtures/localToolsMcp.js"; import { parseMcpToolError } from "../../fixtures/mcpToolError.js"; import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js"; -import { cliTest, runFileCli } from "../../support/cli/cliFixture.js"; +import { cliTest } from "../../support/cli/cliFixture.js"; cliTest( "omits HAR extension payloads from capture previews while retaining complete records", @@ -166,10 +166,10 @@ cliTest( "--json", ], }; - const output = - label === "null-timings" - ? await cli.run(invocation) - : await runFileCli(cli, invocation); + const output = await cli.run({ + ...invocation, + freshProcess: label === "null-timings", + }); expect(output.exitCode).toBe(1); const expected = { code: "invalid_request", diff --git a/tests/acceptance/tools/mobileGraphExamples.test.ts b/tests/acceptance/tools/mobileGraphExamples.test.ts index c30b13c3f7..7eeb4eff33 100644 --- a/tests/acceptance/tools/mobileGraphExamples.test.ts +++ b/tests/acceptance/tools/mobileGraphExamples.test.ts @@ -12,7 +12,7 @@ import { import { createEvidence, parseEvidence } from "../../../src/domain/evidence.js"; import { artifactInventoryResultSchema } from "../../../src/domain/artifactGraph.js"; import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js"; -import { cliTest, runFileCli } from "../../support/cli/cliFixture.js"; +import { cliTest } from "../../support/cli/cliFixture.js"; const graphExamples = [ { @@ -112,10 +112,10 @@ cliTest.for(graphExamples)( const invocation = { arguments: [command, JSON.stringify(input), "--json"], }; - const result = - code === "evidence_integrity_mismatch" - ? await cli.run(invocation) - : await runFileCli(cli, invocation); + const result = await cli.run({ + ...invocation, + freshProcess: code === "evidence_integrity_mismatch", + }); expect(result.exitCode).toBe(1); expect(result.json).toMatchObject({ code, diff --git a/tests/acceptance/tools/nativeFunctionMatching.test.ts b/tests/acceptance/tools/nativeFunctionMatching.test.ts index bdbdb61caf..d13cda3ee6 100644 --- a/tests/acceptance/tools/nativeFunctionMatching.test.ts +++ b/tests/acceptance/tools/nativeFunctionMatching.test.ts @@ -3,7 +3,7 @@ import { join } from "node:path"; import { expect } from "vitest"; import { cliTest, - runFileCli, + type CliInvocation, type TestCli, } from "../../support/cli/cliFixture.js"; import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js"; @@ -173,7 +173,7 @@ const verifyPortableCli = async ( ); const original = await readFile(bundlePath); await writeFile(inputPath, JSON.stringify({ ...input, detail: "complete" })); - const cliFull = await runFileCli(cli, { + const cliFull = await cli.run({ arguments: [ "match-native-functions", inputPath, @@ -187,7 +187,7 @@ const verifyPortableCli = async ( expect(cliFull.exitCode, cliFull.stderr).toBe(0); expect(parseEvidence(cliFull.json).evidence_id).toBe(evidenceId); await writeFile(inputPath, JSON.stringify(input)); - const cliSummary = await runFileCli(cli, { + const cliSummary = await cli.run({ arguments: [ "match-native-functions", inputPath, @@ -222,7 +222,7 @@ const verifyPortableCli = async ( expect(ids.has(unrelated.evidence_id)).toBe(false); expect(ids.has(evidenceId)).toBe(true); expect(Buffer.compare(await readFile(bundlePath), original)).toBe(0); - const incomplete = await runFileCli(cli, { + const incomplete = await cli.run({ arguments: [ "match-native-functions", inputPath, @@ -508,8 +508,8 @@ cliTest( "right_observations", ] as const) { // Keep fresh-process coverage for both source-admission routes on left. - const run = - field === "left" ? cli.run.bind(cli) : runFileCli.bind(undefined, cli); + const run = (invocation: CliInvocation) => + cli.run({ ...invocation, freshProcess: field === "left" }); const observation = field.endsWith("observations"); const request = { left, @@ -581,7 +581,7 @@ cliTest( detail: "complete", }; await writeFile(requestPath, JSON.stringify(inlineRequest)); - const inline = await runFileCli(cli, { + const inline = await cli.run({ arguments: ["match-native-functions", requestPath, "--json"], }); await writeFile( @@ -589,7 +589,7 @@ cliTest( JSON.stringify(createEvidenceBundle([wrongRole, right])), ); await writeFile(requestPath, JSON.stringify(portableRequest)); - const portable = await runFileCli(cli, { + const portable = await cli.run({ arguments: [ "match-native-functions", requestPath, @@ -611,7 +611,7 @@ cliTest( const corruptRequest = { left: badCoordinate, right, detail: "complete" }; await writeFile(requestPath, JSON.stringify(corruptRequest)); - const corruptInline = await runFileCli(cli, { + const corruptInline = await cli.run({ arguments: ["match-native-functions", requestPath, "--json"], }); const corruptPortableRequest = { @@ -623,7 +623,7 @@ cliTest( detail: "complete", }; await writeFile(requestPath, JSON.stringify(corruptPortableRequest)); - const corruptPortable = await runFileCli(cli, { + const corruptPortable = await cli.run({ arguments: [ "match-native-functions", requestPath, @@ -641,7 +641,7 @@ cliTest( const malformedBundlePath = join(root, "malformed-outer-frame.json"); await writeFile(malformedBundlePath, JSON.stringify({ records: [left] })); - const malformedBundle = await runFileCli(cli, { + const malformedBundle = await cli.run({ arguments: [ "match-native-functions", requestPath, diff --git a/tests/acceptance/tools/passiveCaptureCorrespondence.test.ts b/tests/acceptance/tools/passiveCaptureCorrespondence.test.ts index 6a2bb0705f..e2990fdd4b 100644 --- a/tests/acceptance/tools/passiveCaptureCorrespondence.test.ts +++ b/tests/acceptance/tools/passiveCaptureCorrespondence.test.ts @@ -17,7 +17,7 @@ import { withPassiveDroppedEvents, } from "../../fixtures/passiveNetworkCapture.js"; import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js"; -import { cliTest, runFileCli } from "../../support/cli/cliFixture.js"; +import { cliTest } from "../../support/cli/cliFixture.js"; type Capture = { inspection: CompareWebCapturesInput["before"]["inspection"]; @@ -516,9 +516,10 @@ cliTest.for(cases.filter(({ name }) => cliCaseNames.has(name)))( arguments: ["compare-web-captures", ...paths, "--json"], environment: { REA_LOG_LEVEL: "silent" }, }; - const response = await (scenario.name === "heterogeneous HTTP omission 0" - ? cli.run(invocation) - : runFileCli(cli, invocation)); + const response = await cli.run({ + ...invocation, + freshProcess: scenario.name === "heterogeneous HTTP omission 0", + }); expect(response.exitCode, response.stderr).toBe(0); expectComparison(response.json, scenario); }, diff --git a/tests/acceptance/tools/passiveNetworkInventory.test.ts b/tests/acceptance/tools/passiveNetworkInventory.test.ts index 22679d8a5f..4a68881335 100644 --- a/tests/acceptance/tools/passiveNetworkInventory.test.ts +++ b/tests/acceptance/tools/passiveNetworkInventory.test.ts @@ -10,7 +10,7 @@ import { withPassiveDroppedEvents, } from "../../fixtures/passiveNetworkCapture.js"; import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js"; -import { cliTest, runFileCli } from "../../support/cli/cliFixture.js"; +import { cliTest } from "../../support/cli/cliFixture.js"; const comparisonCases = () => { const before = passiveNetworkCapture(); @@ -191,9 +191,10 @@ cliTest.for(cases)( arguments: ["compare-web-captures", ...paths, "--json"], environment: { REA_LOG_LEVEL: "silent" }, }; - const response = await (name === "added" || name === "removed" - ? cli.run(options) - : runFileCli(cli, options)); + const response = await cli.run({ + ...options, + freshProcess: name === "added" || name === "removed", + }); expect(response.exitCode, response.stderr).toBe(0); expectComparison(response.json, name, change); expect( diff --git a/tests/acceptance/tools/protobufCapture.test.ts b/tests/acceptance/tools/protobufCapture.test.ts index 62de66e78e..c161b36352 100644 --- a/tests/acceptance/tools/protobufCapture.test.ts +++ b/tests/acceptance/tools/protobufCapture.test.ts @@ -9,7 +9,7 @@ import { } from "node:fs/promises"; import { join } from "node:path"; import { expect } from "vitest"; -import { cliTest, runFileCli } from "../../support/cli/cliFixture.js"; +import { cliTest } from "../../support/cli/cliFixture.js"; import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js"; import { protobufCaptureDescriptor, @@ -649,9 +649,10 @@ captureTest.for( "--json", ], }; - const response = await (name === "valid" - ? cli.run(options) - : runFileCli(cli, options)); + const response = await cli.run({ + ...options, + freshProcess: name === "valid", + }); expect(response.exitCode, response.stdout + response.stderr).toBe(0); const evidence = parseEvidence(response.json); const report = webNetworkCaptureSchema.parse(evidence.normalized_result); diff --git a/tests/acceptance/tools/protobufDescriptorFiles.test.ts b/tests/acceptance/tools/protobufDescriptorFiles.test.ts index 6b964a085a..f37f1fead7 100644 --- a/tests/acceptance/tools/protobufDescriptorFiles.test.ts +++ b/tests/acceptance/tools/protobufDescriptorFiles.test.ts @@ -147,7 +147,7 @@ cliTest( }, }; if (change === "declarations") { - const output = await cli.sharedCliRun!({ + const output = await cli.run({ arguments: [ "inspect-web-network-capture", capture, @@ -254,7 +254,7 @@ cliTest( const bytes = writer.finish(); const descriptor = join(root, `${mode}.pb`); await writeFile(descriptor, bytes); - const output = await cli.sharedCliRun!({ + const output = await cli.run({ arguments: [ "inspect-web-network-capture", capture, diff --git a/tests/acceptance/tools/protobufDescriptorIdentity.test.ts b/tests/acceptance/tools/protobufDescriptorIdentity.test.ts index 24ce91033a..d57476ad7e 100644 --- a/tests/acceptance/tools/protobufDescriptorIdentity.test.ts +++ b/tests/acceptance/tools/protobufDescriptorIdentity.test.ts @@ -132,7 +132,7 @@ cliTest.for([ const descriptor = join(root, `${variant}.pb`); const bytes = toBinary(FileDescriptorSetSchema, definition); await writeFile(descriptor, bytes); - const output = await cli.sharedCliRun!({ + const output = await cli.run({ arguments: [ "inspect-web-network-capture", capture, @@ -222,7 +222,7 @@ cliTest( const definition = toBinary(FileDescriptorSetSchema, valid); await writeFile(capture, protobufHar(body, "application/x-protobuf")); await writeFile(descriptor, definition); - const output = await cli.sharedCliRun!({ + const output = await cli.run({ arguments: [ "inspect-web-network-capture", capture, diff --git a/tests/acceptance/tools/protobufMimeAdmission.test.ts b/tests/acceptance/tools/protobufMimeAdmission.test.ts index c9d2066314..46b1cae62b 100644 --- a/tests/acceptance/tools/protobufMimeAdmission.test.ts +++ b/tests/acceptance/tools/protobufMimeAdmission.test.ts @@ -30,7 +30,7 @@ cliTest( const path = join(root, `${index}.har`); const original = protobufHar(body, mime); await writeFile(path, original); - const output = await cli.sharedCliRun!({ + const output = await cli.run({ arguments: [ "inspect-web-network-capture", path, @@ -109,7 +109,7 @@ cliTest( await writeFile(path, original); const call = await cli.sharedMcpCall!(); for (const framing of ["auto", "raw"] as const) { - const output = await cli.sharedCliRun!({ + const output = await cli.run({ arguments: [ "inspect-web-network-capture", path, @@ -233,7 +233,7 @@ cliTest( const mime = 'application/protobuf;encoding="binary'; const original = protobufHar(body, mime); await writeFile(path, original); - const output = await cli.sharedCliRun!({ + const output = await cli.run({ arguments: [ "inspect-web-network-capture", path, diff --git a/tests/acceptance/tools/protobufRegistryIdentity.test.ts b/tests/acceptance/tools/protobufRegistryIdentity.test.ts index f6cc0bb65b..6cb481def0 100644 --- a/tests/acceptance/tools/protobufRegistryIdentity.test.ts +++ b/tests/acceptance/tools/protobufRegistryIdentity.test.ts @@ -117,7 +117,7 @@ cliTest( // Both orders cross the actual CLI adapter; other geometries reuse // the production MCP session and the same application workflow. if (scope === "same-file") { - const output = await cli.sharedCliRun!({ + const output = await cli.run({ arguments: [ "inspect-web-network-capture", capture, diff --git a/tests/acceptance/tools/unavailableNativeCollectionViews.test.ts b/tests/acceptance/tools/unavailableNativeCollectionViews.test.ts index fc1a27e2b5..245048f02e 100644 --- a/tests/acceptance/tools/unavailableNativeCollectionViews.test.ts +++ b/tests/acceptance/tools/unavailableNativeCollectionViews.test.ts @@ -124,7 +124,7 @@ cliTest( }, ]; const call = await cli.sharedMcpCall!(); - const run = cli.sharedCliRun!.bind(cli); + const run = cli.run.bind(cli); for (const test of cases) { const request = { source: { kind: "inline", evidence: test.evidence }, diff --git a/tests/boundary/cli/analysisSelectorCli.test.ts b/tests/boundary/cli/analysisSelectorCli.test.ts index df18fff028..e9d919c123 100644 --- a/tests/boundary/cli/analysisSelectorCli.test.ts +++ b/tests/boundary/cli/analysisSelectorCli.test.ts @@ -1,7 +1,7 @@ import { describe, expect } from "vitest"; import { nativeDataTypeInputSchema } from "../../../src/domain/native/nativeDataType.js"; -import { cliTest, runFileCli } from "../../support/cli/cliFixture.js"; +import { cliTest } from "../../support/cli/cliFixture.js"; const selectors = [ ["function", "procedure", "analyze_function"], @@ -101,7 +101,7 @@ describe("CLI analysis selector boundaries", () => { ["selector", `--${option}=selector`], ]) { const label = `${command} ${selection.join(" ")}`; - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: [ command, "does-not-exist", @@ -174,7 +174,7 @@ cliTest( [["--address", "0x1000"], { code: "target_unavailable" }], ] as const) { const label = `inspect-native-data-type ${selection.join(" ")}`; - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: [ "inspect-native-data-type", "does-not-exist", diff --git a/tests/boundary/cli/analysisViewCli.test.ts b/tests/boundary/cli/analysisViewCli.test.ts index 9a32ee6555..60dc7cc75e 100644 --- a/tests/boundary/cli/analysisViewCli.test.ts +++ b/tests/boundary/cli/analysisViewCli.test.ts @@ -16,7 +16,7 @@ import { expect } from "vitest"; import { TextReader, Uint8ArrayWriter, ZipWriter } from "@zip.js/zip.js"; import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js"; -import { cliTest, runFileCli } from "../../support/cli/cliFixture.js"; +import { cliTest } from "../../support/cli/cliFixture.js"; import { analysisViewLayoutEvidence, @@ -119,7 +119,7 @@ cliTest( ); await writeFile(path, JSON.stringify(createEvidenceBundle([parent]))); const inspect = async (view: JsonValue) => { - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: [ "inspect-analysis-view", JSON.stringify({ @@ -177,7 +177,7 @@ cliTest( ]); const original = JSON.stringify(bundle); await writeFile(path, original); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: [ "inspect-analysis-view", JSON.stringify(request), @@ -216,10 +216,10 @@ cliTest( "--json", ], }; - const rejected = - source.kind === "inline" - ? await cli.run(invocation) - : await runFileCli(cli, invocation); + const rejected = await cli.run({ + ...invocation, + freshProcess: source.kind === "inline", + }); expect(rejected.exitCode).toBe(1); expect(rejected.json).toMatchObject({ code: expectedCode }); expect(rejected.json).not.toHaveProperty("evidence_id"); @@ -251,9 +251,10 @@ cliTest( }; // Keep a fresh-process integrity check for the altered bundle while the // worker must reread the same path for the other invalid inputs. - const rejected = freshProcess - ? await cli.run(invocation) - : await runFileCli(cli, invocation); + const rejected = await cli.run({ + ...invocation, + freshProcess: freshProcess, + }); expect(rejected.exitCode).toBe(1); expect(rejected.json).toMatchObject({ code: expectedCode }); expect(rejected.json).not.toHaveProperty("evidence_id"); @@ -280,7 +281,7 @@ cliTest( view: { kind: "native", facet: "callees", offset: 0, limit: 1 }, }), ); - const response = await runFileCli(cli, { + const response = await cli.run({ arguments: ["inspect-analysis-view", request, "--json"], }); expect(response.exitCode).toBe(0); @@ -319,7 +320,7 @@ cliTest( const expected = await inspectAnalysisView(input); if (!expected.ok) throw expected.error; await writeFile(request, JSON.stringify(input)); - const response = await runFileCli(cli, { + const response = await cli.run({ arguments: ["inspect-analysis-view", request, "--json"], }); expect(response.exitCode).toBe(0); @@ -360,7 +361,7 @@ cliTest( view: { kind: "api-surface", include_query_parameter_names: true }, }), ); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-analysis-view", path, "--json"], }); expect(result.exitCode).toBe(0); @@ -390,7 +391,7 @@ cliTest( await writer.add("main.js", new TextReader("console.log('portable');\n")); await writeFile(archive, await writer.close()); const bundlePath = join(root, "inspection-bundle.json"); - const inspected = await runFileCli(cli, { + const inspected = await cli.run({ arguments: [ "inspect-artifact", archive, @@ -440,7 +441,7 @@ cliTest( view: { kind: "page", collection: "sections", offset: 0, limit: 20 }, }; await writeFile(unsupportedRequest, JSON.stringify(unsupportedInput)); - const unsupported = await runFileCli(cli, { + const unsupported = await cli.run({ arguments: ["inspect-analysis-view", unsupportedRequest, "--json"], }); expect(unsupported.exitCode).toBe(1); @@ -468,7 +469,7 @@ cliTest( }, }), ); - const projected = await runFileCli(cli, { + const projected = await cli.run({ arguments: ["inspect-analysis-view", request, "--json"], }); expect(projected.exitCode).toBe(0); @@ -497,7 +498,7 @@ cliTest( const writer = new ZipWriter(new Uint8ArrayWriter()); await writer.add("main.js", new TextReader("console.log(1);\n")); await writeFile(archive, await writer.close()); - const inspected = await runFileCli(cli, { + const inspected = await cli.run({ arguments: ["inspect-artifact", archive, "--json"], }); expect(inspected.exitCode).toBe(0); @@ -541,10 +542,10 @@ cliTest( }; // Exercise a valid and a malformed parent in fresh processes as well // as the shared worker's repeated reads of the rewritten input path. - const reply = - group === "manifest" && variant !== "missing" - ? await cli.run(invocation) - : await runFileCli(cli, invocation); + const reply = await cli.run({ + ...invocation, + freshProcess: group === "manifest" && variant !== "missing", + }); expect(reply.stderr).toBe(""); if (variant === "valid") { expect(application.ok).toBe(true); @@ -599,7 +600,7 @@ cliTest( ], }); await writeFile(path, JSON.stringify(request)); - const refused = await runFileCli(cli, { + const refused = await cli.run({ arguments: ["inspect-analysis-view", path, "--json"], }); expect(refused.exitCode).toBe(1); @@ -648,7 +649,7 @@ cliTest( view: { kind: "api-surface" }, }), ); - const refused = await runFileCli(cli, { + const refused = await cli.run({ arguments: ["inspect-analysis-view", path, "--json"], environment, }); @@ -679,7 +680,7 @@ cliTest( view: { kind: "api-surface" }, }), ); - const response = await runFileCli(cli, { + const response = await cli.run({ arguments: ["inspect-analysis-view", path, "--json"], environment, }); @@ -716,7 +717,7 @@ cliTest( detail: "summary" as const, filters: { evidence_id: parent.evidence_id }, }; - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: [ "inspect-evidence-bundle", JSON.stringify({ path, ...options }), @@ -729,7 +730,7 @@ cliTest( if (!expected.ok) throw expected.error; expect(result.json).toEqual(expected.value); // The same shared owner selects the complete record from the portable file. - const records = await runFileCli(cli, { + const records = await cli.run({ arguments: [ "inspect-evidence-bundle", JSON.stringify({ path, ...options, detail: "records", limit: 1 }), @@ -746,7 +747,7 @@ cliTest( records: [parent], missing_evidence_links: [], }); - const complete = await runFileCli(cli, { + const complete = await cli.run({ arguments: [ "inspect-evidence-bundle", JSON.stringify({ path }), @@ -781,7 +782,7 @@ cliTest( detail: "links", traversal: "retained-closure", } as const; - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: [ "inspect-evidence-bundle", JSON.stringify({ path, ...options }), @@ -850,7 +851,7 @@ cliTest( }; { - const json = await runFileCli(cli, { + const json = await cli.run({ arguments: [ "inspect-analysis-view", input, @@ -869,7 +870,7 @@ cliTest( ).toHaveLength(rowCount); } - const markdown = await runFileCli(cli, { + const markdown = await cli.run({ arguments: [ "inspect-analysis-view", input, @@ -908,7 +909,7 @@ cliTest( const input = join(root, "view.json"); await mkdir(root, { recursive: true }); await writeFile(input, JSON.stringify(request)); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-analysis-view", input, "--json"], environment: { HOME: root, @@ -934,7 +935,7 @@ cliTest( const root = await createTestTempDirectory("rea-empty-layout-view-cli-"); const empty = analysisViewLayoutEvidence(binaryLayoutFixture()); for (const collection of ["sections", "symbols"] as const) { - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: [ "inspect-analysis-view", JSON.stringify({ @@ -965,7 +966,7 @@ cliTest( expect(result.json).not.toHaveProperty("details.issues.0.minimum"); expect(result.json).not.toHaveProperty("details.issues.0.maximum"); } - const populated = await runFileCli(cli, { + const populated = await cli.run({ arguments: [ "inspect-analysis-view", JSON.stringify({ @@ -1022,7 +1023,7 @@ cliTest( view: { kind: "native", facet: "pseudocode", offset: 0, limit: 12 }, }), ); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-analysis-view", input, "--json"], environment: { HOME: root, XDG_CONFIG_HOME: root, XDG_CACHE_HOME: root }, }); @@ -1065,7 +1066,7 @@ cliTest( { kind: "summary" }, { kind: "evm", facet: "bytecode_hex", offset: 0, limit: 4 }, ]) { - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: [ "inspect-analysis-view", JSON.stringify({ @@ -1087,7 +1088,7 @@ cliTest( cliTest("rejects malformed JSON before projection", async ({ cli }) => { const root = await createTestTempDirectory("rea-analysis-view-cli-bad-"); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-analysis-view", "{", "--json"], environment: { HOME: root, @@ -1107,7 +1108,7 @@ cliTest("rejects a request that omits source", async ({ cli }) => { const root = await createTestTempDirectory("rea-analysis-view-cli-missing-"); const input = join(root, "missing-source.json"); await writeFile(input, JSON.stringify({ view: { kind: "summary" } })); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-analysis-view", input, "--json"], environment: { HOME: root, @@ -1142,7 +1143,7 @@ for (const [view, reason] of [ view, }), ); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-analysis-view", input, "--json"], environment: { HOME: root, @@ -1195,7 +1196,7 @@ cliTest( XDG_CACHE_HOME: root, }; await writeFile(capturePath, bytes); - const capture = await runFileCli(cli, { + const capture = await cli.run({ arguments: ["inspect-web-network-capture", capturePath, "har", "--json"], environment, }); @@ -1212,7 +1213,7 @@ cliTest( view: { kind: "api-surface" }, }), ); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-analysis-view", input, "--json"], environment, }); @@ -1266,7 +1267,7 @@ cliTest( view, }), ); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-analysis-view", requestPath, "--json"], }); expect(result.exitCode, result.stdout).toBe(0); @@ -1312,7 +1313,7 @@ cliTest( }, }), ); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-analysis-view", path, "--json"], }); expect(result.exitCode, result.stdout).toBe(0); @@ -1348,7 +1349,7 @@ cliTest( view: { kind: "environment-manifest" }, }), ); - const refused = await runFileCli(cli, { + const refused = await cli.run({ arguments: ["inspect-analysis-view", path, "--json"], }); expect(refused.exitCode).toBe(1); @@ -1383,7 +1384,7 @@ cliTest( }, }), ); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-analysis-view", path, "--json"], }); expect(result.exitCode, result.stdout).toBe(0); @@ -1457,7 +1458,7 @@ cliTest.for([ view: { kind: "environment-state" }, }); await writeFile(path, request); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-analysis-view", path, "--json"], }); expect(result.exitCode, result.stdout).toBe(0); @@ -1485,7 +1486,7 @@ cliTest( const root = await createTestTempDirectory("rea-manifest-chain-cli-"); const capturePath = join(root, "capture.har"); await writeFile(capturePath, JSON.stringify(environmentManifestHar())); - const capture = await runFileCli(cli, { + const capture = await cli.run({ arguments: ["inspect-web-network-capture", capturePath, "har", "--json"], }); expect(capture.exitCode, capture.stdout).toBe(0); @@ -1498,7 +1499,7 @@ cliTest( view: { kind: "environment-manifest" }, }), ); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-analysis-view", input, "--json"], }); expect(result.exitCode, result.stdout).toBe(0); diff --git a/tests/boundary/cli/cliIntegerArguments.test.ts b/tests/boundary/cli/cliIntegerArguments.test.ts index 77fc6a710a..7fd51fe70e 100644 --- a/tests/boundary/cli/cliIntegerArguments.test.ts +++ b/tests/boundary/cli/cliIntegerArguments.test.ts @@ -4,11 +4,7 @@ import { join } from "node:path"; import { describe, expect } from "vitest"; import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js"; -import { - cliTest, - runFileCli, - type CliResult, -} from "../../support/cli/cliFixture.js"; +import { cliTest, type CliResult } from "../../support/cli/cliFixture.js"; const SESSION_ID = "27d3e3f1-f1e5-49ae-91ec-95af1f343a5a"; const NON_INTEGER_TEXT = [ @@ -132,7 +128,7 @@ describe("CLI integer arguments", () => { cliTest.for(NON_INTEGER_OPTION_TEXT)( "rejects %j as an Android reference overload index", async (value, { cli }) => { - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: [ "trace-android-references", "/nonexistent/missing.apk", @@ -149,7 +145,7 @@ describe("CLI integer arguments", () => { cliTest.for(NON_INTEGER_TEXT)( "rejects %j as a Cutter expected generation", async (value, { cli }) => { - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["cutter-command", SESSION_ID, value, "pdi", "--json"], }); @@ -160,7 +156,7 @@ describe("CLI integer arguments", () => { cliTest.for(NON_INTEGER_TEXT)( "rejects %j as a web module script index", async (value, { cli }) => { - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: [ "trace-web-module-imports", "/nonexistent/manifest.json", @@ -176,7 +172,7 @@ describe("CLI integer arguments", () => { cliTest.for(NON_INTEGER_OPTION_TEXT)( "rejects %j in a repeated web capture record selector", async (value, { cli }) => { - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: [ "inspect-web-network-capture", "/nonexistent/capture.qlog", @@ -206,7 +202,7 @@ describe("byte-entropy window options", () => { cliTest.for(values)( `rejects %j as a byte-entropy ${flag} value`, async (value, { cli }) => { - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: [ "inspect-byte-entropy", await entropySample(), @@ -225,7 +221,7 @@ describe("byte-entropy window options", () => { async ({ cli }) => { const path = await entropySample(); const window = async (...flags: string[]): Promise => { - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["inspect-byte-entropy", path, ...flags, "--json"], }); expect(result.exitCode, result.stdout).toBe(0); @@ -271,7 +267,7 @@ describe("pyc and BEAM limit options", () => { cliTest.for(values)( `rejects %j as ${command} ${flag}`, async (value, { cli }) => { - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: [ command, "/nonexistent/missing-artifact", @@ -289,7 +285,7 @@ describe("pyc and BEAM limit options", () => { ["inspect-python-pyc", "--max-depth=016"], ["inspect-erlang-beam", "--max-chunks=016"], ] as const)("keeps a decimal %s limit", async ([command, flag], { cli }) => { - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: [command, "/nonexistent/missing-artifact", flag, "--json"], }); @@ -322,7 +318,7 @@ describe("artifact decimal options", () => { cliTest.for(values)( `rejects %j as ${command} ${flag}`, async (value, { cli }) => { - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: [ command, "/nonexistent/missing-artifact", @@ -337,7 +333,7 @@ describe("artifact decimal options", () => { } cliTest("keeps decimal keyed-archive offset and limit", async ({ cli }) => { - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: [ "inspect-keyed-archive", "/nonexistent/missing-archive", diff --git a/tests/boundary/cli/cliLogLevelConfiguration.test.ts b/tests/boundary/cli/cliLogLevelConfiguration.test.ts index 9aba657f40..11cfd91609 100644 --- a/tests/boundary/cli/cliLogLevelConfiguration.test.ts +++ b/tests/boundary/cli/cliLogLevelConfiguration.test.ts @@ -1,6 +1,6 @@ import { expect } from "vitest"; -import { cliTest, runFileCli } from "../../support/cli/cliFixture.js"; +import { cliTest } from "../../support/cli/cliFixture.js"; const invalidLogLevelMessage = 'REA configuration is invalid: REA_LOG_LEVEL: Invalid option: expected one of "trace"|"debug"|"info"|"warn"|"error"|"fatal"|"silent".\n'; @@ -23,7 +23,7 @@ for (const level of ["loud", "INFO"]) }; for (const doctor of [ await cli.run(doctorInvocation), - await runFileCli(cli, doctorInvocation), + await cli.run(doctorInvocation), ]) { expect(doctor.exitCode, doctor.stderr).toBe(1); expect(doctor.stderr).toBe(""); @@ -82,8 +82,9 @@ cliTest( arguments: arguments_, environment: { REA_LOG_LEVEL: "loud" }, }; - const fresh = await cli.run(invocation); - const shared = await runFileCli(cli, invocation); + // Compare the launcher itself with the file's shared process. + const fresh = await cli.run({ ...invocation, freshProcess: true }); + const shared = await cli.run(invocation); for (const result of [fresh, shared]) { expect(result.exitCode, JSON.stringify(arguments_)).toBe(1); expect(result.stdout, JSON.stringify(arguments_)).toBe(""); @@ -97,8 +98,8 @@ cliTest( arguments: ["doctor"], environment: { REA_LOG_LEVEL: "loud", COMPLETE: "bash" }, }; - const fresh = await cli.run(completion); - const shared = await runFileCli(cli, completion); + const fresh = await cli.run({ ...completion, freshProcess: true }); + const shared = await cli.run(completion); for (const result of [fresh, shared]) { expect(result.exitCode).toBe(1); expect(result.stdout).toBe(""); diff --git a/tests/boundary/cli/cliSessionParity.test.ts b/tests/boundary/cli/cliSessionParity.test.ts index 71fcf16625..de0d2926fe 100644 --- a/tests/boundary/cli/cliSessionParity.test.ts +++ b/tests/boundary/cli/cliSessionParity.test.ts @@ -1,10 +1,13 @@ import { expect } from "vitest"; -import { cliTest, runFileCli } from "../../support/cli/cliFixture.js"; +import { + cliProcessEnvironment, + cliTest, + definedCliEnvironment, +} from "../../support/cli/cliFixture.js"; -// The file session and the executable run one shared one-shot owner. These -// commands are answered before the command runtime loads, which is where a -// separate session implementation previously diverged. +// The file session and the executable run one shared one-shot owner. Compare +// a fresh process with the session itself: `cli.run` may route either way. cliTest.for([ { name: "a formatted version report", arguments: ["--version", "--json"] }, { @@ -15,16 +18,27 @@ cliTest.for([ name: "a rejected output combination", arguments: ["capabilities", "--json", "--token-limit", "5"], }, + { + // The session's own stdin carries its frames; a command that reads + // stdin must see the same empty input a fresh process does. + name: "a command reading absent stdin", + arguments: ["compare-wasm-interfaces", "-", "--json"], + }, ])( "the file session reports $name exactly as a fresh process does", - async ({ arguments: arguments_ }, { cli }) => { + async ({ arguments: arguments_ }, { cli, cliCommandSession }) => { const environment = { REA_LOG_LEVEL: "silent" }; - const fresh = await cli.run({ arguments: arguments_, environment }); - const shared = await runFileCli(cli, { + const fresh = await cli.run({ arguments: arguments_, environment, + freshProcess: true, }); - expect(fresh.stdout).not.toBe(""); + const shared = await cliCommandSession.run({ + arguments: arguments_, + cwd: process.cwd(), + environment: definedCliEnvironment(cliProcessEnvironment(environment)), + }); + expect(fresh.stdout + fresh.stderr).not.toBe(""); expect({ exitCode: shared.exitCode, stdout: shared.stdout, diff --git a/tests/boundary/cli/domValueLengthUnits.test.ts b/tests/boundary/cli/domValueLengthUnits.test.ts index eb3bf4c656..d1583bb9a0 100644 --- a/tests/boundary/cli/domValueLengthUnits.test.ts +++ b/tests/boundary/cli/domValueLengthUnits.test.ts @@ -193,7 +193,7 @@ cliTest( before.dom = { total_nodes: 0, nodes: [] }; after.dom = { total_nodes: 0, nodes: [] }; } - const result = await cli.sharedCliRun!({ + const result = await cli.run({ arguments: [ "compare-web-captures", JSON.stringify({ inspection: before }), diff --git a/tests/boundary/cli/dylibResolutionCli.test.ts b/tests/boundary/cli/dylibResolutionCli.test.ts index 703596818a..a06e5231e3 100644 --- a/tests/boundary/cli/dylibResolutionCli.test.ts +++ b/tests/boundary/cli/dylibResolutionCli.test.ts @@ -17,11 +17,7 @@ import { import { dylibResolutionResultSchema } from "../../../src/domain/apple/dylibResolution.js"; import { parseEvidence } from "../../../src/domain/evidence.js"; import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js"; -import { - cliTest, - runFileCli, - type TestCli, -} from "../../support/cli/cliFixture.js"; +import { cliTest, type TestCli } from "../../support/cli/cliFixture.js"; const ENVIRONMENT = { REA_LOG_LEVEL: "silent", REA_ANALYSIS_PROVIDER: "auto" }; @@ -50,7 +46,7 @@ const expectRootInputError = async ( readonly message: string; }, ): Promise => { - const rejected = await runFileCli(cli, { + const rejected = await cli.run({ arguments: ["trace-dylib-resolution", app, "--root", root, "--json"], environment: ENVIRONMENT, }); @@ -121,7 +117,7 @@ cliTest.skipIf(process.platform !== "linux")( ]), machoImage({}), ); - const observed = await runFileCli(cli, { + const observed = await cli.run({ arguments: ["trace-dylib-resolution", app, "--json"], environment: ENVIRONMENT, }); @@ -141,7 +137,7 @@ cliTest( async ({ cli }) => { const app = await fixtureApp(); await symlink("unexamined", join(app, "Contents", "LinkedResources")); - const observed = await runFileCli(cli, { + const observed = await cli.run({ arguments: ["trace-dylib-resolution", app, "--json"], environment: ENVIRONMENT, }); @@ -179,7 +175,7 @@ cliTest( ], }), }); - const output = await runFileCli(cli, { + const output = await cli.run({ arguments: ["trace-dylib-resolution", app, "--json"], environment: ENVIRONMENT, }); @@ -373,7 +369,7 @@ for (const dependency of ["eager", "lazy", "none", "conditional"] as const) { async ({ cli }) => { const { app, root, nested, uncertain } = await loadedSiblingFixture(dependency); - const output = await runFileCli(cli, { + const output = await cli.run({ arguments: ["trace-dylib-resolution", join(app, root), "--json"], environment: ENVIRONMENT, }); @@ -466,7 +462,7 @@ for (const dependency of ["eager", "lazy", "none", "conditional"] as const) { describe("trace-dylib-resolution CLI", () => { cliTest("traces every executable root of an app bundle", async ({ cli }) => { const app = await fixtureApp(); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["trace-dylib-resolution", app, "--json"], environment: ENVIRONMENT, }); @@ -517,7 +513,7 @@ describe("trace-dylib-resolution CLI", () => { "narrows roots and architecture, and rejects escaping roots", async ({ cli }) => { const app = await fixtureApp(); - const narrowed = await runFileCli(cli, { + const narrowed = await cli.run({ arguments: [ "trace-dylib-resolution", app, @@ -573,7 +569,7 @@ describe("trace-dylib-resolution CLI", () => { }), "unrelated.txt": "not traversed", }); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: [ "trace-dylib-resolution", join(directory, "tool"), @@ -598,7 +594,7 @@ describe("trace-dylib-resolution CLI", () => { async ({ cli }) => { const app = await fixtureApp(); await withBrokenHelper(app); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["trace-dylib-resolution", app, "--json"], environment: ENVIRONMENT, }); @@ -649,7 +645,7 @@ cliTest( }); } - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["trace-dylib-resolution", app, "--json"], environment: ENVIRONMENT, }); @@ -706,7 +702,7 @@ cliTest( commands: [buildVersionCommand(1)], }), }); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["trace-dylib-resolution", join(directory, "tool"), "--json"], environment: ENVIRONMENT, }); @@ -756,7 +752,7 @@ describe.skipIf(process.platform === "win32" || process.getuid?.() === 0)( }); await chmod(join(directory, "libsecret.dylib"), 0o000); try { - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: [ "trace-dylib-resolution", join(directory, "tool"), @@ -804,7 +800,7 @@ cliTest( ], }), }); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: [ "trace-dylib-resolution", app, @@ -877,7 +873,7 @@ cliTest( { cpu: CPU.x86_64, bytes: machoImage({ cpu: CPU.x86_64 }) }, ]), }); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: [ "trace-dylib-resolution", app, @@ -931,7 +927,7 @@ cliTest( commands: [buildVersionCommand(1)], }), }); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: [ "trace-dylib-resolution", app, @@ -988,7 +984,7 @@ cliTest( ], }), ); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["trace-dylib-resolution", path, "--json"], environment: ENVIRONMENT, }); @@ -1036,7 +1032,7 @@ describe("trace-dylib-resolution root enumeration", () => { "classifies every Mach-O among more files than one batch of magic reads", async ({ cli }) => { const app = await crowdedApp(); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["trace-dylib-resolution", app, "--json"], environment: ENVIRONMENT, }); @@ -1067,7 +1063,7 @@ describe("trace-dylib-resolution root enumeration", () => { await writeFile(secret, "x"); await chmod(secret, 0o000); try { - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["trace-dylib-resolution", app, "--json"], environment: ENVIRONMENT, }); diff --git a/tests/boundary/cli/electronWrappedCallPaths.test.ts b/tests/boundary/cli/electronWrappedCallPaths.test.ts index fe131f0fb8..40909faeb3 100644 --- a/tests/boundary/cli/electronWrappedCallPaths.test.ts +++ b/tests/boundary/cli/electronWrappedCallPaths.test.ts @@ -7,11 +7,7 @@ import { z } from "zod"; import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js"; import { connectLocalToolsMcp } from "../../fixtures/localToolsMcp.js"; import { writeFixtureFiles } from "../../support/javascriptApplicationFixture.js"; -import { - cliTest, - runFileCli, - type TestCli, -} from "../../support/cli/cliFixture.js"; +import { cliTest, type TestCli } from "../../support/cli/cliFixture.js"; import { javascriptApplicationAnalysisResultSchema } from "../../../src/domain/javascript/javascriptApplicationAnalysis.js"; import { applicationFeatureTraceResultSchema } from "../../../src/domain/javascript/javascriptFeatureTraceSchemas.js"; import { analyzeJavaScriptDirectory } from "../../support/cli/javascriptApplication.js"; @@ -223,7 +219,7 @@ cliTest( direction: "outgoing", }), ); - const traced = await runFileCli(cli, { + const traced = await cli.run({ arguments: ["trace-application-feature", input, "--json"], }); expect(traced.exitCode).toBe(0); @@ -425,7 +421,7 @@ cliTest( "main.cjs": `const { ipcMain, clipboard } = require("electron"); ipcMain.handle("nested:write", (event, value) => clipboard.writeText(value));`, }); - const output = await runFileCli(cli, { + const output = await cli.run({ arguments: ["analyze-javascript-application", root, "--json"], }); expect(output.exitCode, output.stderr).toBe(0); @@ -467,7 +463,7 @@ cliTest( direction: "outgoing", }), ); - const traced = await runFileCli(cli, { + const traced = await cli.run({ arguments: ["trace-application-feature", traceInput, "--json"], }); expect(traced.exitCode).toBe(0); @@ -495,7 +491,7 @@ cliTest( "main.js": `const { ipcMain } = require("electron"); ipcMain.on("write", (event) => event.sender.copy());`, }); - const output = await runFileCli(cli, { + const output = await cli.run({ arguments: ["analyze-javascript-application", root, "--json"], }); expect(output.exitCode).toBe(0); @@ -552,7 +548,7 @@ cliTest( "main.js": `const { ipcMain } = require("electron"); ipcMain.handle("nested:write", (event, value) => value);`, }); - const output = await runFileCli(cli, { + const output = await cli.run({ arguments: ["analyze-javascript-application", root, "--json"], }); expect(output.exitCode).toBe(0); @@ -585,7 +581,7 @@ cliTest( direction: "outgoing", }), ); - const traced = await runFileCli(cli, { + const traced = await cli.run({ arguments: ["trace-application-feature", traceInput, "--json"], }); expect(traced.exitCode).toBe(0); @@ -855,7 +851,7 @@ cliTest( e.ipcMain.on("shallow:run", event => event.sender.copy()); e.ipcMain.on("deep:run", event => event.sender.copy());`, }); - const output = await runFileCli(cli, { + const output = await cli.run({ arguments: ["analyze-javascript-application", root, "--json"], }); expect(output.exitCode).toBe(0); @@ -909,7 +905,7 @@ cliTest( "main.cjs": `const e=require("electron"); e.ipcMain.on("shared:run", event => event.sender.copy());`, }); - const output = await runFileCli(cli, { + const output = await cli.run({ arguments: ["analyze-javascript-application", root, "--json"], }); expect(output.exitCode, output.stderr).toBe(0); @@ -944,7 +940,7 @@ cliTest( join(root, "main.cjs"), 'const e=require("electron"); e.ipcMain.on("shared:run", () => {}); e.ipcMain.on("shared:run", () => {});', ); - const competing = await runFileCli(cli, { + const competing = await cli.run({ arguments: ["analyze-javascript-application", root, "--json"], }); expect(competing.exitCode, competing.stderr).toBe(0); @@ -975,7 +971,7 @@ cliTest( "main.cjs": `const { ipcMain } = require("electron"); ipcMain.on("empty:key", event => event.sender.copy());`, }); - const output = await runFileCli(cli, { + const output = await cli.run({ arguments: ["analyze-javascript-application", root, "--json"], }); expect(output.exitCode).toBe(0); diff --git a/tests/boundary/cli/environmentBuildCli.test.ts b/tests/boundary/cli/environmentBuildCli.test.ts index 0746086b41..5c3482c568 100644 --- a/tests/boundary/cli/environmentBuildCli.test.ts +++ b/tests/boundary/cli/environmentBuildCli.test.ts @@ -289,3 +289,26 @@ cliTest.for(["--dry-run=true", "--dry-run=false"] as const)( ); }, ); + +cliTest( + "reports its default build-stage logging through the routed CLI driver", + async ({ cli }) => { + const root = await createTestTempDirectory("rea-environment-build-logs-"); + const har = join(root, "grafana.har"); + await writeFile(har, JSON.stringify(environmentManifestHar())); + // Without REA_LOG_LEVEL the build logs its stages at info on stderr. + const invocation = { + arguments: ["environment-build", har, "--dry-run", "--json"], + }; + const routed = await cli.run(invocation); + const fresh = await cli.run({ ...invocation, freshProcess: true }); + expect(fresh.stderr).toContain("Building environment"); + const stages = (stderr: string) => + stderr + .split("\n") + .filter((line) => line.length > 0) + .map((line) => (JSON.parse(line) as { stage?: unknown }).stage); + expect(stages(routed.stderr)).toEqual(stages(fresh.stderr)); + expect(routed.exitCode).toBe(fresh.exitCode); + }, +); diff --git a/tests/boundary/cli/evidenceBundleCommands.test.ts b/tests/boundary/cli/evidenceBundleCommands.test.ts index d7a59fe659..2e3b3a504b 100644 --- a/tests/boundary/cli/evidenceBundleCommands.test.ts +++ b/tests/boundary/cli/evidenceBundleCommands.test.ts @@ -22,7 +22,7 @@ import { updateResidualUnknown, } from "../../../src/domain/residualUnknown.js"; import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js"; -import { cliTest, runFileCli } from "../../support/cli/cliFixture.js"; +import { cliTest } from "../../support/cli/cliFixture.js"; const revisionBundle = () => { const records = ["open", "investigate"].map((operation) => @@ -78,7 +78,7 @@ cliTest( [["compare", "bundle.json", ""], "right"], [["evidence-export", "missing.json", ""], "output"], ] as const) { - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: [...args, "--json"], cwd: root, }); @@ -138,7 +138,7 @@ cliTest( ["evidence-export", { source: " bundle.json", output: " exported.json" }], ["compare", { left: " bundle.json", right: " other.json" }], ] as const) { - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: [command, "--schema", "--json"], }); expect(result.exitCode, result.stderr).toBe(0); @@ -246,7 +246,7 @@ cliTest.for([ : operation === "compare-left" ? ["compare", path, canonical] : ["compare", canonical, path]; - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: [...arguments_, "--json"], environment: { REA_LOG_LEVEL: "silent" }, }); @@ -325,7 +325,7 @@ cliTest( await writeFile(source, JSON.stringify(bundle)); for (const full of [false, true]) { const output = join(root, `${label}-${String(full)}.json`); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: [ "evidence-export", source, @@ -387,7 +387,7 @@ cliTest( { ...bundle, unknowns: bundle.unknowns.slice(1) }, ]) { await writeFile(path, JSON.stringify(invalid)); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["evidence-import", path, "--json"], }); expect(result.exitCode).toBe(1); @@ -414,7 +414,7 @@ cliTest( ); await writeFile(path, JSON.stringify(createEvidenceBundle([record]))); - const output = await runFileCli(cli, { + const output = await cli.run({ arguments: [ "inspect-evidence-bundle", JSON.stringify({ path, detail: "complete" }), @@ -466,12 +466,12 @@ cliTest( "--filter-output", "records.0.normalized_result", ]; - const json = await runFileCli(cli, { arguments: [...base, "--json"] }); + const json = await cli.run({ arguments: [...base, "--json"] }); expect(json.exitCode, json.stderr).toBe(0); expect(json.stdout).toContain(JSON.stringify(result.text)); expect(json.stdout).toContain(JSON.stringify(result.type)); expect(json.stdout).toContain(JSON.stringify(result.literal)); - const markdown = await runFileCli(cli, { + const markdown = await cli.run({ arguments: [...base, "--format", "md"], }); expect(markdown.exitCode, markdown.stderr).toBe(0); @@ -497,7 +497,7 @@ cliTest( records: [record], }; await writeFile(path, JSON.stringify(bundle)); - const imported = await runFileCli(cli, { + const imported = await cli.run({ arguments: ["evidence-import", path, "--json"], }); expect(imported.exitCode).toBe(1); diff --git a/tests/boundary/cli/il2cppCallerPath.test.ts b/tests/boundary/cli/il2cppCallerPath.test.ts index 1b3884dc39..2e8f9be846 100644 --- a/tests/boundary/cli/il2cppCallerPath.test.ts +++ b/tests/boundary/cli/il2cppCallerPath.test.ts @@ -6,7 +6,7 @@ import { readEvidenceBundle } from "../../../src/application/EvidenceBundleFiles import { createLinkedIl2cppMetadataFixture } from "../../fixtures/il2cpp/metadata.js"; import { createIl2cppDyldBindFixture } from "../../fixtures/il2cpp/binary.js"; import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js"; -import { cliTest, runFileCli } from "../../support/cli/cliFixture.js"; +import { cliTest } from "../../support/cli/cliFixture.js"; cliTest( "preserves both selected IL2CPP input spellings in inline and bundled Evidence", @@ -22,7 +22,7 @@ cliTest( ); const input = "./metadata.dat"; const binary = "./GameAssembly.dylib"; - const inline = await runFileCli(cli, { + const inline = await cli.run({ cwd: root, arguments: [ "inspect-il2cpp-metadata", @@ -47,7 +47,7 @@ cliTest( "./evidence.json", "--json", ]; - const summary = await runFileCli(cli, { + const summary = await cli.run({ cwd: root, arguments: summaryArgs, }); @@ -56,7 +56,7 @@ cliTest( parseEvidence((summary.json as { summary: unknown }).summary) .caller_input_paths, ).toEqual({ binary_path: binary }); - const refused = await runFileCli(cli, { + const refused = await cli.run({ cwd: root, arguments: summaryArgs, }); @@ -71,7 +71,7 @@ cliTest( expect(saved.value.records[0]?.caller_input_paths).toEqual({ binary_path: binary, }); - const without = await runFileCli(cli, { + const without = await cli.run({ cwd: root, arguments: ["inspect-il2cpp-metadata", input, "--json"], }); diff --git a/tests/boundary/cli/javascriptAssignmentInfluence.test.ts b/tests/boundary/cli/javascriptAssignmentInfluence.test.ts index 043ca01e83..2cb6ddd006 100644 --- a/tests/boundary/cli/javascriptAssignmentInfluence.test.ts +++ b/tests/boundary/cli/javascriptAssignmentInfluence.test.ts @@ -4,7 +4,7 @@ import { expect } from "vitest"; import { z } from "zod"; import { javaScriptSemanticTraceResultSchema } from "../../../src/domain/javascript/javascriptSemanticTraceSchemas.js"; import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js"; -import { cliTest, runFileCli } from "../../support/cli/cliFixture.js"; +import { cliTest } from "../../support/cli/cliFixture.js"; cliTest( "traces reassignment inputs through the public analysis and trace commands", @@ -51,9 +51,10 @@ cliTest( const trace = { arguments: ["trace-javascript-semantics", queryFile, "--json"], }; - const traced = await (name === "declaration" - ? cli.run(trace) - : runFileCli(cli, trace)); + const traced = await cli.run({ + ...trace, + freshProcess: name === "declaration", + }); expect(traced.exitCode, `${name} traced: ${traced.stderr}`).toBe(0); const { normalized_result: result } = z .object({ normalized_result: javaScriptSemanticTraceResultSchema }) @@ -74,7 +75,7 @@ cliTest( query: { ...query.query, include_ambiguous_dynamic_edges: true }, }), ); - const expanded = await runFileCli(cli, { + const expanded = await cli.run({ arguments: ["trace-javascript-semantics", queryFile, "--json"], }); expect(expanded.exitCode, `${name} expanded: ${expanded.stderr}`).toBe(0); diff --git a/tests/boundary/cli/keyedArchivePrototypeKeyCli.test.ts b/tests/boundary/cli/keyedArchivePrototypeKeyCli.test.ts index ba14408175..1d4121c832 100644 --- a/tests/boundary/cli/keyedArchivePrototypeKeyCli.test.ts +++ b/tests/boundary/cli/keyedArchivePrototypeKeyCli.test.ts @@ -9,7 +9,7 @@ import { expectKeyedArchivePrototypeKeys, keyedArchivePrototypeKeyFixture, } from "../../fixtures/keyedArchivePrototypeKeys.js"; -import { cliTest, runFileCli } from "../../support/cli/cliFixture.js"; +import { cliTest } from "../../support/cli/cliFixture.js"; import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js"; import { keyedArchiveResultSchema } from "../../../src/domain/apple/keyedArchive.js"; import { connectLocalToolsMcp } from "../../fixtures/localToolsMcp.js"; @@ -86,9 +86,10 @@ cliTest( USERPROFILE: directory, }, }; - const output = await (name !== "plain" && rootArguments.length === 0 - ? cli.run(invocation) - : runFileCli(cli, invocation)); + const output = await cli.run({ + ...invocation, + freshProcess: name !== "plain" && rootArguments.length === 0, + }); expect(output.exitCode, output.stdout).toBe(0); const result = keyedArchiveResultSchema.parse( parseEvidence(output.json).normalized_result, diff --git a/tests/boundary/cli/managedMemberComparisonCli.test.ts b/tests/boundary/cli/managedMemberComparisonCli.test.ts index 5680390577..d79bee2a3e 100644 --- a/tests/boundary/cli/managedMemberComparisonCli.test.ts +++ b/tests/boundary/cli/managedMemberComparisonCli.test.ts @@ -134,7 +134,7 @@ cliTest( const sha256 = createHash("sha256").update(bytes).digest("hex"); await writeFile(path, bytes); await writeFile(peerPath, bytes); - const inspected = await cli.sharedCliRun!({ + const inspected = await cli.run({ arguments: ["inspect-managed-members", path, "--json"], }); expect(inspected.exitCode, inspected.stderr).toBe(0); @@ -160,7 +160,7 @@ cliTest( : {}), }); } - const compared = await cli.sharedCliRun!({ + const compared = await cli.run({ arguments: ["compare-managed-members", path, path, "--json"], }); expect(compared.exitCode, compared.stderr).toBe(0); diff --git a/tests/boundary/cli/managedMemberSummaryCli.test.ts b/tests/boundary/cli/managedMemberSummaryCli.test.ts index 45045b4d49..5f74d5118d 100644 --- a/tests/boundary/cli/managedMemberSummaryCli.test.ts +++ b/tests/boundary/cli/managedMemberSummaryCli.test.ts @@ -28,11 +28,7 @@ import { } from "../../../src/domain/managed/managedArtifact.js"; import { buildManagedPeFixture } from "../../../src/dotnet/ManagedPe.fixture.js"; import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js"; -import { - cliTest, - runFileCli, - type TestCli, -} from "../../support/cli/cliFixture.js"; +import { cliTest, type TestCli } from "../../support/cli/cliFixture.js"; const memberFixture = (): Buffer => buildManagedPeFixture({ @@ -86,7 +82,7 @@ const verifyCliSelection = async ({ parent, report, }: CliMemberViewContext) => { - const selected = await runFileCli(cli, { + const selected = await cli.run({ cwd, arguments: [ "inspect-analysis-view", @@ -154,7 +150,7 @@ const verifyCliFullPages = async ( expect(reportedCounts[collection]).toBe(rows.length); const paged: unknown[] = []; for (let offset: number | null = 0; offset !== null;) { - const response = await runFileCli(cli, { + const response = await cli.run({ cwd, arguments: [ "inspect-analysis-view", diff --git a/tests/boundary/cli/markdownLiteralCells.test.ts b/tests/boundary/cli/markdownLiteralCells.test.ts index c17f04f691..1c37a8052a 100644 --- a/tests/boundary/cli/markdownLiteralCells.test.ts +++ b/tests/boundary/cli/markdownLiteralCells.test.ts @@ -12,7 +12,7 @@ import { modelArtifactSchema } from "../../../src/domain/model/modelArtifact.js" import { renderCliDocument } from "../../../src/cliOutput.js"; import { safetensorsFixture } from "../../fixtures/model/model.js"; import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js"; -import { cliTest, runFileCli } from "../../support/cli/cliFixture.js"; +import { cliTest } from "../../support/cli/cliFixture.js"; afterAll(() => disposeMdItInstance()); @@ -230,7 +230,7 @@ cliTest( "preserves capability provider identities in rendered Markdown", async ({ cli }) => { const json = await cli.run({ arguments: ["capabilities", "--json"] }); - const markdown = await runFileCli(cli, { + const markdown = await cli.run({ arguments: ["capabilities", "--format", "md"], }); expect(json.exitCode, json.stderr).toBe(0); @@ -276,7 +276,7 @@ cliTest( "--filter-output", "records.0.normalized_result", ]; - const markdown = await runFileCli(cli, { + const markdown = await cli.run({ arguments: [...base, "--format", "md"], }); const json = await cli.run({ arguments: [...base, "--json"] }); @@ -342,7 +342,7 @@ cliTest( ); const base = ["inspect-model-artifact", path, "--detail", "complete"]; const json = await cli.run({ arguments: [...base, "--json"] }); - const markdown = await runFileCli(cli, { + const markdown = await cli.run({ arguments: [...base, "--format", "md"], }); expect(json.exitCode, json.stderr).toBe(0); @@ -397,7 +397,7 @@ cliTest( "--filter-output", "records.0.normalized_result", ]; - const markdown = await runFileCli(cli, { + const markdown = await cli.run({ arguments: [...base, "--format", "md"], }); const json = await cli.run({ arguments: [...base, "--json"] }); diff --git a/tests/boundary/cli/reconstructionObligationLedgerCli.test.ts b/tests/boundary/cli/reconstructionObligationLedgerCli.test.ts index 5eb233db1d..6b08587d76 100644 --- a/tests/boundary/cli/reconstructionObligationLedgerCli.test.ts +++ b/tests/boundary/cli/reconstructionObligationLedgerCli.test.ts @@ -6,7 +6,7 @@ import { join } from "node:path"; import { describe, expect, it } from "vitest"; -import { cliTest, runFileCli } from "../../support/cli/cliFixture.js"; +import { cliTest } from "../../support/cli/cliFixture.js"; import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js"; import { javascriptApplicationAnalysisResultSchema } from "../../../src/domain/javascript/javascriptApplicationAnalysis.js"; import { reconstructionObligationLedgerSchema } from "../../../src/domain/reconstructionObligationLedgerSchemas.js"; @@ -384,10 +384,10 @@ describe("reconstruction proof applicability through CLI", () => { "--json", ], }; - const result = - name === "matching implementation and revision" - ? await cli.run(invocation) - : await runFileCli(cli, invocation); + const result = await cli.run({ + ...invocation, + freshProcess: name === "matching implementation and revision", + }); expect(result.exitCode, result.stderr).toBe(0); const ledger = reconstructionObligationLedgerSchema.parse( (result.json as { normalized_result: unknown }).normalized_result, diff --git a/tests/boundary/cli/referenceSourceSummary.test.ts b/tests/boundary/cli/referenceSourceSummary.test.ts index 1102c351a9..06adf583fe 100644 --- a/tests/boundary/cli/referenceSourceSummary.test.ts +++ b/tests/boundary/cli/referenceSourceSummary.test.ts @@ -7,7 +7,7 @@ import { describe, expect } from "vitest"; import { historicalSourceGraphSchema } from "../../../src/domain/referenceSourceGraph.js"; import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js"; -import { cliTest, runFileCli } from "../../support/cli/cliFixture.js"; +import { cliTest } from "../../support/cli/cliFixture.js"; describe.skipIf(process.platform === "win32")( "compiled historical-source summaries", @@ -199,10 +199,10 @@ describe("compiled historical-source summary input boundaries", () => { arguments: ["import-reference-source", root, ...options, "--json"], cwd: root, }; - const result = - label === "--outline-depth=" - ? await cli.run(invocation) - : await runFileCli(cli, invocation); + const result = await cli.run({ + ...invocation, + freshProcess: label === "--outline-depth=", + }); expect(result.exitCode).toBe(1); expect(result.json).toMatchObject({ code: "VALIDATION_ERROR", @@ -226,7 +226,7 @@ describe("compiled historical-source summary input boundaries", () => { const root = await createTestTempDirectory( "rea-reference-summary-input-", ); - const result = await runFileCli(cli, { + const result = await cli.run({ arguments: ["import-reference-source", root, ...options, "--json"], cwd: root, }); diff --git a/tests/boundary/cli/sharedJavaScriptCliSession.test.ts b/tests/boundary/cli/sharedJavaScriptCliSession.test.ts index 420d019622..c39a5630f8 100644 --- a/tests/boundary/cli/sharedJavaScriptCliSession.test.ts +++ b/tests/boundary/cli/sharedJavaScriptCliSession.test.ts @@ -922,15 +922,6 @@ test("closes the real JavaScript CLI worker through its resource cleanup", async expect(session.processId()).toBeUndefined(); }); -const requireSharedCli = (cli: { - sharedCliRun?: (invocation: CliInvocation) => Promise; -}): ((invocation: CliInvocation) => Promise) => { - const run = cli.sharedCliRun; - if (run === undefined) - throw new Error("cliTest did not provide a shared CLI session"); - return run; -}; - const normalized = (result: CliResult) => z .object({ normalized_result: javascriptApplicationAnalysisResultSchema }) @@ -957,12 +948,12 @@ const writeApplication = async (prefix: string): Promise => { cliTest( "matches a fresh CLI process and reuses that process for the same environment", async ({ cli, cliCommandSession }) => { - const sharedCliRun = requireSharedCli(cli); const fresh = await cli.run({ + freshProcess: true, arguments: ["--version"], environment: { REA_LOG_LEVEL: "silent" }, }); - const shared = await sharedCliRun({ + const shared = await cli.run({ arguments: ["--version"], environment: { REA_LOG_LEVEL: "silent" }, }); @@ -971,7 +962,7 @@ cliTest( expect(shared.stderr).toBe(fresh.stderr); const pid = cliCommandSession.processId(); expect(pid).toEqual(expect.any(Number)); - const again = await sharedCliRun({ + const again = await cli.run({ arguments: ["--version"], environment: { REA_LOG_LEVEL: "silent" }, }); @@ -979,10 +970,11 @@ cliTest( expect(cliCommandSession.processId()).toBe(pid); const freshInvalid = await cli.run({ + freshProcess: true, arguments: ["--version"], environment: { REA_LOG_LEVEL: "not-a-level" }, }); - const sharedInvalid = await sharedCliRun({ + const sharedInvalid = await cli.run({ arguments: ["--version"], environment: { REA_LOG_LEVEL: "not-a-level" }, }); @@ -992,7 +984,7 @@ cliTest( expect(sharedInvalid.stderr).toBe(freshInvalid.stderr); expect(cliCommandSession.processId()).not.toBe(pid); - const restored = await sharedCliRun({ + const restored = await cli.run({ arguments: ["--version"], environment: { REA_LOG_LEVEL: "silent" }, }); @@ -1005,13 +997,13 @@ cliTest( cliTest( "reuses one CLI process across JavaScript application directories", async ({ cli, cliCommandSession }) => { - const sharedCliRun = requireSharedCli(cli); const firstRoot = await writeApplication("rea-shared-cli-"); const fresh = await cli.run({ + freshProcess: true, arguments: analyzeArguments(firstRoot), environment: { REA_LOG_LEVEL: "silent" }, }); - const shared = await sharedCliRun({ + const shared = await cli.run({ arguments: analyzeArguments(firstRoot), environment: { REA_LOG_LEVEL: "silent" }, }); @@ -1022,10 +1014,11 @@ cliTest( const secondRoot = await writeApplication("rea-shared-cli-next-"); const freshNext = await cli.run({ + freshProcess: true, arguments: analyzeArguments(secondRoot), environment: { REA_LOG_LEVEL: "silent" }, }); - const sharedNext = await sharedCliRun({ + const sharedNext = await cli.run({ arguments: analyzeArguments(secondRoot), environment: { REA_LOG_LEVEL: "silent" }, }); diff --git a/tests/boundary/cli/workflowInputIdentities.test.ts b/tests/boundary/cli/workflowInputIdentities.test.ts index 7f45350f60..4e9b1b301e 100644 --- a/tests/boundary/cli/workflowInputIdentities.test.ts +++ b/tests/boundary/cli/workflowInputIdentities.test.ts @@ -36,7 +36,7 @@ const runJson = async ( await writeFile(path, JSON.stringify(input)); // These JSON commands share no provider state; reuse the fixture-owned CLI // process so the matrix measures validation rather than repeated startup. - const run = cli.sharedCliRun?.bind(cli) ?? cli.run.bind(cli); + const run = cli.run.bind(cli); return run({ arguments: [ command, diff --git a/tests/boundary/filesystem/keyedArchiveNumberMetadata.test.ts b/tests/boundary/filesystem/keyedArchiveNumberMetadata.test.ts index 691645d13f..466f7085e2 100644 --- a/tests/boundary/filesystem/keyedArchiveNumberMetadata.test.ts +++ b/tests/boundary/filesystem/keyedArchiveNumberMetadata.test.ts @@ -13,7 +13,7 @@ import { expectKeyedArchiveNumberMetadata, keyedArchiveNumberMetadataFixture, } from "../../fixtures/keyedArchiveNumberMetadata.js"; -import { cliTest, runFileCli } from "../../support/cli/cliFixture.js"; +import { cliTest } from "../../support/cli/cliFixture.js"; it("preserves 16-byte binary integers and adjacent 64-bit integer metadata", async () => { const fixture = await keyedArchiveWideIntegerFixture(); @@ -133,7 +133,7 @@ cliTest.for(archiveNumberMetadataCases)( "built CLI preserves numeric meaning for $name", async (item, { cli }) => { const fixture = await keyedArchiveNumberMetadataFixture(item); - const output = await runFileCli(cli, { + const output = await cli.run({ arguments: ["inspect-keyed-archive", fixture.path, "--json"], environment: { REA_LOG_LEVEL: "silent", REA_ANALYSIS_PROVIDER: "auto" }, }); diff --git a/tests/boundary/mcp/browserScenarioInputDiagnosticsMcp.test.ts b/tests/boundary/mcp/browserScenarioInputDiagnosticsMcp.test.ts index bc7a4d4cc7..7da159b1b3 100644 --- a/tests/boundary/mcp/browserScenarioInputDiagnosticsMcp.test.ts +++ b/tests/boundary/mcp/browserScenarioInputDiagnosticsMcp.test.ts @@ -7,7 +7,7 @@ import { z } from "zod"; import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js"; import { parseMcpToolError } from "../../fixtures/mcpToolError.js"; -import { cliTest, runFileCli } from "../../support/cli/cliFixture.js"; +import { cliTest } from "../../support/cli/cliFixture.js"; const scenarios = [ { @@ -126,7 +126,7 @@ cliTest.for(["full", "compact"] as const)( `${scenario.name}: ${JSON.stringify(validate.errors)}`, ).toBe(scenario.fullSchemaValid || profile === "compact"); - const cliResult = await runFileCli(cli, { + const cliResult = await cli.run({ arguments: [ "capture-browser-scenario", JSON.stringify(input), diff --git a/tests/boundary/mcp/managedNativeVerificationAdapters.test.ts b/tests/boundary/mcp/managedNativeVerificationAdapters.test.ts index 82ee1aedfc..67affdb330 100644 --- a/tests/boundary/mcp/managedNativeVerificationAdapters.test.ts +++ b/tests/boundary/mcp/managedNativeVerificationAdapters.test.ts @@ -15,7 +15,7 @@ import { buildManagedPeFixture } from "../../../src/dotnet/ManagedPe.fixture.js" import { fileLocalToolsMcp } from "../../fixtures/localToolsMcp.js"; import { parseMcpToolError } from "../../fixtures/mcpToolError.js"; import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js"; -import { cliTest, runFileCli } from "../../support/cli/cliFixture.js"; +import { cliTest } from "../../support/cli/cliFixture.js"; const fileMcp = fileLocalToolsMcp(); @@ -330,10 +330,10 @@ cliTest( const invocation = { arguments: ["verify-managed-native-boundaries", request, "--json"], }; - const cliRun = - index === 0 - ? await cli.run(invocation) - : await runFileCli(cli, invocation); + const cliRun = await cli.run({ + ...invocation, + freshProcess: index === 0, + }); expect(cliRun.exitCode, cliRun.stderr).toBe(0); const cliResult = managedNativeVerificationResultSchema.parse( parseEvidence(cliRun.json).normalized_result, diff --git a/tests/boundary/process/processCli.test.ts b/tests/boundary/process/processCli.test.ts index ec46bdf6b3..3b24ae6f5a 100644 --- a/tests/boundary/process/processCli.test.ts +++ b/tests/boundary/process/processCli.test.ts @@ -148,9 +148,7 @@ describe("process CLI cleanup errors", () => { try { // A retained PTY can keep its CLI process alive. Read the completed // compiled command result before releasing the child and joining exit. - if (cli.sharedCliRun === undefined) - throw new Error("Expected the compiled CLI command session"); - const result = await cli.sharedCliRun({ + const result = await cli.run({ arguments: ["capture-process", scenario, "--json"], environment: { TMPDIR: root, diff --git a/tests/fixtures/javascriptReturnShapes.ts b/tests/fixtures/javascriptReturnShapes.ts index f9c873599a..8e24c39ef4 100644 --- a/tests/fixtures/javascriptReturnShapes.ts +++ b/tests/fixtures/javascriptReturnShapes.ts @@ -9,7 +9,7 @@ import { toolContract } from "../../src/contracts/toolContracts.js"; import { javascriptApplicationAnalysisResultSchema } from "../../src/domain/javascript/javascriptApplicationAnalysis.js"; import { projectedExportReturnShapesSchema } from "../../src/domain/javascript/javascriptExportShapeComparisonSchemas.js"; import { createTestTempDirectory } from "./temporaryDirectory.js"; -import { runFileCli, type TestCli } from "../support/cli/cliFixture.js"; +import type { TestCli } from "../support/cli/cliFixture.js"; import { createMcpSchemaValidation } from "./mcpSchemaValidation.js"; const exportedReturnFields = (value: unknown) => { @@ -46,7 +46,7 @@ const analyzeReturnShapesThroughCli = async ( ) => { const root = await createTestTempDirectory("rea-return-shapes-"); await writeFile(join(root, fileName), source); - const cliResponse = await runFileCli(cli, { + const cliResponse = await cli.run({ arguments: [ "analyze-javascript-application", root, diff --git a/tests/support/cli/cliCommandSessionWorker.mjs b/tests/support/cli/cliCommandSessionWorker.mjs index 653c2ddf5b..dec542971c 100644 --- a/tests/support/cli/cliCommandSessionWorker.mjs +++ b/tests/support/cli/cliCommandSessionWorker.mjs @@ -1,4 +1,4 @@ -import { Writable } from "node:stream"; +import { Readable, Writable } from "node:stream"; import { access } from "node:fs/promises"; import { dirname, resolve } from "node:path"; import { fileURLToPath } from "node:url"; @@ -21,6 +21,10 @@ const { default: packageJson } = await import( { with: { type: "json" } } ); +// Present the launcher's entrypoint, as a fresh `rea` process does. The +// runtime derives the current registration command from it at load time. +process.argv[1] = resolve(packageRoot, "scripts/rea.mjs"); + const runtimeFiles = [ "dist/cli-runtime/cli.js", "dist/cli-runtime/cliOneShot.js", @@ -158,6 +162,15 @@ async function runOneShot(args) { const originalStderrWrite = process.stderr.write.bind(process.stderr); process.stdout.write = captureWrite(stdoutChunks); process.stderr.write = captureWrite(stderrChunks); + // This process's stdin carries session frames. Give each command the empty, + // ended stdin a fresh process receives when the caller supplies no input. + const stdinDescriptor = Object.getOwnPropertyDescriptor(process, "stdin"); + const commandStdin = Readable.from([]); + Object.defineProperty(process, "stdin", { + configurable: true, + enumerable: stdinDescriptor?.enumerable ?? true, + get: () => commandStdin, + }); const destination = new Writable({ write(chunk, encoding, callback) { process.stdout.write(chunk, encoding, callback); @@ -209,6 +222,8 @@ async function runOneShot(args) { } finally { process.stdout.write = originalStdoutWrite; process.stderr.write = originalStderrWrite; + if (stdinDescriptor !== undefined) + Object.defineProperty(process, "stdin", stdinDescriptor); } const exitCode = explicitExit ?? diff --git a/tests/support/cli/cliFixture.ts b/tests/support/cli/cliFixture.ts index db955c3ca3..de85866343 100644 --- a/tests/support/cli/cliFixture.ts +++ b/tests/support/cli/cliFixture.ts @@ -1,5 +1,11 @@ import { resolve } from "node:path"; import { tmpdir } from "node:os"; +import { + productionMcpDoctorArguments, + splitIncurBuiltinArguments, +} from "../../../src/cliBuiltinArguments.js"; +import { DEFAULT_INFO_LOGGING_COMMANDS } from "../../../src/cliCommandNames.js"; +import { parseLogLevel } from "../../../src/config/processStart.js"; import { INLINE_EVIDENCE_MCP_ENVIRONMENT } from "../../../scripts/lib/mcp-verification-environment.mjs"; import { @@ -23,6 +29,11 @@ export interface CliInvocation { readonly environment?: NodeJS.ProcessEnv; readonly input?: string | Uint8Array; readonly timeoutMs?: number; + /** + * Start a new process even when the file's shared process could serve the + * command. Use it for process-level facts such as startup or exit. + */ + readonly freshProcess?: boolean; } /** Normalized result from the compiled REA CLI. */ @@ -39,18 +50,17 @@ export type SharedMcpCall = Awaited< /** Compiled REA CLI driver owned by one test. */ export interface TestCli { + /** + * Run one command. `cliTest` drivers use the file's shared process when it + * reports the command exactly as a fresh process would, and start a fresh + * process otherwise. Drivers from `createTestCli` always start one. + */ run(invocation: CliInvocation): Promise; /** * File-scoped stdio MCP caller. Present on `cliTest` drivers and absent for * drivers created with `createTestCli` alone. */ sharedMcpCall?(): Promise; - /** - * File-scoped compiled CLI process for commands with no stdin. Present on - * `cliTest` drivers. A different environment restarts the process. Stdin and - * explicit deadlines stay on `run`, which starts a fresh process. - */ - sharedCliRun?(invocation: CliInvocation): Promise; } /** One file-scoped stdio MCP session. */ @@ -256,17 +266,45 @@ export const cliCompileCacheEnvironment = ( }); /** - * Run one command on the file-scoped compiled CLI. Stdin and explicit - * deadlines stay on `TestCli.run`, which starts a fresh process. + * Whether the file's shared process reports this command exactly as a fresh + * `rea` process would. Stdin and deadlines belong to a process; the launcher + * routes MCP and its doctor before the shared one-shot path; Node options + * change the process itself; and enabled logging, whether selected or a + * command default, writes outside the + * per-command output capture. */ -export const runFileCli = ( - cli: TestCli, - invocation: CliInvocation, -): Promise => { - const run = cli.sharedCliRun; - if (run === undefined) - throw new Error("cliTest did not provide a shared CLI session"); - return run(invocation); +const sharedProcessCanServe = ({ + arguments: arguments_, + environment, + input, + timeoutMs, + freshProcess, +}: CliInvocation): boolean => + freshProcess !== true && + input === undefined && + timeoutMs === undefined && + arguments_[0] !== "mcp" && + arguments_[0] !== "--mcp" && + productionMcpDoctorArguments([...arguments_]) === undefined && + environment?.NODE_OPTIONS === undefined && + !enablesLogging(environment?.REA_LOG_LEVEL) && + !logsByDefault(arguments_, environment?.REA_LOG_LEVEL); + +/** A command that logs at info by default unless REA_LOG_LEVEL selects otherwise. */ +const logsByDefault = ( + arguments_: readonly string[], + level: string | undefined, +): boolean => + level === undefined && + DEFAULT_INFO_LOGGING_COMMANDS.has( + splitIncurBuiltinArguments([...arguments_]).command[0] ?? "", + ); + +/** A valid level other than silent; an invalid level is a captured refusal. */ +const enablesLogging = (level: string | undefined): boolean => { + if (level === undefined) return false; + const parsed = parseLogLevel(level); + return parsed.ok && parsed.value !== "silent"; }; /** Create a compiled CLI driver backed by the supplied process registry. */ @@ -342,22 +380,17 @@ export const cliTest = processTest.extend<{ ) => { const cli = createTestCli(processes, cliCompileCache); await use({ - ...cli, + run: (invocation) => + sharedProcessCanServe(invocation) + ? cliCommandSession.run({ + arguments: invocation.arguments, + cwd: invocation.cwd ?? process.cwd(), + environment: definedCliEnvironment( + cliProcessEnvironment(invocation.environment, cliCompileCache), + ), + }) + : cli.run(invocation), sharedMcpCall: () => mcpSession.call(), - sharedCliRun: (invocation) => { - if ( - invocation.input !== undefined || - invocation.timeoutMs !== undefined - ) - return cli.run(invocation); - return cliCommandSession.run({ - arguments: invocation.arguments, - cwd: invocation.cwd ?? process.cwd(), - environment: definedCliEnvironment( - cliProcessEnvironment(invocation.environment, cliCompileCache), - ), - }); - }, }); }, }); diff --git a/tests/support/cli/javascriptApplication.ts b/tests/support/cli/javascriptApplication.ts index 7d8ac90fe7..3fc5de5f91 100644 --- a/tests/support/cli/javascriptApplication.ts +++ b/tests/support/cli/javascriptApplication.ts @@ -55,7 +55,7 @@ export const analyzeJavaScriptDirectory = async ( ) => { let document: unknown; if (surface === "CLI") { - const run = cli.sharedCliRun ?? ((invocation) => cli.run(invocation)); + const run = cli.run.bind(cli); const response = await run({ arguments: [ "analyze-javascript-application",