From b3657c557f6431d259b2fcb65e73a7c2d48bd4a4 Mon Sep 17 00:00:00 2001 From: morluto <76467478+morluto@users.noreply.github.com> Date: Sat, 14 Nov 2026 14:28:08 +0800 Subject: [PATCH] perf(tests): run plain CLI commands on the file's shared process (#10412) * refactor(cli): run one-shot commands through one shared owner scripts/rea.mjs and the test CLI session worker each implemented the one-shot command sequence: argument normalization, output admission, configuration errors, version documents, the Amp scope refusal, and result delivery. The worker copy had drifted: it never rendered formatted version reports or the Amp refusal. Move the sequence into src/cliOneShot.ts. Both callers supply a host for their streams, exit and resource ownership. The launcher keeps closed-pipe status, worker shutdown and process exit; the session worker captures output per command. The cliOutput bundle entry is superseded and removed. The dispatcher test now replaces only the command runtime, so output admission runs production code. A new parity test compares the file session with a fresh process. * test(cli): bundle the one-shot entry in the runtime bundle fixture * perf(tests): run plain CLI commands on the file's shared process `cli.run` in `cliTest` files started a fresh compiled CLI for every command, about 0.45s and 260 MB each before any work. The file-scoped session already existed, but callers had to opt in through `runFileCli` or `sharedCliRun`, so 1,300 call sites still spawned. Make the fixture choose: `cli.run` uses the shared process when it reports the command exactly as a fresh process would, and starts one otherwise (stdin, explicit deadlines, MCP and MCP doctor routing, NODE_OPTIONS, or enabled logging). `freshProcess: true` keeps explicit process-level checks. The session worker now presents the launcher's entrypoint, so registration identity matches a fresh process. `runFileCli` and `sharedCliRun` are superseded and removed; their call sites use `cli.run`, and paired fresh/shared checks use `freshProcess`. * style(tests): format the managed member summary call site * test(cli): give each shared-session command an empty stdin The shared CLI process reads session frames from its own stdin. A command that reads stdin without supplied input, such as `compare-wasm-interfaces - --json`, read that pipe and hung, where a fresh process receives EOF. The worker now presents an ended, empty stdin to each command and restores its own afterwards. The parity test compared the routed path with itself after cli.run began choosing the session. It now compares a fresh process with the session directly, including the `mcp add` refusal and a command reading absent stdin. * test(cli): keep default command logging and launcher checks on fresh processes environment-serve and environment-build log at info when REA_LOG_LEVEL is unset, through a logger that writes to stderr directly. The shared CLI process captures only process.stderr writes, so routed runs lost those lines. Declare the default-logging commands beside the command names, have infoLogger read that set, and start a fresh process for them. The log-level precedence checks compared the shared process with itself after routing; their fresh side now passes freshProcess: true. --- docs/development/testing.md | 16 +++- src/cli/environmentCommands.ts | 21 +++- src/cliCommandNames.ts | 9 ++ tests/acceptance/apkZip64FramingCli.test.ts | 2 +- .../applications/adbListingNumbers.test.ts | 9 +- .../adbNumericDeclarations.test.ts | 23 +++-- .../adbPackageInventoryCoverage.test.ts | 9 +- .../applications/apiContract.test.ts | 6 +- .../applicationVersionMatchingCli.test.ts | 6 +- .../applications/erlangBeamViews.test.ts | 4 +- .../applications/firmwareComparison.test.ts | 2 +- .../applications/graphqlContract.test.ts | 8 +- .../interfaceBuilderPlistNumbers.test.ts | 8 +- .../javascriptExportBindingModuleView.test.ts | 8 +- ...scriptFeatureTraceSeedSelectionCli.test.ts | 3 +- .../keyedArchiveTextUnicode.test.ts | 13 ++- .../keyedArchiveXmlNumbers.test.ts | 6 +- .../mobileDirectoryComponents.test.ts | 13 +-- .../applications/nativeExportsViews.test.ts | 2 +- .../applications/peResources.test.ts | 18 ++-- .../applications/playwrightTrace.test.ts | 6 +- .../applications/pythonPycViews.test.ts | 4 +- .../staticObservedEndpoints.test.ts | 2 +- .../artifactDmgAttachmentRecoveryCli.test.ts | 3 + .../cliBuiltinDocumentTokens.test.ts | 16 ++-- tests/acceptance/elfNoteCoreProfile.test.ts | 2 +- tests/acceptance/elfNotesAbiEndian.test.ts | 2 +- tests/acceptance/elfNotesAndroid.test.ts | 2 +- tests/acceptance/elfNotesCore.test.ts | 2 +- tests/acceptance/goBinaryCli.test.ts | 40 ++++---- .../acceptance/goBinaryP32PointerCli.test.ts | 4 +- .../goBinaryPointerMappingsCli.test.ts | 6 +- tests/acceptance/gpuCodeObjectsCli.test.ts | 44 ++++----- tests/acceptance/il2cppMetadataCli.test.ts | 56 +++++------ .../il2cppStructuralExtentsCli.test.ts | 6 +- .../acceptance/localPathAdmissionCli.test.ts | 8 +- tests/acceptance/managedCliOutput.test.ts | 10 +- tests/acceptance/managedMetadataDelta.test.ts | 2 +- tests/acceptance/managedSelectedScope.test.ts | 4 +- tests/acceptance/modelArtifactCli.test.ts | 16 ++-- tests/acceptance/peCertificatesCli.test.ts | 32 +++---- tests/acceptance/peImportsCli.test.ts | 9 +- tests/acceptance/pythonPycCli.test.ts | 26 ++--- .../acceptance/rizinCommandRejection.test.ts | 2 +- tests/acceptance/rustBinaryCli.test.ts | 26 ++--- tests/acceptance/rustUnnamedSections.test.ts | 2 +- tests/acceptance/sqliteDatabaseCli.test.ts | 47 +++++---- .../acceptance/tools/harBoundaryFacts.test.ts | 10 +- .../tools/mobileGraphExamples.test.ts | 10 +- .../tools/nativeFunctionMatching.test.ts | 22 ++--- .../passiveCaptureCorrespondence.test.ts | 9 +- .../tools/passiveNetworkInventory.test.ts | 9 +- .../acceptance/tools/protobufCapture.test.ts | 9 +- .../tools/protobufDescriptorFiles.test.ts | 4 +- .../tools/protobufDescriptorIdentity.test.ts | 4 +- .../tools/protobufMimeAdmission.test.ts | 6 +- .../tools/protobufRegistryIdentity.test.ts | 2 +- .../unavailableNativeCollectionViews.test.ts | 2 +- .../boundary/cli/analysisSelectorCli.test.ts | 6 +- tests/boundary/cli/analysisViewCli.test.ts | 95 ++++++++++--------- .../boundary/cli/cliIntegerArguments.test.ts | 26 +++-- .../cli/cliLogLevelConfiguration.test.ts | 13 +-- tests/boundary/cli/cliSessionParity.test.ts | 30 ++++-- .../boundary/cli/domValueLengthUnits.test.ts | 2 +- tests/boundary/cli/dylibResolutionCli.test.ts | 42 ++++---- .../cli/electronWrappedCallPaths.test.ts | 26 +++-- .../boundary/cli/environmentBuildCli.test.ts | 23 +++++ .../cli/evidenceBundleCommands.test.ts | 20 ++-- tests/boundary/cli/il2cppCallerPath.test.ts | 10 +- .../cli/javascriptAssignmentInfluence.test.ts | 11 ++- .../cli/keyedArchivePrototypeKeyCli.test.ts | 9 +- .../cli/managedMemberComparisonCli.test.ts | 4 +- .../cli/managedMemberSummaryCli.test.ts | 10 +- .../boundary/cli/markdownLiteralCells.test.ts | 10 +- .../reconstructionObligationLedgerCli.test.ts | 10 +- .../cli/referenceSourceSummary.test.ts | 12 +-- .../cli/sharedJavaScriptCliSession.test.ts | 27 ++---- .../cli/workflowInputIdentities.test.ts | 2 +- .../keyedArchiveNumberMetadata.test.ts | 4 +- ...browserScenarioInputDiagnosticsMcp.test.ts | 4 +- .../managedNativeVerificationAdapters.test.ts | 10 +- tests/boundary/process/processCli.test.ts | 4 +- tests/fixtures/javascriptReturnShapes.ts | 4 +- tests/support/cli/cliCommandSessionWorker.mjs | 17 +++- tests/support/cli/cliFixture.ts | 95 +++++++++++++------ tests/support/cli/javascriptApplication.ts | 2 +- 86 files changed, 632 insertions(+), 548 deletions(-) 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",