From 1596d6a275e5569426f1932b5eb1327f0f877f72 Mon Sep 17 00:00:00 2001 From: morluto <76467478+morluto@users.noreply.github.com> Date: Sun, 8 Nov 2026 00:01:09 +0800 Subject: [PATCH] test: reduce wording coupling and prune redundant coverage --- bridge/pwndbg/test_core_context.py | 2 +- src/adb/AdbProvider.test.ts | 7 +- .../ApkSignatureAdmission.test.ts | 5 +- src/apkSignature/ApkSignatureProvider.test.ts | 6 +- src/apkSignature/apkSignatureFailures.test.ts | 4 +- src/apkSignature/apkV1Signers.test.ts | 4 +- src/apktool/ApktoolProvider.test.ts | 4 +- src/application/AppBundleResourceRoot.test.ts | 1 - src/application/ClientRegistration.test.ts | 1 - .../analysisView/AnalysisViewService.test.ts | 2 +- .../analysisView/BrowserScenarioViews.test.ts | 4 +- .../binary/AnalysisSnapshotCache.test.ts | 4 +- .../BinarySessionBundleResources.test.ts | 2 - .../binary/BinarySessionLiveness.test.ts | 4 +- .../binaryDiagnostics/AppleIpsService.test.ts | 1 - .../BinaryHardeningService.test.ts | 3 - .../binaryDiagnostics/ElfNotesService.test.ts | 1 - .../PeCertificatesService.test.ts | 1 - .../PeLoadConfigService.test.ts | 1 - .../decompileDirectory/decompileAll.test.ts | 2 - .../environmentArgumentMembers.test.ts | 1 - .../environmentReplayCapacity.test.ts | 1 - .../environment/environmentState.test.ts | 2 - src/application/go/GoBinaryService.test.ts | 2 - .../ElectronActiveObservationService.test.ts | 1 - .../ElectronObservationService.test.ts | 2 +- .../JavaScriptHtmlBaseResolution.test.ts | 8 +- ...ptSemanticGraphExpressionInfluence.test.ts | 30 +++--- .../python/PythonPycService.test.ts | 1 - .../rust/RustBinaryService.test.ts | 1 - .../EspFlashImageArtifactReader.test.ts | 2 +- src/artifacts/HexRecordArtifactReader.test.ts | 10 +- .../MachOSliceArtifactReader.test.ts | 6 +- src/artifacts/Uf2ArtifactReader.test.ts | 6 +- .../InterfaceBuilderDecodeBudget.test.ts | 4 +- .../apple/KeyedArchiveReader.test.ts | 8 +- src/artifacts/apple/MachoFileReads.test.ts | 2 +- src/domain/analysisErrorProjection.test.ts | 9 +- .../environmentManifestView.test.ts | 4 +- .../analysisView/environmentStateView.test.ts | 8 +- src/domain/analysisView/goBinaryView.test.ts | 1 - src/domain/analysisView/machoView.test.ts | 2 +- .../apiContract/parseApiContract.test.ts | 1 - src/domain/apple/appleApplication.test.ts | 8 +- src/domain/binaryTarget.mips.test.ts | 5 +- src/domain/binaryTarget.riscv.test.ts | 18 ---- src/domain/callPath.test.ts | 4 +- src/domain/evidence.test.ts | 2 +- src/domain/firmware/espFlashLayout.test.ts | 2 +- src/domain/firmware/espFlashPlan.test.ts | 7 +- src/domain/firmware/hexRecords.test.ts | 52 +++++----- src/domain/firmware/uf2.test.ts | 16 +++- src/domain/fixedAllowanceRemediation.test.ts | 6 +- ...riptSemanticAnalysis.require_links.test.ts | 6 +- src/domain/skillManifest.test.ts | 1 - src/domain/webModuleImports.test.ts | 6 +- src/dotnet/ManagedArtifactInspector.test.ts | 4 +- src/dotnet/ManagedCilDecoding.test.ts | 4 +- src/dotnet/ManagedMemberInspector.test.ts | 19 ++-- .../ManagedNativeBoundaryInspector.test.ts | 4 +- src/dotnet/ManagedSignatureResolution.test.ts | 4 +- .../devicetree/DevicetreeParser.test.ts | 10 +- src/ghidra/GhidraProvider.behavior.test.ts | 15 ++- src/ida/IdaCallGraph.test.ts | 22 +++-- src/ida/IdaCalleeRelationships.test.ts | 5 +- src/ida/IdaContainment.test.ts | 4 +- src/ida/IdaNativeProtocol.test.ts | 8 +- src/ida/IdaSessionClient.test.ts | 4 +- src/javascript/recovery/WakaruReport.test.ts | 4 +- src/native/apple/AppleIpsParser.test.ts | 8 +- src/native/elf/ElfNoteParser.test.ts | 22 ++--- src/native/elf/ElfNotesProvider.test.ts | 6 +- src/native/exports/NativeExportParser.test.ts | 10 +- src/native/parsers/plist.test.ts | 55 +++++++---- src/native/pe/PeCertificateParser.test.ts | 4 +- src/process/ArtifactAnalysisResources.test.ts | 8 +- src/process/ProcessOwnership.lineage.test.ts | 4 +- .../ProcessOwnership.observation.test.ts | 5 +- src/process/ProcessOwnership.part2.test.ts | 5 +- src/process/ProcessOwnership.part3.test.ts | 20 ++-- .../ProcessOwnership.validation.test.ts | 11 ++- src/python/PythonPycParser.test.ts | 95 +++++-------------- tests/acceptance/apkSignatureV1Cli.test.ts | 4 +- .../applicationVersionMatchingCli.test.ts | 15 ++- .../applicationWorkflowCli.test.ts | 11 ++- .../artifactComparisonCoverage.test.ts | 10 +- .../interfaceBuilderPlistNumbers.test.ts | 4 +- .../javascriptIndirectLoaderOwners.test.ts | 2 +- .../keyedArchiveScalarKinds.test.ts | 13 +-- .../mobileFrameworkConventions.test.ts | 4 +- .../nativeExportsVersionRequirements.test.ts | 5 +- .../packageSpecifierSuffixCli.test.ts | 2 +- .../artifactComparisonEspFlash.test.ts | 2 +- .../artifactComparisonPlist.test.ts | 4 +- .../artifactExtractionCoverage.test.ts | 20 ++-- tests/acceptance/devicetreeCli.test.ts | 6 +- tests/acceptance/elfNotesCli.test.ts | 10 +- tests/acceptance/managedCliOutput.test.ts | 4 +- tests/acceptance/managedSelectedScope.test.ts | 10 +- tests/acceptance/peCertificatesCli.test.ts | 8 +- tests/acceptance/rustBinaryCli.test.ts | 9 +- .../webSessionLifecycleWindow.test.ts | 6 +- ...BrowserProvider.lifecycle_failures.test.ts | 6 +- .../cdpObservationWindowBuffer.test.ts | 6 +- .../browser/cdpObservationWindowScope.test.ts | 10 +- tests/boundary/cli/analysisViewCli.test.ts | 6 +- tests/boundary/cli/argumentGuidance.test.ts | 3 +- .../cli/browserInputDiagnostics.test.ts | 8 +- tests/boundary/cli/dylibResolutionCli.test.ts | 9 +- tests/boundary/cli/electronCli.test.ts | 2 +- .../cli/explicitSensitiveArgumentsCli.test.ts | 3 +- .../cli/scenarioManifestCoverage.test.ts | 4 +- .../analysisSnapshotProvenance.test.ts | 8 +- .../filesystem/android/application.test.ts | 4 +- .../filesystem/appleApplication.test.ts | 4 +- .../artifactInventoryRegularRoot.test.ts | 2 +- .../filesystem/artifactZipIntegrity.test.ts | 6 +- .../filesystem/clientLifecycle.test.ts | 5 +- .../composedWorkflowEvidenceLinks.test.ts | 8 +- .../freebuffClientConfiguration.test.ts | 4 +- .../filesystem/interfaceBuilderBundle.test.ts | 16 ++-- .../electronApplicationAnalysis.test.ts | 2 +- .../javascriptModuleRelationships.test.ts | 4 +- .../filesystem/processCliFileInput.test.ts | 2 +- .../firmware/firmwareReportFiles.test.ts | 8 +- tests/boundary/go/goBinaryProvider.test.ts | 6 +- .../javascriptRuntimeObservation.test.ts | 2 +- .../javascriptSemanticTraceEvidence.test.ts | 8 +- tests/boundary/mcp/analysisViewMcp.test.ts | 10 +- tests/boundary/mcp/appleIpsMcp.test.ts | 4 +- tests/boundary/mcp/artifactMcp.test.ts | 2 +- ...browserScenarioInputDiagnosticsMcp.test.ts | 3 +- .../mcp/callPathSearchExtentMcp.test.ts | 4 +- tests/boundary/mcp/cutterMcp.test.ts | 8 +- tests/boundary/mcp/dylibResolutionMcp.test.ts | 9 +- tests/boundary/mcp/electronMcp.test.ts | 2 +- tests/boundary/mcp/elfNotesMcp.test.ts | 12 +-- .../enhancedTools.callPathsAndBatches.test.ts | 2 +- .../mcp/enhancedTools.limitations.test.ts | 2 +- .../mcp/functionComparisonCoverageMcp.test.ts | 8 +- .../mcp/gamecubeDolSessionMcp.test.ts | 5 +- tests/boundary/mcp/ghidraEvidenceMcp.test.ts | 24 +++-- .../boundary/mcp/ghidraFileOffsetMcp.test.ts | 2 +- tests/boundary/mcp/ghidraInventoryMcp.test.ts | 17 ++-- tests/boundary/mcp/ghidraNativeApiMcp.test.ts | 2 +- .../mcp/nativeMachoSummaryMcp.test.ts | 4 +- .../mcp/nativeUiPartialCallRoutesMcp.test.ts | 2 +- .../processComparisonDiagnosticsMcp.test.ts | 4 +- .../mcp/rizinDebuggerCancellationMcp.test.ts | 6 +- tests/boundary/mcp/rizinPendingOutput.test.ts | 6 +- tests/boundary/mcp/rustBinaryMcp.test.ts | 5 +- .../mcp/selectedSnapshotCleanupMcp.test.ts | 2 +- .../providers/CutterBridgeClient.test.ts | 8 +- .../providers/CutterBridgeService.test.ts | 4 +- .../RizinDebugSessionCoverage.part3.test.ts | 30 ++++-- ...RizinDebugSessionReviewRegressions.test.ts | 10 +- .../providers/RizinTimeoutEvidence.test.ts | 12 ++- .../cutterBridgeRequestLimit.test.ts | 2 +- .../providers/ida/calleeRelationships.test.ts | 3 +- ...anagedMemberComparisonFromMetadata.test.ts | 32 ++++--- ...actProvider.extractionCompleteness.test.ts | 11 +-- .../native/lipoFieldOwnership.test.ts | 4 +- .../providers/native/machoSliceReader.test.ts | 6 +- .../native/nativePlistTextSource.test.ts | 2 +- .../native/plistSourceFormat.test.ts | 2 +- 165 files changed, 633 insertions(+), 597 deletions(-) diff --git a/bridge/pwndbg/test_core_context.py b/bridge/pwndbg/test_core_context.py index 3e61455a91..42f09d901e 100644 --- a/bridge/pwndbg/test_core_context.py +++ b/bridge/pwndbg/test_core_context.py @@ -109,7 +109,7 @@ class CoreContextTests(unittest.TestCase): self.assertFalse(result["ok"]) self.assertEqual(result["reason"], "output-limit") self.assertNotIn("exceeds64", result["message"]) - self.assertEqual(result["message"], "Complete debugger context exceeds the 64 MiB reply budget.") + self.assertIn("64 MiB", result["message"]) self.assertEqual(reads, []) self.assertEqual(queries, ["maps"]) diff --git a/src/adb/AdbProvider.test.ts b/src/adb/AdbProvider.test.ts index 40bc2951fc..793c55cb4b 100644 --- a/src/adb/AdbProvider.test.ts +++ b/src/adb/AdbProvider.test.ts @@ -1998,7 +1998,6 @@ exit 1`, operation: "install_adb_package", input: { serial: "emulator-5554", apk_path: apk, replace: false }, }); - expect(execution.ok).toBe(true); if (!execution.ok) throw execution.error; expect(execution.value.result).toMatchObject({ bytes: 3, @@ -2006,8 +2005,10 @@ exit 1`, replace_requested: false, replaced: null, }); - expect(execution.value.limitations).toContain( - "Whether an existing package was replaced is unknown; replace_requested records only the adb install mode", + expect(execution.value.limitations).toContainEqual( + expect.stringMatching( + /package was replaced is unknown.*replace_requested.*install mode/iu, + ), ); const { binary: failing } = await writeAdbStub( diff --git a/src/apkSignature/ApkSignatureAdmission.test.ts b/src/apkSignature/ApkSignatureAdmission.test.ts index e5d481d709..b83e8fe150 100644 --- a/src/apkSignature/ApkSignatureAdmission.test.ts +++ b/src/apkSignature/ApkSignatureAdmission.test.ts @@ -33,8 +33,9 @@ it("bounds process-wide scans and waiting requests, cancels queued work and reco code: "resource_constraint", retryable: true, remediation: { - action: - "Wait for an active analysis to complete, then retry within the reported admission limits.", + action: expect.stringMatching( + /wait.*active analysis.*complete.*retry/iu, + ), }, }); expect(granted).toBe(false); diff --git a/src/apkSignature/ApkSignatureProvider.test.ts b/src/apkSignature/ApkSignatureProvider.test.ts index ef44e3bf99..0cfdec4d80 100644 --- a/src/apkSignature/ApkSignatureProvider.test.ts +++ b/src/apkSignature/ApkSignatureProvider.test.ts @@ -303,8 +303,10 @@ describe("ApkSignatureProvider v1 signers", () => { if (!result.ok) throw new Error(result.error.message); const report = apkSignatureResultSchema.parse(result.value.result); expect(report.posture.v1_signers_match_block_signers).toBeNull(); - expect(result.value.limitations).toContain( - "v1_signers_match_block_signers is null: the v3 signer's declared rotation lineage could not be decoded, so the original certificate v1 is expected to carry is unknown.", + expect(result.value.limitations).toContainEqual( + expect.stringMatching( + /v3.*rotation lineage.*could not be decoded.*original certificate.*unknown/iu, + ), ); }); diff --git a/src/apkSignature/apkSignatureFailures.test.ts b/src/apkSignature/apkSignatureFailures.test.ts index 3216b81158..147b67def1 100644 --- a/src/apkSignature/apkSignatureFailures.test.ts +++ b/src/apkSignature/apkSignatureFailures.test.ts @@ -56,9 +56,7 @@ it.each([ }); expect(projected.remediation.action).toContain(String(limit)); expect(projected.remediation.action).toContain("no runtime override"); - expect(projected.remediation.action).toContain( - "Repeating the unchanged request fails the same way", - ); + expect(projected.remediation.action).toMatch(/unchanged request.*fails/iu); expect(projected.remediation.action).not.toContain( "Wait for an active analysis to complete", ); diff --git a/src/apkSignature/apkV1Signers.test.ts b/src/apkSignature/apkV1Signers.test.ts index 359becbbc9..d2f7854780 100644 --- a/src/apkSignature/apkV1Signers.test.ts +++ b/src/apkSignature/apkV1Signers.test.ts @@ -75,8 +75,8 @@ describe("v1 signers identified by subjectKeyIdentifier", () => { expect(facts.certificate?.sha256_fingerprint).toMatch(/^[a-f0-9]{64}$/); expect(v1PostureFingerprint(facts)).toBeNull(); expect(v1SignerNotes([keyIdentified])).toEqual([ - expect.stringContaining( - "META-INF/CERT.RSA: a SignerInfo names its certificate by subjectKeyIdentifier", + expect.stringMatching( + /META-INF\/CERT\.RSA.*subjectKeyIdentifier.*apksig does not resolve/iu, ) as string, ]); const bySerial = v1SignerFacts(signerOf(LEAF_CERTIFICATE)); diff --git a/src/apktool/ApktoolProvider.test.ts b/src/apktool/ApktoolProvider.test.ts index c3fafe37ca..a79d2fcced 100644 --- a/src/apktool/ApktoolProvider.test.ts +++ b/src/apktool/ApktoolProvider.test.ts @@ -786,8 +786,8 @@ it("uses an owned APK snapshot and framework cache and resolves regional locales format: "apk", digest: { sha256: apk.sha256 }, }); - expect(observation.value.limitations).not.toContain( - "Artifact identity is unavailable for this observation.", + expect(observation.value.limitations).not.toContainEqual( + expect.stringMatching(/artifact identity.*unavailable/iu), ); expect(await readFile(apk.path, "utf8")).toBe("changed original"); const result = apktoolResultSchemas.decode_android_resources.parse( diff --git a/src/application/AppBundleResourceRoot.test.ts b/src/application/AppBundleResourceRoot.test.ts index fd1a716a08..b322c3d7d2 100644 --- a/src/application/AppBundleResourceRoot.test.ts +++ b/src/application/AppBundleResourceRoot.test.ts @@ -72,7 +72,6 @@ it.skipIf(process.platform === "win32")( await symlink(join(temp, "physical", "child"), join(temp, "alias")); const selected = `${temp}/alias/../Fixture.app`; const resolved = await resolveAppBundleResourceRoot(selected); - expect(resolved.ok).toBe(true); if (!resolved.ok) throw resolved.error; expect(resolved.value.callerPath).toBe(selected); expect(resolved.value.canonicalPath).toBe( diff --git a/src/application/ClientRegistration.test.ts b/src/application/ClientRegistration.test.ts index f7304020e4..c507359567 100644 --- a/src/application/ClientRegistration.test.ts +++ b/src/application/ClientRegistration.test.ts @@ -14,7 +14,6 @@ it("preserves every OpenCode environment key while normalizing its launcher", () '{"type":"local","command":["rea","mcp"],"environment":{"__proto__":"operator","constructor":"selected","normal":"value"}}', ); const result = parseClientRegistration(input, client); - expect(result.ok).toBe(true); if (!result.ok) throw result.error; expect(result.value.command).toBe("rea"); expect(result.value.args).toEqual(["mcp"]); diff --git a/src/application/analysisView/AnalysisViewService.test.ts b/src/application/analysisView/AnalysisViewService.test.ts index 0775951e6d..e60bf12000 100644 --- a/src/application/analysisView/AnalysisViewService.test.ts +++ b/src/application/analysisView/AnalysisViewService.test.ts @@ -362,7 +362,7 @@ it("reports inline Evidence identity failures at the complete source path", asyn { path: ["source", "evidence", "evidence_id"], reason: "invalid_value", - message: "Evidence semantic identifier does not match its record", + message: expect.stringMatching(/semantic identifier.*not match/iu), }, ], }, diff --git a/src/application/analysisView/BrowserScenarioViews.test.ts b/src/application/analysisView/BrowserScenarioViews.test.ts index bb48a39940..2ffc5397e0 100644 --- a/src/application/analysisView/BrowserScenarioViews.test.ts +++ b/src/application/analysisView/BrowserScenarioViews.test.ts @@ -86,8 +86,8 @@ describe("browser scenario analysis views", () => { const summary = await view(capture, { kind: "summary" }); expect(summary).toMatchObject({ limitations: [ - "Artifact identity is unavailable for this observation.", - "Browser host platform and architecture were not observed.", + expect.stringMatching(/artifact identity.*unavailable/iu), + expect.stringMatching(/platform.*architecture.*not observed/iu), ], summary: { limitation_count: 2 }, }); diff --git a/src/application/binary/AnalysisSnapshotCache.test.ts b/src/application/binary/AnalysisSnapshotCache.test.ts index c6de868fbb..6611ae8d46 100644 --- a/src/application/binary/AnalysisSnapshotCache.test.ts +++ b/src/application/binary/AnalysisSnapshotCache.test.ts @@ -1053,7 +1053,9 @@ describe("analysis snapshot cache partitioning", () => { ).toMatchObject({ provider: ANALYSIS_SNAPSHOT_PROVIDER, analysisProfile: ANALYSIS_SNAPSHOT_PROFILE, - limitations: [expect.stringContaining("local REA analysis snapshot")], + limitations: [ + expect.stringMatching(/snapshot.*did not re.run the provider/iu), + ], }); const changedProfile = createAnalysisProfile(ANALYSIS_SNAPSHOT_PROVIDER, { loader: "configured-override", diff --git a/src/application/binary/BinarySessionBundleResources.test.ts b/src/application/binary/BinarySessionBundleResources.test.ts index c5f7914f38..f135eb2884 100644 --- a/src/application/binary/BinarySessionBundleResources.test.ts +++ b/src/application/binary/BinarySessionBundleResources.test.ts @@ -132,7 +132,6 @@ it("opens a resource-only bundle without executable identity and keeps snapshot ); const opened = await session.openBundleResources(rootPath); - expect(opened.ok).toBe(true); if (!opened.ok) throw opened.error; expect(session.allowsSnapshotReplay("inspect_plist")).toBe(false); expect(opened.value.executableSha256).toBeNull(); @@ -837,7 +836,6 @@ it("cancels a binary switch during resource retirement and retains null-subject ); expect((await session.openBundleResources(rootPath)).ok).toBe(true); const observed = await session.execute("inspect_plist", {}); - expect(observed.ok).toBe(true); if (!observed.ok) throw observed.error; const recorded = session.recordEvidence( createEvidence(undefined, identity, { diff --git a/src/application/binary/BinarySessionLiveness.test.ts b/src/application/binary/BinarySessionLiveness.test.ts index 91b4ad06bf..37e44f2471 100644 --- a/src/application/binary/BinarySessionLiveness.test.ts +++ b/src/application/binary/BinarySessionLiveness.test.ts @@ -179,8 +179,8 @@ it("keeps unbound observations out of persisted target snapshots", async () => { expect( replay.value.sourceEvidence?.map((source) => source.evidence_id), ).toEqual([bound.value.evidenceId]); - expect(replay.value.limitations).toContain( - "Loaded from a local REA analysis snapshot; this call did not re-run the provider.", + expect(replay.value.limitations).toContainEqual( + expect.stringMatching(/snapshot.*did not re.run the provider/iu), ); }); diff --git a/src/application/binaryDiagnostics/AppleIpsService.test.ts b/src/application/binaryDiagnostics/AppleIpsService.test.ts index e7daf4329a..29ddea06df 100644 --- a/src/application/binaryDiagnostics/AppleIpsService.test.ts +++ b/src/application/binaryDiagnostics/AppleIpsService.test.ts @@ -175,7 +175,6 @@ it("records the selected report as a file subject", async () => { identity, inspect: () => Promise.resolve(ok(report)), }).inspect({ path: report.artifact.path }); - expect(result.ok).toBe(true); if (!result.ok) throw result.error; expect(result.value).toMatchObject({ confidence: "observed", diff --git a/src/application/binaryDiagnostics/BinaryHardeningService.test.ts b/src/application/binaryDiagnostics/BinaryHardeningService.test.ts index ee6c9d94bc..b2736ab2b9 100644 --- a/src/application/binaryDiagnostics/BinaryHardeningService.test.ts +++ b/src/application/binaryDiagnostics/BinaryHardeningService.test.ts @@ -69,7 +69,6 @@ const service = (value: BinaryHardening = report()) => it("returns derived Evidence for a multi-field ELF row", async () => { const value = report(); const result = await service(value).inspect({ path: value.artifact.path }); - expect(result.ok).toBe(true); if (!result.ok) throw result.error; expect(result.value).toMatchObject({ confidence: "derived", @@ -305,7 +304,6 @@ it("keeps sibling Mach-O slices when one range is empty or past EOF", async () = ], }); const result = await service(value).inspect({ path: value.artifact.path }); - expect(result.ok).toBe(true); if (!result.ok) throw result.error; expect(result.value.normalized_result).toMatchObject({ images: [ @@ -354,7 +352,6 @@ it("keeps a sibling when a failed slice declares an offset past 2^32", async () ], }); const result = await service(value).inspect({ path: value.artifact.path }); - expect(result.ok).toBe(true); if (!result.ok) throw result.error; expect(result.value.locations).toEqual([ { kind: "artifact-path", path: value.artifact.path }, diff --git a/src/application/binaryDiagnostics/ElfNotesService.test.ts b/src/application/binaryDiagnostics/ElfNotesService.test.ts index 1b51f0c763..75547fc619 100644 --- a/src/application/binaryDiagnostics/ElfNotesService.test.ts +++ b/src/application/binaryDiagnostics/ElfNotesService.test.ts @@ -53,7 +53,6 @@ it("records the selected ELF and an absent note table", async () => { identity, inspect: () => Promise.resolve(ok(report)), }).inspect({ path: report.artifact.path }); - expect(result.ok).toBe(true); if (!result.ok) throw result.error; expect(result.value).toMatchObject({ confidence: "observed", diff --git a/src/application/binaryDiagnostics/PeCertificatesService.test.ts b/src/application/binaryDiagnostics/PeCertificatesService.test.ts index f0bc7d12c4..5fb1b34213 100644 --- a/src/application/binaryDiagnostics/PeCertificatesService.test.ts +++ b/src/application/binaryDiagnostics/PeCertificatesService.test.ts @@ -72,7 +72,6 @@ it("retains complete certificate provenance from the production parser", async ( identity, inspect: () => Promise.resolve(ok(parsed)), }).inspect({ path: parsed.artifact.path }); - expect(result.ok).toBe(true); if (!result.ok) throw result.error; expect(result.value.normalized_result).toEqual(parsed); }); diff --git a/src/application/binaryDiagnostics/PeLoadConfigService.test.ts b/src/application/binaryDiagnostics/PeLoadConfigService.test.ts index 6882dea115..dd2de934ab 100644 --- a/src/application/binaryDiagnostics/PeLoadConfigService.test.ts +++ b/src/application/binaryDiagnostics/PeLoadConfigService.test.ts @@ -55,7 +55,6 @@ const service = (value: PeLoadConfig = report) => }); it("retains absent directories and artifact provenance", async () => { const result = await service().inspect({ path: report.artifact.path }); - expect(result.ok).toBe(true); if (!result.ok) throw result.error; expect(result.value).toMatchObject({ confidence: "observed", diff --git a/src/application/decompileDirectory/decompileAll.test.ts b/src/application/decompileDirectory/decompileAll.test.ts index f055ab427c..6e3d9263cd 100644 --- a/src/application/decompileDirectory/decompileAll.test.ts +++ b/src/application/decompileDirectory/decompileAll.test.ts @@ -399,7 +399,6 @@ describe("decompile_all inventory limitations", () => { directory(), { name: "Foo::", limit: 1 }, ); - expect(created.ok).toBe(true); if (!created.ok) throw created.error; expect(created.value).toMatchObject({ inventory_limitations: ["first inventory limitation"], @@ -433,7 +432,6 @@ describe("decompile_all inventory limitations", () => { directory(), { resume: true, name: "Bar::" }, ); - expect(resumed.ok).toBe(true); if (!resumed.ok) throw resumed.error; expect(resumed.value).toMatchObject({ inventory_limitations: ["second inventory limitation"], diff --git a/src/application/environment/environmentArgumentMembers.test.ts b/src/application/environment/environmentArgumentMembers.test.ts index 20352ddcef..8ba51dd637 100644 --- a/src/application/environment/environmentArgumentMembers.test.ts +++ b/src/application/environment/environmentArgumentMembers.test.ts @@ -71,7 +71,6 @@ it.each(["__proto__", "constructor"])( { source, store: serving.store }, { tool, plan, args }, ); - expect(result.ok).toBe(true); if (!result.ok) throw result.error; expect(Object.hasOwn(result.value.args, name)).toBe(true); expect(result.value.args[name]).toBe("selected"); diff --git a/src/application/environment/environmentReplayCapacity.test.ts b/src/application/environment/environmentReplayCapacity.test.ts index dcfdb595b9..841ef92e4a 100644 --- a/src/application/environment/environmentReplayCapacity.test.ts +++ b/src/application/environment/environmentReplayCapacity.test.ts @@ -41,7 +41,6 @@ it("bounds content-type display without changing body interpretation or retained (tool) => tool.name === "get_users_by_id", )!; const result = replayEnvironmentTool(source, tool, { id: "2" }); - expect(result.ok).toBe(true); if (!result.ok) throw result.error; expect(result.value.body).toEqual({ value: 1 }); expect( diff --git a/src/application/environment/environmentState.test.ts b/src/application/environment/environmentState.test.ts index 7c1799487d..9add67b4d8 100644 --- a/src/application/environment/environmentState.test.ts +++ b/src/application/environment/environmentState.test.ts @@ -748,11 +748,9 @@ it("retains arbitrary dictionary keys and owned JSON references at state intake" const entity = document.rea_state.entities[0]!; const fields = entity.fields; const owned = parseStateDocument(state, document, true); - expect(owned.ok).toBe(true); if (!owned.ok) throw owned.error; expect(owned.value.entities[0]?.fields).toBe(fields); const copied = parseStateDocument(state, document); - expect(copied.ok).toBe(true); if (!copied.ok) throw copied.error; expect(copied.value.entities[0]?.fields).not.toBe(fields); expect(copied.value.entities[0]?.fields).toEqual(fields); diff --git a/src/application/go/GoBinaryService.test.ts b/src/application/go/GoBinaryService.test.ts index efa65a4087..e3109ed1e2 100644 --- a/src/application/go/GoBinaryService.test.ts +++ b/src/application/go/GoBinaryService.test.ts @@ -74,7 +74,6 @@ it("preserves unsupported subject architectures and does not duplicate metadata" inspect: () => Promise.resolve(ok(report)), }); const result = await service.inspect({ path: report.artifact.path }); - expect(result.ok).toBe(true); if (!result.ok) throw result.error; expect(result.value.subject).toMatchObject({ architecture: null, @@ -120,7 +119,6 @@ it("preserves byte-valued build metadata and validates compiler source lengths", identity, inspect: () => Promise.resolve(ok(partial)), }).inspect({ path: report.artifact.path }); - expect(result.ok).toBe(true); if (!result.ok) throw result.error; expect(result.value.normalized_result).toEqual(partial); diff --git a/src/application/javascript/ElectronActiveObservationService.test.ts b/src/application/javascript/ElectronActiveObservationService.test.ts index 1f551e4fbf..11e24dd6c9 100644 --- a/src/application/javascript/ElectronActiveObservationService.test.ts +++ b/src/application/javascript/ElectronActiveObservationService.test.ts @@ -249,7 +249,6 @@ it.skipIf(process.platform === "win32")( application_path: canonicalApplication, application_root: await realpath(readable), }); - expect(result.ok).toBe(true); if (!result.ok) throw result.error; expect(result.value.parameters).toEqual({ ...input, diff --git a/src/application/javascript/ElectronObservationService.test.ts b/src/application/javascript/ElectronObservationService.test.ts index 5393957b0c..cfe606c9e4 100644 --- a/src/application/javascript/ElectronObservationService.test.ts +++ b/src/application/javascript/ElectronObservationService.test.ts @@ -188,7 +188,7 @@ it("retains completed target facts when cancellation arrives before Evidence pub isolation: "none", }, limitations: expect.arrayContaining([ - "Browser host platform and architecture were not observed.", + expect.stringMatching(/platform.*architecture.*not observed/iu), ]), normalized_result: targets, }); diff --git a/src/application/javascript/JavaScriptHtmlBaseResolution.test.ts b/src/application/javascript/JavaScriptHtmlBaseResolution.test.ts index 621e04117e..52b0d99c7a 100644 --- a/src/application/javascript/JavaScriptHtmlBaseResolution.test.ts +++ b/src/application/javascript/JavaScriptHtmlBaseResolution.test.ts @@ -140,9 +140,7 @@ describe("HTML base href empty segments", () => { it("keeps an above-root base escape when the reference also climbs", () => { expect(resolve("../app.js", "../../")).toMatchObject({ resolution_status: "rejected", - limitations: [ - "The resolved candidate escapes the canonical artifact root.", - ], + limitations: [expect.stringMatching(/escapes.*artifact root/iu)], }); }); }); @@ -160,9 +158,7 @@ describe("HTML base href path syntax", () => { it("still confines a base href that escapes the artifact root", () => { expect(resolve("app.js", "/a/../../secret/")).toMatchObject({ resolution_status: "rejected", - limitations: [ - "The resolved candidate escapes the canonical artifact root.", - ], + limitations: [expect.stringMatching(/escapes.*artifact root/iu)], }); }); it("reports an external base href as external rather than malformed", () => { diff --git a/src/application/javascript/JavaScriptSemanticGraphExpressionInfluence.test.ts b/src/application/javascript/JavaScriptSemanticGraphExpressionInfluence.test.ts index 11c36243c2..5b57d71daf 100644 --- a/src/application/javascript/JavaScriptSemanticGraphExpressionInfluence.test.ts +++ b/src/application/javascript/JavaScriptSemanticGraphExpressionInfluence.test.ts @@ -36,8 +36,12 @@ it.each([ it("explains an unmodeled dynamic call result at the reached argument", () => { const result = trace('const url = wrap(base + "/x"); fetch(url);'); - expect(result.unknowns.map((unknown) => unknown.detail)).toContain( - "The dynamic call's return dependency on this argument is unknown.", + expect(result.unknowns).toContainEqual( + expect.objectContaining({ + reason: "dynamic-call", + family: "data-flow", + relation_kinds: ["influences"], + }), ); }); @@ -48,20 +52,24 @@ it.each([ ])("keeps nested dynamic call return influence unknown: %s", (body) => { const result = trace(body); expect(result.status).not.toBe("found"); - expect(result.unknowns.map((unknown) => unknown.detail)).toContain( - "The dynamic call's return dependency on this argument is unknown.", + expect(result.unknowns).toContainEqual( + expect.objectContaining({ + reason: "dynamic-call", + family: "data-flow", + relation_kinds: ["influences"], + }), ); }); it("does not report an unused dynamic call result as a missing value", () => { const result = trace('consume(base + "/x");'); - expect( - result.unknowns.some( - (unknown) => - unknown.detail === - "The dynamic call's return dependency on this argument is unknown.", - ), - ).toBe(false); + expect(result.unknowns).not.toContainEqual( + expect.objectContaining({ + reason: "dynamic-call", + family: "data-flow", + relation_kinds: ["influences"], + }), + ); }); it("bounds operator production before graph admission and reports truncation", () => { diff --git a/src/application/python/PythonPycService.test.ts b/src/application/python/PythonPycService.test.ts index 950c4e3deb..90d2c5b282 100644 --- a/src/application/python/PythonPycService.test.ts +++ b/src/application/python/PythonPycService.test.ts @@ -94,7 +94,6 @@ it("records the selected artifact and the header plus marshal ranges", async () identity, inspect: () => Promise.resolve(ok(report)), }).inspect({ path: report.artifact.path }); - expect(result.ok).toBe(true); if (!result.ok) throw result.error; expect(result.value).toMatchObject({ confidence: "observed", diff --git a/src/application/rust/RustBinaryService.test.ts b/src/application/rust/RustBinaryService.test.ts index f145d9502c..f855482410 100644 --- a/src/application/rust/RustBinaryService.test.ts +++ b/src/application/rust/RustBinaryService.test.ts @@ -76,7 +76,6 @@ it("records the selected artifact and the dependency section range", async () => identity, inspect: () => Promise.resolve(ok(report)), }).inspect({ path: report.artifact.path }); - expect(result.ok).toBe(true); if (!result.ok) throw result.error; expect(result.value).toMatchObject({ confidence: "observed", diff --git a/src/artifacts/EspFlashImageArtifactReader.test.ts b/src/artifacts/EspFlashImageArtifactReader.test.ts index 896ec33cd0..87b6778446 100644 --- a/src/artifacts/EspFlashImageArtifactReader.test.ts +++ b/src/artifacts/EspFlashImageArtifactReader.test.ts @@ -199,7 +199,7 @@ describe("ESP flash layout coverage and selection", () => { ).toMatchObject({ declared_size: 0, limitations: [ - "partitions/ota_1.bin lies outside the dump: the file holds none of its 65536 bytes.", + expect.stringMatching(/partitions\/ota_1\.bin.*outside.*65536/iu), ], format_metadata: { index: 5, diff --git a/src/artifacts/HexRecordArtifactReader.test.ts b/src/artifacts/HexRecordArtifactReader.test.ts index 7b6721837e..408289bb6a 100644 --- a/src/artifacts/HexRecordArtifactReader.test.ts +++ b/src/artifacts/HexRecordArtifactReader.test.ts @@ -27,8 +27,8 @@ it("reports unknown coverage and termination facts as limitations", async () => ); expect(truncated.manifest.root_format).toBe("srec"); expect(truncated.coverage.status).toBe("unknown"); - expect(truncated.limitations).toContain( - "S-record has no termination record; the file may be truncated.", + expect(truncated.limitations).toContainEqual( + expect.stringMatching(/no termination record.*may be truncated/iu), ); expect(truncated.occurrences.map((row) => row.logical_path).sort()).toEqual([ ".", @@ -41,8 +41,10 @@ it("reports unknown coverage and termination facts as limitations", async () => ), ); expect(mismatched.coverage.status).toBe("complete"); - expect(mismatched.limitations).toContain( - "S-record termination S7 does not match the address width of S1 data records.", + expect(mismatched.limitations).toContainEqual( + expect.stringMatching( + /termination.*S7.*does not match.*address width.*S1/iu, + ), ); }); diff --git a/src/artifacts/MachOSliceArtifactReader.test.ts b/src/artifacts/MachOSliceArtifactReader.test.ts index e7d3bb1e7e..036ff4dc1b 100644 --- a/src/artifacts/MachOSliceArtifactReader.test.ts +++ b/src/artifacts/MachOSliceArtifactReader.test.ts @@ -529,8 +529,10 @@ describe("universal slices outside the structural index", () => { ["slices/ppc", 4096], ["slices/arm64", 8192], ]); - expect(entries[0]?.limitations).toContain( - "Structural Mach-O facts were unsupported (big-endian Mach-O slices are not supported); slice range is based on lipo output.", + expect(entries[0]?.limitations).toContainEqual( + expect.stringMatching( + /big.endian Mach.O.*not supported.*slice range.*lipo/iu, + ), ); }); diff --git a/src/artifacts/Uf2ArtifactReader.test.ts b/src/artifacts/Uf2ArtifactReader.test.ts index 37d7df4733..0ba9b85abd 100644 --- a/src/artifacts/Uf2ArtifactReader.test.ts +++ b/src/artifacts/Uf2ArtifactReader.test.ts @@ -120,8 +120,10 @@ it("leaves coverage unknown when numbered blocks are absent", async () => { ), ); expect(snapshot.coverage.status).toBe("unknown"); - expect(snapshot.limitations).toContain( - "UF2 numbering declares 1 block(s) that are absent: family 0xe48bff57 (RP2XXX_ABSOLUTE) with 2 numbered blocks has 1 of them.", + expect(snapshot.limitations).toContainEqual( + expect.stringMatching( + /1 block.*absent.*0xe48bff57.*2 numbered blocks.*1/iu, + ), ); expect( snapshot.occurrences diff --git a/src/artifacts/apple/InterfaceBuilderDecodeBudget.test.ts b/src/artifacts/apple/InterfaceBuilderDecodeBudget.test.ts index b32dee98fd..314ec82b9f 100644 --- a/src/artifacts/apple/InterfaceBuilderDecodeBudget.test.ts +++ b/src/artifacts/apple/InterfaceBuilderDecodeBudget.test.ts @@ -228,8 +228,8 @@ it("retains explicit date uncertainty for an overlapping binary plist object", a data: { $plist_type: "data", base64: "" }, }); expect(result.limitations).toContainEqual( - expect.stringContaining( - "Panel.nib: 1 date value(s) are reported with unknown timestamps because their sub-millisecond source records overlap", + expect.stringMatching( + /Panel\.nib: 1 date.*unknown timestamps.*sub.millisecond.*overlap/iu, ), ); }); diff --git a/src/artifacts/apple/KeyedArchiveReader.test.ts b/src/artifacts/apple/KeyedArchiveReader.test.ts index 9dd1ce6a9e..0bc815b1d9 100644 --- a/src/artifacts/apple/KeyedArchiveReader.test.ts +++ b/src/artifacts/apple/KeyedArchiveReader.test.ts @@ -178,7 +178,9 @@ describe("keyed archive scalar projection", () => { }), ); expect(graph.limitations).toContainEqual( - expect.stringContaining("sub-millisecond source records overlap"), + expect.stringMatching( + /1 date.*unknown timestamps.*sub.millisecond.*overlap/iu, + ), ); }); }); @@ -663,8 +665,8 @@ describe("binary keyed archive dates", () => { "2001-01-01T00:00:00.100Z", ); expect(graph.limitations).toContain(unknownDateLimitation(1)); - expect(graph.limitations.join("\n")).not.toContain( - "number metadata could not be completely observed", + expect(graph.limitations).not.toContainEqual( + expect.stringMatching(/number metadata.*could not.*observed/iu), ); expect(Buffer.compare(submillisecond, original)).toBe(0); expect(createHash("sha256").update(submillisecond).digest("hex")).toBe( diff --git a/src/artifacts/apple/MachoFileReads.test.ts b/src/artifacts/apple/MachoFileReads.test.ts index 771c3922b7..76393320a4 100644 --- a/src/artifacts/apple/MachoFileReads.test.ts +++ b/src/artifacts/apple/MachoFileReads.test.ts @@ -58,7 +58,7 @@ it("keeps structural Mach-O slice inventory across short descriptor reads", asyn ]); expect(shortReads).toBeGreaterThan(4); expect(entries[0]?.limitations).toEqual([ - "Slice range was derived from structural Mach-O facts because lipo omitted it.", + expect.stringMatching(/structural Mach.O.*lipo.*omitted/iu), ]); } finally { await reader.close(); diff --git a/src/domain/analysisErrorProjection.test.ts b/src/domain/analysisErrorProjection.test.ts index d85961b36e..17cd7f92b6 100644 --- a/src/domain/analysisErrorProjection.test.ts +++ b/src/domain/analysisErrorProjection.test.ts @@ -187,10 +187,12 @@ describe("agent recovery metadata", () => { operation: "inspect_analysis_view", }, remediation: { - action: - "Read the declared input schema or CLI usage for inspect_analysis_view; if using MCP and the catalog may be stale, call tools/list. Correct the listed fields, then retry. Do not repeat the unchanged call.", + action: expect.stringContaining("inspect_analysis_view"), }, }); + expect(projected.remediation.action).toContain("tools/list"); + expect(projected.remediation.action).toMatch(/correct.*fields.*retry/iu); + expect(projected.remediation.action).toMatch(/not repeat.*unchanged/iu); expect(analysisErrorProjectionSchema.parse(projected)).toEqual(projected); }); @@ -1039,8 +1041,7 @@ describe("analysis error projection: caller contract", () => { ]); expect(projected[1]).toMatchObject({ category: "unsupported_provider", - message: - "This analysis is unavailable for the current target. Choose another analysis or target.", + message: expect.stringMatching(/unavailable.*target/iu), details: { provider_id: "fixture", operation: "overview", diff --git a/src/domain/analysisView/environmentManifestView.test.ts b/src/domain/analysisView/environmentManifestView.test.ts index f14d9b2f38..806c1db38e 100644 --- a/src/domain/analysisView/environmentManifestView.test.ts +++ b/src/domain/analysisView/environmentManifestView.test.ts @@ -426,8 +426,8 @@ it("excludes other origins, pages, assets and non-JSON groups with reasons and f "/health:non-json", "/status:non-json", ]); - expect(mixed.limitations).toContain( - "1 retained records without a usable request line are not represented in the manifest.", + expect(mixed.limitations).toContainEqual( + expect.stringMatching(/1.*request line.*not represented.*manifest/iu), ); expect(mixed.records.map((record) => record.content_type)).toEqual([ "text/csv", diff --git a/src/domain/analysisView/environmentStateView.test.ts b/src/domain/analysisView/environmentStateView.test.ts index 80d18e6eff..f85149622c 100644 --- a/src/domain/analysisView/environmentStateView.test.ts +++ b/src/domain/analysisView/environmentStateView.test.ts @@ -1484,8 +1484,8 @@ it.each([ const before = structuredClone(capture); const state = stateOf(parentOf(analysisViewCaptureEvidence(capture))); expect(state.timeline).toBe("recorded-start-time"); - expect(state.limitations).toContain( - "Some records carry no usable request duration; their completion and overlap remain unknown.", + expect(state.limitations).toContainEqual( + expect.stringMatching(/request duration.*completion.*overlap.*unknown/iu), ); expect(state.documents).toEqual( expect.arrayContaining([ @@ -1534,8 +1534,8 @@ it("does not confirm completion of an untimed delete while preserving unrelated const before = structuredClone(result); const state = stateOf({ ...source, normalizedResult: result }); expect(state.timeline).toBe("recorded-start-time"); - expect(state.limitations).toContain( - "Some records carry no usable request duration; their completion and overlap remain unknown.", + expect(state.limitations).toContainEqual( + expect.stringMatching(/request duration.*completion.*overlap.*unknown/iu), ); expect(state.writes).toContainEqual( expect.objectContaining({ tool: "delete_items_by_id", served: false }), diff --git a/src/domain/analysisView/goBinaryView.test.ts b/src/domain/analysisView/goBinaryView.test.ts index a82c68f6ca..a641272419 100644 --- a/src/domain/analysisView/goBinaryView.test.ts +++ b/src/domain/analysisView/goBinaryView.test.ts @@ -65,7 +65,6 @@ it("bounds inherited messages while keeping their complete parent reference", () limitations: [note], }; const result = projectAnalysisView(input, { kind: "summary" }); - expect(result.ok).toBe(true); if (!result.ok) throw result.error; expect(result.value.limitations.join(" ")).toContain(input.evidenceId); expect(result.value.limitations.join(" ")).toContain("omitted"); diff --git a/src/domain/analysisView/machoView.test.ts b/src/domain/analysisView/machoView.test.ts index 13369b8988..5e918f08aa 100644 --- a/src/domain/analysisView/machoView.test.ts +++ b/src/domain/analysisView/machoView.test.ts @@ -440,7 +440,7 @@ describe("Mach-O analysis views", () => { issues: [ { path: ["view"], - message: "Mach-O inspection supports summary and macho views.", + message: expect.stringMatching(/summary and macho views/iu), }, ], }); diff --git a/src/domain/apiContract/parseApiContract.test.ts b/src/domain/apiContract/parseApiContract.test.ts index 7bad1c079c..2409a23d96 100644 --- a/src/domain/apiContract/parseApiContract.test.ts +++ b/src/domain/apiContract/parseApiContract.test.ts @@ -23,7 +23,6 @@ const parse = (value: unknown, source = artifact) => { }; const report = (value: unknown, source = artifact) => { const result = parse(value, source); - expect(result.ok).toBe(true); if (!result.ok) throw result.error; return apiContractSchema.parse(result.value); }; diff --git a/src/domain/apple/appleApplication.test.ts b/src/domain/apple/appleApplication.test.ts index 8910ef1a40..9afbe8cde6 100644 --- a/src/domain/apple/appleApplication.test.ts +++ b/src/domain/apple/appleApplication.test.ts @@ -301,8 +301,8 @@ describe("macOS application roots and bundle roles", () => { ); expect(result.application_roots).toEqual([]); expect(result.bundles).toEqual([]); - expect(result.limitations).toContain( - "No application bundle (Payload/*.app or *.app/Contents) was present in the supplied inventory pages.", + expect(result.limitations).toContainEqual( + expect.stringMatching(/no application bundle.*inventory/iu), ); }); }); @@ -896,7 +896,9 @@ describe("Apple path conventions", () => { expect(result.limitations).toEqual( expect.arrayContaining([ "Runtime families are inferred from inventory formats and bounded framework path conventions, not observed runtime loading.", - "A bridge basis is inferred from the native path and is repeated for every managed component.", + expect.stringMatching( + /bridge basis.*inferred.*native path.*every managed component/iu, + ), ]), ); }); diff --git a/src/domain/binaryTarget.mips.test.ts b/src/domain/binaryTarget.mips.test.ts index ded2f18bfe..bfce7d7823 100644 --- a/src/domain/binaryTarget.mips.test.ts +++ b/src/domain/binaryTarget.mips.test.ts @@ -67,8 +67,7 @@ describe("MIPS ELF header boundaries", () => { const parsed = parseExecutableHeader(header(true).subarray(0, 39), "x64"); expect(parsed).toEqual({ ok: false, - error: - "truncated ELF header: an ELF32 header needs 52 bytes; the file has 39", + error: expect.stringMatching(/truncated ELF header.*52 bytes.*39/iu), }); }); @@ -77,7 +76,7 @@ describe("MIPS ELF header boundaries", () => { bytes[5] = 0; expect(parseExecutableHeader(bytes, "x64")).toEqual({ ok: false, - error: "unsupported ELF endianness", + error: expect.stringMatching(/unsupported ELF endianness/iu), }); }); }); diff --git a/src/domain/binaryTarget.riscv.test.ts b/src/domain/binaryTarget.riscv.test.ts index 8d168d9356..66586aca30 100644 --- a/src/domain/binaryTarget.riscv.test.ts +++ b/src/domain/binaryTarget.riscv.test.ts @@ -73,24 +73,6 @@ describe("RISC-V ELF header boundaries", () => { }); }); - it("rejects a truncated header before reading flags", () => { - const parsed = parseExecutableHeader(header(true).subarray(0, 39), "x64"); - expect(parsed).toEqual({ - ok: false, - error: - "truncated ELF header: an ELF32 header needs 52 bytes; the file has 39", - }); - }); - - it("does not turn invalid byte order into little-endian RISC-V", () => { - const bytes = header(true); - bytes[5] = 0; - expect(parseExecutableHeader(bytes, "x64")).toEqual({ - ok: false, - error: "unsupported ELF endianness", - }); - }); - it("keeps an unknown e_machine outside the recognized family set", () => { expect(parseExecutableHeader(header(true, 32, 0x5, 2, 244), "x64")).toEqual( { diff --git a/src/domain/callPath.test.ts b/src/domain/callPath.test.ts index 238cd5add9..9d83ed5821 100644 --- a/src/domain/callPath.test.ts +++ b/src/domain/callPath.test.ts @@ -206,8 +206,8 @@ describe("call path reconstruction", () => { ).toBe(false); const unknown = run([observe("0x1000", ["0x2000"])]); expect(unknown.status).toBe("unknown"); - expect(unknown.limitations).toContain( - "No analyze_function Evidence covers reachable function 0x2000", + expect(unknown.limitations).toContainEqual( + expect.stringMatching(/no analyze_function.*0x2000/iu), ); const limitedEmpty = run([ diff --git a/src/domain/evidence.test.ts b/src/domain/evidence.test.ts index 12e1f5d639..dc51837a9c 100644 --- a/src/domain/evidence.test.ts +++ b/src/domain/evidence.test.ts @@ -680,7 +680,7 @@ it("binds an explicit unknown-subject reason into the semantic Evidence identity expect(explicit.subject).toBeNull(); expect(explicit.limitations).toEqual([reason]); expect(defaulted.limitations).toEqual([ - "Artifact identity is unavailable for this observation.", + expect.stringMatching(/artifact identity.*unavailable/iu), ]); expect(explicit.evidence_id).not.toBe(defaulted.evidence_id); expect(parseEvidence(explicit)).toEqual(explicit); diff --git a/src/domain/firmware/espFlashLayout.test.ts b/src/domain/firmware/espFlashLayout.test.ts index 7b6a210a67..330b3f5b40 100644 --- a/src/domain/firmware/espFlashLayout.test.ts +++ b/src/domain/firmware/espFlashLayout.test.ts @@ -92,7 +92,7 @@ describe("ESP partition tables", () => { expect(decoded.md5.status).toBe("invalid"); expect(decoded.partitions).toHaveLength(6); expect(decoded.limitations).toEqual([ - "The partition table MD5 at 0xc0 does not match the entries before it; ESP-IDF's bootloader rejects the table.", + expect.stringMatching(/MD5.*0xc0.*does not match.*bootloader rejects/iu), ]); }); diff --git a/src/domain/firmware/espFlashPlan.test.ts b/src/domain/firmware/espFlashPlan.test.ts index 34086146c9..c73317421e 100644 --- a/src/domain/firmware/espFlashPlan.test.ts +++ b/src/domain/firmware/espFlashPlan.test.ts @@ -318,6 +318,9 @@ describe("ESP flash layout otadata", () => { partitionTable(TWO_OTA_PARTITIONS, { md5: "corrupt" }), ); if (!decoded.ok) throw new Error(decoded.error); + expect(decoded.value.limitations).toEqual([ + expect.stringMatching(/MD5.*0xc0.*does not match.*bootloader rejects/iu), + ]); const plan = planEspFlashLayout(input({ table: decoded.value })); const facts = finishEspFlashLayout(plan, { otadataRecords: [otaRecord({ seq: 1 }), Buffer.alloc(32, 0xff)], @@ -328,8 +331,8 @@ describe("ESP flash layout otadata", () => { partition_index: null, assumptions: [], }); - expect(facts.limitations).toContain( - "The partition table MD5 at 0xc0 does not match the entries before it; ESP-IDF's bootloader rejects the table.", + expect(facts.limitations).toEqual( + expect.arrayContaining([...decoded.value.limitations]), ); }); diff --git a/src/domain/firmware/hexRecords.test.ts b/src/domain/firmware/hexRecords.test.ts index e24bce67dc..1479c60dbd 100644 --- a/src/domain/firmware/hexRecords.test.ts +++ b/src/domain/firmware/hexRecords.test.ts @@ -146,66 +146,62 @@ describe("Intel HEX decoding", () => { [ "checksum", INTEL_HEX_I32_GAP.replace("BEEFC3", "BEEFC4"), - "line 4: record type 00 checksum 0xc4 does not match computed 0xc3", + /line 4.*00.*checksum.*0xc4.*0xc3/iu, ], [ "unknown type", INTEL_HEX_I32_GAP.replace(":00000001FF", ":00000006FA\n:00000001FF"), - "line 6: unknown record type 06", + /line 6.*unknown.*06/iu, ], - ["odd length", ":0000000\n", "line 1: odd number of hex digits (7)"], - ["non-hex", ":00000001FG\n", "line 1: invalid character 0x47 at column 11"], + ["odd length", ":0000000\n", /line 1.*odd.*hex digits.*7/iu], + ["non-hex", ":00000001FG\n", /line 1.*invalid.*0x47.*column 11/iu], [ "lone CR", ":00000001FF\r:00000001FF\n", - "line 1: invalid character 0x0d at column 12", - ], - [ - "byte count", - ":0200000001FD\n", - "line 1: record type 00 byte count 0x02 declares 2 data bytes, but the line holds 1", + /line 1.*invalid.*0x0d.*column 12/iu, ], + ["byte count", ":0200000001FD\n", /line 1.*00.*0x02.*2 data bytes.*1/iu], [ "fixed length", `${intelHexRecord(4, 0, [8])}\n`, - "line 1: record type 04 requires 2 data bytes, found 1", + /line 1.*04.*2 data bytes.*1/iu, ], [ "extended address with a non-zero address field", ":020001040800F1\n", - "line 1: record type 04 requires address 0x0000, found 0x0001", + /line 1.*04.*0x0000.*0x0001/iu, ], [ "segment address with a non-zero address field", `${intelHexRecord(2, 0x10, [0x10, 0x00])}\n`, - "line 1: record type 02 requires address 0x0000, found 0x0010", + /line 1.*02.*0x0000.*0x0010/iu, ], [ "offset wrap", ":020000040000FA\n:10FFF800000102030405060708090A0B0C0D0E0F81\n:00000001FF\n", - "line 2: data at offset 0xfff8 with 16 bytes wraps past 0xffff", + /line 2.*0xfff8.*16 bytes.*wraps.*0xffff/iu, ], [ "record after EOF", `${INTEL_HEX_I8}:00000001FF\n`, - "line 3: record follows the termination record at line 2", + /line 3.*termination.*line 2/iu, ], [ "conflicting start", `${intelHexRecord(5, 0, [0, 0, 0, 1])}\n${intelHexRecord(5, 0, [0, 0, 0, 2])}\n`, - "line 2: start address 0x00000002 conflicts with 0x00000001 declared at line 1", + /line 2.*0x00000002.*conflicts.*0x00000001.*line 1/iu, ], [ "I16HEX above 0xfffff", `${intelHexRecord(2, 0, [0xff, 0xff])}\n${intelHexRecord(0, 0x20, [1])}\n`, - "line 2: I16HEX data at 0x00100010 with 1 bytes exceeds the 20-bit address 0xfffff", + /line 2.*I16HEX.*0x00100010.*1 bytes.*20.bit.*0xfffff/iu, ], ])("fails closed on %s", (_name, text, message) => { const decoded = decode(text); expect(decoded.ok).toBe(false); if (decoded.ok) return; expect(decoded.error.kind).toBe("format"); - expect(decoded.error.message).toContain(message); + expect(decoded.error.message).toMatch(message); }); }); @@ -326,7 +322,9 @@ describe("S-record decoding", () => { variant: "S19", }); expect(decoded.limitations).toEqual([ - "S-record termination S7 does not match the address width of S1 data records.", + expect.stringMatching( + /termination.*S7.*does not match.*address width.*S1/iu, + ), ]); }); @@ -334,7 +332,7 @@ describe("S-record decoding", () => { const decoded = image(SREC_S19.replace("S9030000FC\n", ""), "srec"); expect(decoded.terminated).toBe(false); expect(decoded.limitations).toEqual([ - "S-record has no termination record; the file may be truncated.", + expect.stringMatching(/no termination record.*may be truncated/iu), ]); }); @@ -342,33 +340,33 @@ describe("S-record decoding", () => { [ "count mismatch", SREC_S19.replace("S5030002FA", "S5030003F9"), - "line 4: record type S5 declares 3 data records, but 2 precede it", + /line 4.*S5.*3 data records.*2/iu, ], - ["reserved S4", "S4030000FC\n", "line 1: reserved record type S4"], + ["reserved S4", "S4030000FC\n", /line 1.*reserved.*S4/iu], [ "second header", `${SREC_S19.split("\n")[0] ?? ""}\n${SREC_S19}`, - "line 2: second S0 header record; the first is at line 1", + /line 2.*second S0.*line 1/iu, ], [ "S9 with data", `${srecRecord(9, 0, [1])}\n`, - "line 1: record type S9 byte count 0x04 does not fit its 2-byte address and checksum", + /line 1.*S9.*0x04.*2.byte address.*checksum/iu, ], [ "S1 crossing 0xffff", `${srecRecord(1, 0xffff, [1, 2])}\n`, - "line 1: record type S1 data at 0x0000ffff with 2 bytes crosses 0xffff", + /line 1.*S1.*0x0000ffff.*2 bytes.*crosses 0xffff/iu, ], [ "S3 crossing 0xffffffff", `${srecRecord(3, 0xffff_ffff, [1, 2])}\n`, - "crosses 0xffffffff", + /crosses 0xffffffff/iu, ], ])("fails closed on %s", (_name, text, message) => { const decoded = decode(text, "srec"); expect(decoded.ok).toBe(false); - if (!decoded.ok) expect(decoded.error.message).toContain(message); + if (!decoded.ok) expect(decoded.error.message).toMatch(message); }); it("bounds line length at the format maximum", () => { diff --git a/src/domain/firmware/uf2.test.ts b/src/domain/firmware/uf2.test.ts index 9719b2470b..ff5fb41070 100644 --- a/src/domain/firmware/uf2.test.ts +++ b/src/domain/firmware/uf2.test.ts @@ -222,7 +222,9 @@ it("keeps families apart even when they target the same addresses", () => { }); expect(decoded.complete).toBe(false); expect(decoded.limitations).toEqual([ - "UF2 numbering declares 1 block(s) that are absent: family 0xe48bff57 (RP2XXX_ABSOLUTE) with 2 numbered blocks has 1 of them.", + expect.stringMatching( + /1 block.*absent.*0xe48bff57.*2 numbered blocks.*1/iu, + ), ]); }); @@ -262,7 +264,9 @@ it("reports numbered blocks that are absent and leaves coverage unknown", () => region_count: 2, }); expect(decoded.limitations).toEqual([ - "UF2 numbering declares 1 block(s) that are absent: family 0xe48bff56 (RP2040) with 4 numbered blocks has 3 of them.", + expect.stringMatching( + /1 block.*absent.*0xe48bff56.*4 numbered blocks.*3/iu, + ), ]); }); @@ -415,9 +419,11 @@ it("excludes invalid blocks and a truncated final block, leaving coverage unknow }); expect(decoded.complete).toBe(false); expect(decoded.limitations).toEqual([ - "UF2 file ends with 100 bytes after its last complete 512-byte block; the file may be truncated.", - "3 UF2 block(s) are invalid and were excluded; the first is block 1 at byte offset 512: end magic number does not match.", - "UF2 numbering declares 1 block(s) that are absent: family 0xe48bff56 (RP2040) with 3 numbered blocks has 2 of them.", + expect.stringMatching(/100 bytes.*512.byte block.*may be truncated/iu), + expect.stringMatching(/3 UF2 block.*invalid.*block 1.*offset 512.*magic/iu), + expect.stringMatching( + /1 block.*absent.*0xe48bff56.*3 numbered blocks.*2/iu, + ), ]); }); diff --git a/src/domain/fixedAllowanceRemediation.test.ts b/src/domain/fixedAllowanceRemediation.test.ts index e2214f1804..c3e26b2859 100644 --- a/src/domain/fixedAllowanceRemediation.test.ts +++ b/src/domain/fixedAllowanceRemediation.test.ts @@ -116,9 +116,9 @@ describe("fixed allowance recovery", () => { expect(projected.remediation.action).toContain( "live output may change between requests", ); - if (kind === "capture") - expect(projected.remediation.action).toContain( - "Repeating the unchanged request fails the same way.", + if (kind === "capture" || kind === "count") + expect(projected.remediation.action).toMatch( + /unchanged request.*fails/iu, ); }, ); diff --git a/src/domain/javascript/javascriptSemanticAnalysis.require_links.test.ts b/src/domain/javascript/javascriptSemanticAnalysis.require_links.test.ts index 675234dec6..79798fa782 100644 --- a/src/domain/javascript/javascriptSemanticAnalysis.require_links.test.ts +++ b/src/domain/javascript/javascriptSemanticAnalysis.require_links.test.ts @@ -196,8 +196,10 @@ describe("TypeScript type-only module links", () => { [null, "LocalInterface", "LocalInterface", null], [null, "hiddenFunction", "hiddenFunction", null], ]); - expect(ir.limitations).toContain( - "Compiler options are unavailable; inline type-only imports or re-exports may preserve a module side-effect dependency.", + expect(ir.limitations).toContainEqual( + expect.stringMatching( + /compiler options.*unavailable.*type.only.*side.effect/iu, + ), ); }); }); diff --git a/src/domain/skillManifest.test.ts b/src/domain/skillManifest.test.ts index 5b475ae80d..9b374d0864 100644 --- a/src/domain/skillManifest.test.ts +++ b/src/domain/skillManifest.test.ts @@ -85,7 +85,6 @@ it("updates bounded frontmatter without changing Markdown examples or comments", '\n# Instructions\n\n```yaml\nmetadata:\n version: "body-only"\n tool_count: 99\n```\n'; const content = manifest(" version: '49' # keep version comment\n", body); const generated = addSkillManifestToolCount(content, 7); - expect(generated.ok).toBe(true); if (!generated.ok) throw generated.error; expect(generated.value.endsWith(body)).toBe(true); expect(generated.value).toContain("version: '49' # keep version comment"); diff --git a/src/domain/webModuleImports.test.ts b/src/domain/webModuleImports.test.ts index e02998203b..2ef7eba144 100644 --- a/src/domain/webModuleImports.test.ts +++ b/src/domain/webModuleImports.test.ts @@ -35,8 +35,10 @@ describe("native module syntax observations", () => { "only-types", "mixed", ]); - expect(result.diagnostics).toContain( - "Compiler options are unavailable; inline type-only imports or re-exports may preserve a module side-effect dependency.", + expect(result.diagnostics).toContainEqual( + expect.stringMatching( + /compiler options.*unavailable.*type.only.*side.effect/iu, + ), ); }); it("keeps JavaScript relational imports distinct from TypeScript import types", () => { diff --git a/src/dotnet/ManagedArtifactInspector.test.ts b/src/dotnet/ManagedArtifactInspector.test.ts index d66ab29c08..f3a7fd8fd9 100644 --- a/src/dotnet/ManagedArtifactInspector.test.ts +++ b/src/dotnet/ManagedArtifactInspector.test.ts @@ -443,8 +443,8 @@ describe("managed artifact coded indexes", () => { expect.objectContaining({ code: "invalid-row", scope: "metadata.ManifestResource:0x28000001", - detail: expect.stringContaining( - "coded index 0x9 is invalid: coded index selects row 2 in table 35, which has 1 rows", + detail: expect.stringMatching( + /0x9.*invalid.*row 2.*table 35.*1 rows/iu, ), }), ], diff --git a/src/dotnet/ManagedCilDecoding.test.ts b/src/dotnet/ManagedCilDecoding.test.ts index ade7734cc5..80ae25ffd7 100644 --- a/src/dotnet/ManagedCilDecoding.test.ts +++ b/src/dotnet/ManagedCilDecoding.test.ts @@ -402,7 +402,9 @@ describe("managed CIL prefix completion", () => { decoded_instruction_count: 1, opcode_counts: { "volatile.": 1 }, normalized_il_sha256: null, - issue: "CIL prefix chain at IL offset 0 has no following instruction", + issue: expect.stringMatching( + /CIL prefix.*offset 0.*no following instruction/iu, + ), }); }); diff --git a/src/dotnet/ManagedMemberInspector.test.ts b/src/dotnet/ManagedMemberInspector.test.ts index 5801de7ee5..5ef32b21ed 100644 --- a/src/dotnet/ManagedMemberInspector.test.ts +++ b/src/dotnet/ManagedMemberInspector.test.ts @@ -837,8 +837,9 @@ describe("managed TypeSpec declaring names", () => { { code: "invalid-row", scope: "metadata.TypeSpec:0x1b000001", - detail: - "TypeDefOrRefOrSpec operand 0x25 is invalid: coded index selects row 9 in table 1, which has 1 rows", + detail: expect.stringMatching( + /0x25.*invalid.*row 9.*table 1.*1 rows/iu, + ), }, ], }); @@ -957,10 +958,10 @@ describe("managed TypeRef resolution scopes", () => { { code: "invalid-row", scope: "metadata.TypeRef:0x01000001", - detail: expect.stringContaining( + detail: expect.stringMatching( enclosingRow === 0 - ? "coded index selects row zero in table 1" - : "coded index selects row 9 in table 1, which has 1 rows", + ? /row zero.*table 1/iu + : /row 9.*table 1.*1 rows/iu, ), }, ]); @@ -2420,8 +2421,8 @@ describe("managed metadata reference bounds", () => { { code: "invalid-row", scope: "metadata.TypeDef:0x02000001", - detail: expect.stringContaining( - "TypeDef Extends coded index 0xc is invalid: coded index selects row 3 in table 2, which has 1 rows", + detail: expect.stringMatching( + /TypeDef Extends.*0xc.*invalid.*row 3.*table 2.*1 rows/iu, ), }, ], @@ -2449,8 +2450,8 @@ describe("managed metadata reference bounds", () => { { code: "invalid-row", scope: "metadata.MemberRef:0x0a000001", - detail: expect.stringContaining( - "MemberRef parent coded index 0x28 is invalid: coded index selects row 5 in table 2, which has 1 rows", + detail: expect.stringMatching( + /MemberRef parent.*0x28.*invalid.*row 5.*table 2.*1 rows/iu, ), }, ], diff --git a/src/dotnet/ManagedNativeBoundaryInspector.test.ts b/src/dotnet/ManagedNativeBoundaryInspector.test.ts index adce48d8ad..a49e4b2939 100644 --- a/src/dotnet/ManagedNativeBoundaryInspector.test.ts +++ b/src/dotnet/ManagedNativeBoundaryInspector.test.ts @@ -136,8 +136,8 @@ describe("managed native boundary coded indexes", () => { expect.objectContaining({ code: "invalid-row", scope: "metadata.ImplMap:0x1c000001", - detail: expect.stringContaining( - "coded index 0x5 is invalid: coded index selects row 2 in table 6, which has 1 rows", + detail: expect.stringMatching( + /0x5.*invalid.*row 2.*table 6.*1 rows/iu, ), }), ], diff --git a/src/dotnet/ManagedSignatureResolution.test.ts b/src/dotnet/ManagedSignatureResolution.test.ts index 21dfbc45b8..974359945f 100644 --- a/src/dotnet/ManagedSignatureResolution.test.ts +++ b/src/dotnet/ManagedSignatureResolution.test.ts @@ -346,8 +346,8 @@ describe("type operand names", () => { }); expect(inspection.fields[0]?.signature.parse_status).toBe("malformed"); - expect(inspection.fields[0]?.signature.issue).toContain( - "coded index selects row 9 in table 1, which has 1 rows", + expect(inspection.fields[0]?.signature.issue).toMatch( + /row 9.*table 1.*1 rows/iu, ); expect(inspection.fields[0]?.signature.resolved_sha256).toBeNull(); expect(inspection.coverage.state).toBe("partial"); diff --git a/src/firmware/devicetree/DevicetreeParser.test.ts b/src/firmware/devicetree/DevicetreeParser.test.ts index d0635a236e..6f2afe8dc4 100644 --- a/src/firmware/devicetree/DevicetreeParser.test.ts +++ b/src/firmware/devicetree/DevicetreeParser.test.ts @@ -181,7 +181,9 @@ it("leaves conflicting phandle declarations unresolved", () => { [3, "/same"], ]); expect(r.limitations.filter((s) => s.includes("conflicts"))).toEqual([ - "phandle at /split conflicts with linux,phandle (0x1 vs 0x2); references to either value are unresolved.", + expect.stringMatching( + /\/split.*conflicts.*linux,phandle.*0x1.*0x2.*unresolved/iu, + ), ]); }); it.each([ @@ -211,8 +213,10 @@ it.each([ hex: canonical.toString("hex"), derived: { cells: null }, }); - expect(r.limitations).toContain( - "phandle at /target is not a single cell; this node's reference identity remains unresolved.", + expect(r.limitations).toContainEqual( + expect.stringMatching( + /phandle.*\/target.*not a single cell.*unresolved/iu, + ), ); }, ); diff --git a/src/ghidra/GhidraProvider.behavior.test.ts b/src/ghidra/GhidraProvider.behavior.test.ts index f68d509e53..6b3e87d74f 100644 --- a/src/ghidra/GhidraProvider.behavior.test.ts +++ b/src/ghidra/GhidraProvider.behavior.test.ts @@ -9,6 +9,7 @@ import { ghidraEmptyInstructionOutgoingLimitation, ghidraEmptyOutgoingReferencesLimitation, ghidraEmptyXrefsLimitation, + limitationsFor, } from "./GhidraProviderCapabilities.js"; import { AnalysisProviderRegistry } from "../application/binary/AnalysisProviderRegistry.js"; import { composeBinarySession } from "../application/binary/BinarySessionComposition.js"; @@ -281,6 +282,17 @@ it.each([ }, ); +it("states the fresh-session and pseudocode boundaries", () => { + const functionNotes = limitationsFor("analyze_function").join("\n"); + expect(functionNotes).toMatch(/auto.analysis.*complete/iu); + expect(functionNotes).toMatch( + /original executable bytes (?:are unchanged|are never written|remain unmodified)/iu, + ); + const pseudocodeNotes = limitationsFor("procedure_pseudo_code").join("\n"); + expect(pseudocodeNotes).toMatch(/not original source/iu); + expect(pseudocodeNotes).toMatch(/not text.equivalent to Hopper/iu); +}); + it("carries the inspected release identity into the production launcher", async () => { const launchers: GhidraHeadlessLauncher[] = []; const ghidra = provider( @@ -1641,8 +1653,7 @@ describe("Ghidra file-offset outcome projection", () => { if (result.ok) throw new Error("Expected an input refusal"); expect(projectAnalysisError(result.error)).toMatchObject({ code: "invalid_request", - message: - "Analysis input is invalid. Check the arguments and try again.", + message: expect.stringMatching(/input.*invalid/iu), recovery: { kind: "inspect_input_contract", operation }, details: { issues: [ diff --git a/src/ida/IdaCallGraph.test.ts b/src/ida/IdaCallGraph.test.ts index e39c7358d7..9d533523c3 100644 --- a/src/ida/IdaCallGraph.test.ts +++ b/src/ida/IdaCallGraph.test.ts @@ -174,8 +174,8 @@ describe("reading root callees from the processor-neutral call graph", () => { ["lookup_funcs", { queries: ["0x20", "0x100", "0x300"] }], ]); expect(result.limitations).toContain(UNRECOGNIZED_CALLEE_LIMITATION); - expect(result.limitations).toContain( - "IDA observed callee address 0x300 but could not resolve its name: Not found; the address is retained.", + expect(result.limitations).toContainEqual( + expect.stringMatching(/0x300.*could not resolve.*Not found.*retained/iu), ); }); @@ -190,8 +190,10 @@ describe("reading root callees from the processor-neutral call graph", () => { result.callees, "the graph address remains a real observation", ).toEqual([{ address: "0x20", name: "0x20" }]); - expect(result.limitations).toContain( - "IDA observed callee addresses but the name lookup failed (lookup reply failed); the addresses are retained.", + expect(result.limitations).toContainEqual( + expect.stringMatching( + /lookup failed.*lookup reply failed.*addresses are retained/iu, + ), ); }); @@ -225,8 +227,10 @@ describe("call graph coverage and failure handling", () => { expect( result.limitations, "each truncation signal counts on its own", - ).toContain( - "Upstream callgraph was truncated; additional callees remain unknown.", + ).toContainEqual( + expect.stringMatching( + /callgraph was truncated.*callees remain unknown/iu, + ), ); }); @@ -251,8 +255,10 @@ describe("call graph coverage and failure handling", () => { expect( result.limitations, "reaching the cap is uncertain, so it is reported as possible truncation", - ).toContain( - "Upstream callgraph may be truncated; additional callees may remain unknown.", + ).toContainEqual( + expect.stringMatching( + /callgraph may be truncated.*callees may remain unknown/iu, + ), ); }); diff --git a/src/ida/IdaCalleeRelationships.test.ts b/src/ida/IdaCalleeRelationships.test.ts index 358c83b886..9fddc44125 100644 --- a/src/ida/IdaCalleeRelationships.test.ts +++ b/src/ida/IdaCalleeRelationships.test.ts @@ -189,8 +189,9 @@ describe("headless IDA callee edges on x86 targets", () => { ]); expect(execution.relationshipCoverage).toMatchObject({ status: "unknown", - reason: - "Upstream callees omitted an explicit truncation marker; additional callees remain unknown.", + reason: expect.stringMatching( + /omitted.*truncation marker.*callees remain unknown/iu, + ), }); } finally { expect((await client.close()).ok).toBe(true); diff --git a/src/ida/IdaContainment.test.ts b/src/ida/IdaContainment.test.ts index 7562afda45..1623ce2938 100644 --- a/src/ida/IdaContainment.test.ts +++ b/src/ida/IdaContainment.test.ts @@ -225,7 +225,9 @@ describe("headless IDA containing procedure", () => { classification: null, body: { available: false, - reason: "IDA MCP does not report complete function body ranges.", + reason: expect.stringMatching( + /does not report complete function body ranges/iu, + ), }, }, }); diff --git a/src/ida/IdaNativeProtocol.test.ts b/src/ida/IdaNativeProtocol.test.ts index f22e53235f..88d175b391 100644 --- a/src/ida/IdaNativeProtocol.test.ts +++ b/src/ida/IdaNativeProtocol.test.ts @@ -396,15 +396,11 @@ describe("native IDA provider observations", () => { ? expect.stringContaining( "Callees: Upstream callees capped results at 500; additional callees remain unknown.", ) - : expect.stringContaining( - "Callers: Upstream xrefs do not establish direct caller relationships.", - ), + : expect.stringMatching(/xrefs.*not establish.*caller/iu), }); if (more) expect(dossier.value.relationshipCoverage).toMatchObject({ - reason: expect.stringContaining( - "Callers: Upstream xrefs do not establish direct caller relationships.", - ), + reason: expect.stringMatching(/xrefs.*not establish.*caller/iu), }); expect(dossier.value.result).toMatchObject({ callers: [], diff --git a/src/ida/IdaSessionClient.test.ts b/src/ida/IdaSessionClient.test.ts index b17bdf2ae2..647f9543a9 100644 --- a/src/ida/IdaSessionClient.test.ts +++ b/src/ida/IdaSessionClient.test.ts @@ -283,7 +283,9 @@ describe("headless IDA producer pagination", () => { classification: null, body: { available: false, - reason: "IDA MCP does not report complete function body ranges.", + reason: expect.stringMatching( + /does not report complete function body ranges/iu, + ), }, }, native_api: null, diff --git a/src/javascript/recovery/WakaruReport.test.ts b/src/javascript/recovery/WakaruReport.test.ts index 5e4002d5f2..ee3d8d4a9d 100644 --- a/src/javascript/recovery/WakaruReport.test.ts +++ b/src/javascript/recovery/WakaruReport.test.ts @@ -50,9 +50,7 @@ it("names the fixed report allowance when provenance.json exceeds 8 MiB", async expect(projected.remediation.action).toContain("no runtime override"); expect(projected.remediation.action).toContain("max_heap_mb"); expect(projected.remediation.action).toContain("analysis_timeout_ms"); - expect(projected.remediation.action).toContain( - "Repeating the unchanged request fails the same way", - ); + expect(projected.remediation.action).toMatch(/unchanged request.*fails/iu); }); it("keeps a malformed provenance report distinct from the fixed report allowance", async () => { diff --git a/src/native/apple/AppleIpsParser.test.ts b/src/native/apple/AppleIpsParser.test.ts index d337c8e024..06d72d0d97 100644 --- a/src/native/apple/AppleIpsParser.test.ts +++ b/src/native/apple/AppleIpsParser.test.ts @@ -78,7 +78,9 @@ it.each([ expect(parsed.coverage.type_mismatches).toEqual([ { path: "body.usedImages[0].uuid", - expected: "32 hexadecimal UUID digits with optional canonical hyphens", + expected: expect.stringMatching( + /32 hexadecimal UUID digits.*optional.*hyphens/iu, + ), observed: "malformed UUID string", recorded_value: recorded, }, @@ -310,7 +312,9 @@ it("keeps empty observed text and JSON keys distinct from omitted fields", () => expect(parsed.coverage.type_mismatches).toEqual([ { path: "body.usedImages[0].uuid", - expected: "32 hexadecimal UUID digits with optional canonical hyphens", + expected: expect.stringMatching( + /32 hexadecimal UUID digits.*optional.*hyphens/iu, + ), observed: "malformed UUID string", recorded_value: "", }, diff --git a/src/native/elf/ElfNoteParser.test.ts b/src/native/elf/ElfNoteParser.test.ts index 957465f099..6430aba884 100644 --- a/src/native/elf/ElfNoteParser.test.ts +++ b/src/native/elf/ElfNoteParser.test.ts @@ -120,9 +120,7 @@ it.each([ expect(() => parse(compressedElfNoteSectionFixture(bits, little))).toThrow( expect.objectContaining({ kind: "unsupported", - message: expect.stringContaining( - "Compressed ELF note sections are unsupported", - ), + message: expect.stringMatching(/compressed.*note/iu), }), ); }, @@ -241,8 +239,10 @@ it("keeps note facts but marks section names unavailable for unknown compression expect(report.entries[0]?.gnu?.build_id_hex).toBe( "0123456789abcdef0123456789abcdef01234567", ); - expect(report.limitations).toContain( - "ELF section names are unavailable because compression type 3 is unsupported.", + expect(report.limitations).toContainEqual( + expect.stringMatching( + /section names.*unavailable.*compression type 3.*unsupported/iu, + ), ); }); @@ -480,11 +480,11 @@ it.each([ expect(directory.sections[2]).toMatchObject({ name, name_state: state }); if (name === null) { expect(notes.coverage.status).toBe("complete"); - expect(notes.limitations).toContain( - "ELF section names are incomplete because a name offset is outside its string table.", + expect(notes.limitations.join(" ")).toMatch( + /name offset.*string table/iu, ); - expect(directory.section_names_limitation).toBe( - "ELF section names are incomplete because a name offset is outside its string table", + expect(directory.section_names_limitation).toMatch( + /name offset.*string table/iu, ); } }, @@ -680,9 +680,7 @@ it("keeps compressed-note refusal ahead of alignment precision admission", () => const bytes = compressedElfNoteSectionFixture(); const sectionTable = Number(bytes.readBigUInt64LE(40)); bytes.writeBigUInt64LE(0xffffffffffffffffn, sectionTable + 64 + 48); - expect(() => parse(bytes)).toThrow( - "Compressed ELF note sections are unsupported", - ); + expect(() => parse(bytes)).toThrow(/compressed.*note/iu); }); it.each([ diff --git a/src/native/elf/ElfNotesProvider.test.ts b/src/native/elf/ElfNotesProvider.test.ts index b2b3a82215..bd199cb07c 100644 --- a/src/native/elf/ElfNotesProvider.test.ts +++ b/src/native/elf/ElfNotesProvider.test.ts @@ -77,12 +77,10 @@ describe("ELF note provider failures", () => { expect(result.ok).toBe(false); if (result.ok) throw new Error("Expected an unsupported target"); expect(result.error).toBeInstanceOf(AnalysisUnsupportedTargetError); - expect(result.error.message).toContain( - "ELF extended numbering is unsupported", - ); + expect(result.error.message).toMatch(/extended.*number/iu); expect(projectAnalysisError(result.error)).toMatchObject({ code: "unsupported_target", - message: expect.stringContaining("ELF extended numbering is unsupported"), + message: result.error.message, }); }); diff --git a/src/native/exports/NativeExportParser.test.ts b/src/native/exports/NativeExportParser.test.ts index 3b070e805e..cbe1bb4bec 100644 --- a/src/native/exports/NativeExportParser.test.ts +++ b/src/native/exports/NativeExportParser.test.ts @@ -558,8 +558,9 @@ describe("ELF export version and name coverage", () => { status: "partial", examined_entries: 2, exports: 2, - reason: - "1 export table issue(s) affect reported rows or metadata; see issues", + reason: expect.stringMatching( + /1 export table issue.*reported rows or metadata/iu, + ), }); expect(result.issues).toMatchObject([{ code: "version_unresolved" }]); }); @@ -859,8 +860,9 @@ describe("export inventory review regressions", () => { status: "partial", examined_entries: 1, exports: 1, - reason: - "1 export table issue(s) affect reported rows or metadata; see issues", + reason: expect.stringMatching( + /1 export table issue.*reported rows or metadata/iu, + ), }); }); diff --git a/src/native/parsers/plist.test.ts b/src/native/parsers/plist.test.ts index 64d742469b..37eb125a86 100644 --- a/src/native/parsers/plist.test.ts +++ b/src/native/parsers/plist.test.ts @@ -4,7 +4,11 @@ import { MAX_JSON_DEPTH, jsonValueSchema } from "../../domain/jsonValue.js"; import { projectAnalysisError } from "../../domain/analysisErrorProjection.js"; import { plistUidValue } from "../../domain/apple/plistUid.js"; import { createPlistNumberProjection } from "../../domain/apple/plistNumbers.js"; -import { projectPlistValue } from "../../domain/apple/plistValue.js"; +import { + nonFiniteRealLimitation, + projectPlistValue, + unknownScalarLimitation, +} from "../../domain/apple/plistValue.js"; import { parseXmlPropertyList } from "../../domain/propertyListKeys.js"; import { decodeBinaryPlistScalars, @@ -395,7 +399,7 @@ it("does not reject an unreachable malformed date object as a decoded value", () if (!parsed.ok) throw parsed.error; expect(parsed.value.value).toBe("ok"); expect(parsed.value.limitations).toContainEqual( - expect.stringContaining("number metadata could not be completely observed"), + expect.stringMatching(/metadata.*could not.*observed.*precision/iu), ); }); @@ -406,7 +410,7 @@ it("keeps reachable date uncertainty when another object is malformed", () => { if (!parsed.ok) throw parsed.error; expect(parsed.value.value).toEqual([{ $plist_type: "date", iso8601: null }]); expect(parsed.value.limitations).toContainEqual( - expect.stringContaining("number metadata could not be completely observed"), + expect.stringMatching(/metadata.*could not.*observed.*precision/iu), ); }); @@ -422,7 +426,7 @@ it("retains reachable date source facts when an unreachable offset is invalid", if (!parsed.ok) throw parsed.error; expect(parsed.value.value).toEqual([{ $plist_type: "date", iso8601: null }]); expect(parsed.value.limitations).toContainEqual( - expect.stringContaining("number metadata could not be completely observed"), + expect.stringMatching(/metadata.*could not.*observed.*precision/iu), ); }); @@ -435,7 +439,9 @@ it("does not rewrite shared bytes when a reachable date overlaps another object" { $plist_type: "data", base64: "" }, ]); expect(parsed.limitations).toContainEqual( - expect.stringContaining("sub-millisecond source records overlap"), + expect.stringMatching( + /1 date.*unknown timestamps.*overlap.*reachable binary object/iu, + ), ); }); @@ -449,7 +455,9 @@ it("keeps an exactly millisecond-representable overlapping date known", () => { null, ]); expect(parsed.value.limitations).not.toContainEqual( - expect.stringContaining("sub-millisecond source records overlap"), + expect.stringMatching( + /unknown timestamps.*overlap.*reachable binary object/iu, + ), ); }); @@ -462,7 +470,9 @@ it("keeps a date unknown when its start is inside another reachable payload", () { $plist_type: "date", iso8601: null }, ]); expect(parsed.limitations).toContainEqual( - expect.stringContaining("sub-millisecond source records overlap"), + expect.stringMatching( + /1 date.*unknown timestamps.*overlap.*reachable binary object/iu, + ), ); }); @@ -477,7 +487,9 @@ it("isolates an overlapping date without losing an independent date", () => { { $plist_type: "date", iso8601: "2001-01-01T00:00:00.100Z" }, ]); expect(parsed.value.limitations).toContainEqual( - expect.stringContaining("sub-millisecond source records overlap"), + expect.stringMatching( + /1 date.*unknown timestamps.*overlap.*reachable binary object/iu, + ), ); }); @@ -487,7 +499,9 @@ it("retains the integer sibling of a sub-millisecond overlapping date", () => { expect(parsed.value).toEqual([{ $plist_type: "date", iso8601: null }, 42]); expect(parsed.limitations).toContainEqual( - expect.stringContaining("sub-millisecond source records overlap"), + expect.stringMatching( + /1 date.*unknown timestamps.*overlap.*reachable binary object/iu, + ), ); }); @@ -501,8 +515,8 @@ it("isolates a shared overlapping date once while preserving both references", ( { $plist_type: "data", base64: "" }, ]); expect(parsed.value.limitations).toContainEqual( - expect.stringContaining( - "2 date value(s) are reported with unknown timestamps", + expect.stringMatching( + /2 date.*unknown timestamps.*overlap.*reachable binary object/iu, ), ); }); @@ -543,8 +557,8 @@ it("keeps binary plist source values lost by XML conversion", () => { ps: "a\u2029b", largeInteger: { $plist_type: "integer", decimal: "9007199254740992" }, }); - expect(parsed.value.limitations).toContain( - '1 negative-zero real value(s) are reported as { "$plist_type": "real", "value": "-0" } because a JSON number cannot carry the sign of zero.', + expect(parsed.value.limitations).toContainEqual( + expect.stringMatching(/1 negative.zero.*sign of zero/iu), ); expect(parsed.value.limitations).toContainEqual( expect.stringContaining("1 observed integer literal(s)"), @@ -709,7 +723,7 @@ it("projects retained JSON facts afresh for each XML classification", () => { { nested: [42] }, ]); expect(unclassified.value.limitations).toEqual([ - expect.stringContaining("1 negative-zero real value(s)"), + expect.stringMatching(/1 negative.zero.*sign of zero/iu), expect.stringContaining("1 number(s)"), ]); }); @@ -770,7 +784,7 @@ it("preserves numeric source facts inside prototype-named dictionary entries", ( kept: 42, }); expect(parsed.value.limitations).toEqual([ - expect.stringContaining("1 negative-zero real value(s)"), + expect.stringMatching(/1 negative.zero.*sign of zero/iu), expect.stringContaining("1 number(s)"), ]); }); @@ -840,8 +854,8 @@ it("keeps negative-zero reals distinct in native JSON and XML evidence", () => { expect(JSON.stringify(parsed.value.value)).toBe( '{"Negative":{"$plist_type":"real","value":"-0"},"Spelled":{"$plist_type":"real","value":"-0"},"Positive":0,"Integer":0,"Ratio":1.25}', ); - expect(parsed.value.limitations).toContain( - '2 negative-zero real value(s) are reported as { "$plist_type": "real", "value": "-0" } because a JSON number cannot carry the sign of zero.', + expect(parsed.value.limitations).toContainEqual( + expect.stringMatching(/2 negative.zero.*sign of zero/iu), ); } }); @@ -878,9 +892,12 @@ it("keeps CFPropertyList's non-finite real spellings in XML evidence", () => { ); expect(parsed.value.limitations).toEqual([ expect.stringContaining("XML conversion"), - '3 non-finite real value(s) are reported as { "$plist_type": "real", "value": "" } using canonical numeric labels nan, +infinity or -infinity because a JSON number cannot carry them.', - expect.stringContaining("1 XML scalar value(s) could not be established"), + nonFiniteRealLimitation(3), + unknownScalarLimitation(1), ]); + expect(unknownScalarLimitation(1)).toMatch( + /scalar.*not be established.*literal.*reason/iu, + ); }); it("retains nested own dictionary names consistently in native JSON, XML and binary projections", () => { diff --git a/src/native/pe/PeCertificateParser.test.ts b/src/native/pe/PeCertificateParser.test.ts index 15aa911a67..420ddd0494 100644 --- a/src/native/pe/PeCertificateParser.test.ts +++ b/src/native/pe/PeCertificateParser.test.ts @@ -241,8 +241,8 @@ it("retains certificate and signer facts for ASCII TeletexString names", () => { expect(report.entries[0]?.pkcs7?.signers[0]?.issuer?.text).toBe( "commonName=Plain Name, organizationName=Plain Org", ); - expect(report.limitations).toContain( - "TeletexString values use OpenSSL's ISO-8859-1 compatibility mapping; this is not a full T.61 decoder.", + expect(report.limitations).toContainEqual( + expect.stringMatching(/TeletexString.*ISO.8859.1.*not a full T.61/iu), ); }); diff --git a/src/process/ArtifactAnalysisResources.test.ts b/src/process/ArtifactAnalysisResources.test.ts index b48bc8e023..08abc2ea86 100644 --- a/src/process/ArtifactAnalysisResources.test.ts +++ b/src/process/ArtifactAnalysisResources.test.ts @@ -661,8 +661,8 @@ it("says REA retains the tighter inherited limit when a producer signal fires", expect(projected.remediation.action).toContain( "an unchanged retry may succeed or may fail the same way", ); - expect(projected.remediation.action).not.toContain( - "Repeating the unchanged request fails the same way.", + expect(projected.remediation.action).not.toMatch( + /unchanged request.*fails/iu, ); expect(projected.retryable).toBe(true); expect(projected.details).toMatchObject({ @@ -702,8 +702,8 @@ it("keeps an unchanged SIGXFSZ retry uncertain when the inherited limit was not expect(projected.remediation.action).toContain( "an unchanged retry may succeed or may fail the same way", ); - expect(projected.remediation.action).not.toContain( - "Repeating the unchanged request fails the same way.", + expect(projected.remediation.action).not.toMatch( + /unchanged request.*fails/iu, ); expect(projected.retryable).toBe(true); expect(projected.details).toMatchObject({ diff --git a/src/process/ProcessOwnership.lineage.test.ts b/src/process/ProcessOwnership.lineage.test.ts index 5a07a0f88c..0db4155fa0 100644 --- a/src/process/ProcessOwnership.lineage.test.ts +++ b/src/process/ProcessOwnership.lineage.test.ts @@ -77,8 +77,8 @@ it.each( status: "unavailable", reason: runId === undefined - ? "REA_PROCESS_RUN_ID was absent from the readable environment for PID 100; process lineage ownership remains unverified" - : "process lineage contains an unowned or PID-reused process", + ? expect.stringMatching(/REA_PROCESS_RUN_ID.*PID 100.*unverified/iu) + : expect.stringMatching(/unowned.*PID.reused/iu), }); await expect( cleanupOwnedProcessGroup(ownership, host), diff --git a/src/process/ProcessOwnership.observation.test.ts b/src/process/ProcessOwnership.observation.test.ts index a2cb4641e8..407177358f 100644 --- a/src/process/ProcessOwnership.observation.test.ts +++ b/src/process/ProcessOwnership.observation.test.ts @@ -576,8 +576,9 @@ describe("token sweep batch failures", () => { failures: expect.arrayContaining([ expect.objectContaining({ pid: 101, - diagnostic: - "individual unreadable; run-token batch failed: batch reader unavailable", + diagnostic: expect.stringMatching( + /individual unreadable.*batch reader unavailable/iu, + ), }), ]), }); diff --git a/src/process/ProcessOwnership.part2.test.ts b/src/process/ProcessOwnership.part2.test.ts index e88fb64799..2d7f7d8d4e 100644 --- a/src/process/ProcessOwnership.part2.test.ts +++ b/src/process/ProcessOwnership.part2.test.ts @@ -163,8 +163,9 @@ describe("owned process-group cleanup discovery", () => { await expect(cleanupOwnedProcessGroup(ownership, adapter)).resolves.toEqual( { cleaned: false, - reason: - "descendant process-group leader identity could not be verified", + reason: expect.stringMatching( + /descendant.*leader identity.*not be verified/iu, + ), }, ); expect(signalGroup).not.toHaveBeenCalled(); diff --git a/src/process/ProcessOwnership.part3.test.ts b/src/process/ProcessOwnership.part3.test.ts index 1ab9f3908b..941be0c8aa 100644 --- a/src/process/ProcessOwnership.part3.test.ts +++ b/src/process/ProcessOwnership.part3.test.ts @@ -283,8 +283,9 @@ describe("identityless lineage refresh follows tree membership", () => { }), ).resolves.toMatchObject({ status: "unavailable", - reason: - "REA_PROCESS_RUN_ID was absent from the readable environment for PID 102; process lineage ownership remains unverified", + reason: expect.stringMatching( + /REA_PROCESS_RUN_ID.*PID 102.*unverified/iu, + ), }); expect(outsiderReads).toBe(2); expect(environment.mock.calls.filter(([pid]) => pid === 50)).toHaveLength( @@ -310,8 +311,9 @@ describe("identityless lineage refresh follows tree membership", () => { }), ).resolves.toMatchObject({ status: "unavailable", - reason: - "REA_PROCESS_RUN_ID was absent from the readable environment for PID 102; process lineage ownership remains unverified", + reason: expect.stringMatching( + /REA_PROCESS_RUN_ID.*PID 102.*unverified/iu, + ), }); expect(reads()).toBe(2); }); @@ -427,8 +429,9 @@ describe("identityless lineage refresh rechecks the launcher", () => { }), ).resolves.toMatchObject({ status: "unavailable", - reason: - "REA_PROCESS_RUN_ID was absent from the readable environment for PID 100; process lineage ownership remains unverified", + reason: expect.stringMatching( + /REA_PROCESS_RUN_ID.*PID 100.*unverified/iu, + ), }); expect(launcherReads).toBe(2); }); @@ -462,8 +465,9 @@ describe("identityless lineage refresh rechecks an initial descendant", () => { }), ).resolves.toMatchObject({ status: "unavailable", - reason: - "REA_PROCESS_RUN_ID was absent from the readable environment for PID 102; process lineage ownership remains unverified", + reason: expect.stringMatching( + /REA_PROCESS_RUN_ID.*PID 102.*unverified/iu, + ), }); expect(reads).toBe(2); }); diff --git a/src/process/ProcessOwnership.validation.test.ts b/src/process/ProcessOwnership.validation.test.ts index 9e9c7d6a3c..0a834897de 100644 --- a/src/process/ProcessOwnership.validation.test.ts +++ b/src/process/ProcessOwnership.validation.test.ts @@ -40,7 +40,7 @@ const expectUnownedLineage = (adapter: ProcessOwnershipHost) => runId: "run-token", launcherPid: 100, processGroupId: 100, - reason: "process lineage contains an unowned or PID-reused process", + reason: expect.stringMatching(/unowned.*PID.reused/iu), }); describe("owned process-group cleanup validation: ownership and lineage", () => { @@ -519,7 +519,9 @@ describe("identityless exit refresh leaves unrelated replacements unresolved", ( ), ).resolves.toMatchObject({ cleaned: false, - reason: "descendant process-group leader identity could not be verified", + reason: expect.stringMatching( + /descendant.*leader identity.*not be verified/iu, + ), }); expect(signalGroup).not.toHaveBeenCalled(); }); @@ -1297,8 +1299,9 @@ describe("owned process-group cleanup token batches", () => { { pid: root.pid, reason: "environment-unreadable", - diagnostic: - "individual unreadable; run-token batch failed: batch reader unavailable", + diagnostic: expect.stringMatching( + /individual unreadable.*batch reader unavailable/iu, + ), }, ], }); diff --git a/src/python/PythonPycParser.test.ts b/src/python/PythonPycParser.test.ts index fb1642ce45..b1c1ee4b57 100644 --- a/src/python/PythonPycParser.test.ts +++ b/src/python/PythonPycParser.test.ts @@ -131,9 +131,7 @@ it("decodes a hash-based header and a FLAG_REF top-level code object", () => { expect(root(report).name).toBe(""); }); -it("fails unknown magics and truncated marshal without partial success", () => { - const unknown = Buffer.concat([pythonPycHeader(1), marshalCode("py311")]); - expect(() => parse(unknown)).toThrow(PythonPycFormatFailure); +it("rejects truncated marshal without partial success", () => { expect(() => parse(pythonPycFixture("py311").subarray(0, 20))).toThrow( PythonPycFormatFailure, ); @@ -173,82 +171,34 @@ it.each([ flags, ...(flags === 0 ? {} : { sourceHash: Buffer.alloc(8, 0xab) }), }); - expect(() => parse(header)).toThrow( - expect.objectContaining({ - kind: "unsupported", - message: expect.stringContaining( - `CPython ${version} header is recognized`, - ), - }), + let failure: unknown; + try { + parse(header); + } catch (error) { + failure = error; + } + expect(failure).toBeInstanceOf(PythonPycFormatFailure); + expect(failure).toMatchObject({ kind: "unsupported" }); + if (!(failure instanceof PythonPycFormatFailure)) throw failure; + expect(failure.message).toContain( + `CPython ${version} header is recognized`, ); - expect(() => parse(header)).toThrow( - expect.objectContaining({ - message: expect.stringContaining(`magic ${magic}, 16-byte header`), - }), - ); - // The reported facts are the ones packed into this header. - expect(() => parse(header)).toThrow( - expect.objectContaining({ - message: expect.stringContaining(`flags 0x${flags.toString(16)}`), - }), - ); - expect(() => parse(header)).toThrow( - expect.objectContaining({ - message: expect.stringContaining(mode), - }), - ); - expect(() => parse(header)).toThrow( - expect.objectContaining({ - message: expect.stringContaining( - "no marshal code layout is pinned for that version", - ), - }), - ); - expect(() => parse(header)).toThrow( - expect.objectContaining({ - message: expect.stringContaining( - "marshal decoding is pinned for CPython 3.7", - ), - }), + expect(failure.message).toContain(`magic ${magic}, 16-byte header`); + expect(failure.message).toContain(`flags 0x${flags.toString(16)}`); + expect(failure.message).toContain(mode); + expect(failure.message).not.toContain( + mode === "hash-based" ? "timestamp-based" : "hash-based", ); + expect(failure.message).toMatch(/no marshal code layout.*pinned/iu); + expect(failure.message).toMatch(/marshal decoding.*CPython 3\.7/iu); }, ); -it("reports timestamp-based for a pyc without the hash flag and hash-based with it", () => { - // Same magic, opposite header mode: the two refusals must differ only in that fact. - const timestamp = () => - parse(pythonPycHeader(3666, { mtime: 7, sourceSize: 9 })); - const hash = () => - parse( - pythonPycHeader(3666, { flags: 0x1, sourceHash: Buffer.alloc(8, 1) }), - ); - expect(timestamp).toThrow( - expect.objectContaining({ - message: expect.stringContaining("timestamp-based"), - }), - ); - expect(timestamp).toThrow( - expect.not.objectContaining({ - message: expect.stringContaining("hash-based"), - }), - ); - expect(hash).toThrow( - expect.objectContaining({ message: expect.stringContaining("hash-based") }), - ); - expect(hash).toThrow( - expect.not.objectContaining({ - message: expect.stringContaining("timestamp-based"), - }), - ); -}); - it("does not qualify a magic outside the pinned identity table", () => { expect(() => parse(pythonPycHeader(1))).toThrow( expect.objectContaining({ kind: "unsupported", - message: expect.stringContaining( - "is not a pinned CPython identity, so its header cannot be qualified", - ), + message: expect.stringMatching(/not a pinned CPython identity.*header/iu), }), ); }); @@ -258,8 +208,7 @@ it("reports a short unknown header before unsupported magic, and bad CR LF befor expect(() => parse(unknown)).toThrow( expect.objectContaining({ kind: "malformed", - message: - "Selected file is shorter than a 16-byte CPython 3.7+ pyc header.", + message: expect.stringMatching(/shorter than.*16.byte.*header/iu), }), ); const badTrailer = Buffer.from(unknown); @@ -267,7 +216,7 @@ it("reports a short unknown header before unsupported magic, and bad CR LF befor expect(() => parse(badTrailer)).toThrow( expect.objectContaining({ kind: "malformed", - message: "Pyc magic is not a little-endian integer followed by CR LF.", + message: expect.stringMatching(/magic.*little.endian.*CR LF/iu), }), ); }); diff --git a/tests/acceptance/apkSignatureV1Cli.test.ts b/tests/acceptance/apkSignatureV1Cli.test.ts index 1399f045ad..1f6bd1a33b 100644 --- a/tests/acceptance/apkSignatureV1Cli.test.ts +++ b/tests/acceptance/apkSignatureV1Cli.test.ts @@ -388,8 +388,8 @@ cliTest( expect( cliKeyIdentified.normalized_result.posture.v1_signers_match_block_signers, ).toBeNull(); - expect(cliKeyIdentified.limitations.join(" ")).toContain( - "META-INF/CERT.EC: a SignerInfo names its certificate by subjectKeyIdentifier, which apksig does not resolve", + expect(cliKeyIdentified.limitations.join(" ")).toMatch( + /META-INF\/CERT\.EC.*subjectKeyIdentifier.*apksig does not resolve/iu, ); }, ); diff --git a/tests/acceptance/applications/applicationVersionMatchingCli.test.ts b/tests/acceptance/applications/applicationVersionMatchingCli.test.ts index cfa19663e0..787eb4ff03 100644 --- a/tests/acceptance/applications/applicationVersionMatchingCli.test.ts +++ b/tests/acceptance/applications/applicationVersionMatchingCli.test.ts @@ -55,10 +55,13 @@ cliTest( properties.operation === "select-javascript-sources", ), ); - expect(parseScope?.observations[0]?.evidence).toMatchObject({ + const scopeEvidence = parseScope?.observations[0]?.evidence; + if (scopeEvidence === undefined) + throw new Error("Missing source scope evidence"); + expect(scopeEvidence).toMatchObject({ state: "unavailable", limitations: [ - "Source is outside the caller-selected source_paths; it was inventoried but not analyzed.", + expect.stringMatching(/source_paths.*inventoried.*not analyzed/iu), ], }); @@ -89,9 +92,7 @@ cliTest( ).toMatchObject({ status: "unknown", dimensions: ["availability"], - limitations: [ - "Source is outside the caller-selected source_paths; it was inventoried but not analyzed.", - ], + limitations: scopeEvidence.limitations, evidence_links: expect.arrayContaining([ full.evidence_id, selected.evidence_id, @@ -104,9 +105,7 @@ cliTest( ).toMatchObject({ status: "unknown", dimensions: ["availability"], - limitations: [ - "Source is outside the caller-selected source_paths; it was inventoried but not analyzed.", - ], + limitations: scopeEvidence.limitations, }); expect(forward.summary.added).toBe(0); expect(forward.summary.removed).toBe(0); diff --git a/tests/acceptance/applications/applicationWorkflowCli.test.ts b/tests/acceptance/applications/applicationWorkflowCli.test.ts index c4357c379a..66e6afd271 100644 --- a/tests/acceptance/applications/applicationWorkflowCli.test.ts +++ b/tests/acceptance/applications/applicationWorkflowCli.test.ts @@ -1280,10 +1280,13 @@ describe("application version source-scope MCP comparison", () => { properties.operation === "select-javascript-sources", ), ); - expect(scope?.observations[0]?.evidence).toMatchObject({ + const scopeEvidence = scope?.observations[0]?.evidence; + if (scopeEvidence === undefined) + throw new Error("Missing source scope evidence"); + expect(scopeEvidence).toMatchObject({ state: "unavailable", limitations: [ - "Source is outside the caller-selected source_paths; it was inventoried but not analyzed.", + expect.stringMatching(/source_paths.*inventoried.*not analyzed/iu), ], }); const client = await connectedMcpClient(); @@ -1307,9 +1310,7 @@ describe("application version source-scope MCP comparison", () => { ).toMatchObject({ status: "unknown", dimensions: ["availability"], - limitations: [ - "Source is outside the caller-selected source_paths; it was inventoried but not analyzed.", - ], + limitations: scopeEvidence.limitations, evidence_links: expect.arrayContaining([ full.evidence_id, selected.evidence_id, diff --git a/tests/acceptance/applications/artifactComparisonCoverage.test.ts b/tests/acceptance/applications/artifactComparisonCoverage.test.ts index 8b22fc310c..83e5ecc6bf 100644 --- a/tests/acceptance/applications/artifactComparisonCoverage.test.ts +++ b/tests/acceptance/applications/artifactComparisonCoverage.test.ts @@ -367,16 +367,20 @@ cliTest( edge_count: 0, }); expect(inventory.limitations).toEqual([ - "Inventory covers root bytes only; child container entries were not examined.", + expect.stringMatching(/root bytes only.*child.*not examined/iu), ]); + const leftInventory = artifactInventoryResultSchema.parse( + left.normalized_result, + ); + expect(leftInventory.limitations).toEqual(inventory.limitations); const equal = await compare(left, right); expect(equal.normalized_result).toMatchObject({ status: "unchanged", summary: { unchanged: 1, unknown: 0 }, changes: [], limitations: [ - "Left: Inventory covers root bytes only; child container entries were not examined.", - "Right: Inventory covers root bytes only; child container entries were not examined.", + ...leftInventory.limitations.map((note) => `Left: ${note}`), + ...inventory.limitations.map((note) => `Right: ${note}`), ], }); const behavior = await call("find_changed_behavior", { diff --git a/tests/acceptance/applications/interfaceBuilderPlistNumbers.test.ts b/tests/acceptance/applications/interfaceBuilderPlistNumbers.test.ts index 25f74d6f70..942c1b43ff 100644 --- a/tests/acceptance/applications/interfaceBuilderPlistNumbers.test.ts +++ b/tests/acceptance/applications/interfaceBuilderPlistNumbers.test.ts @@ -185,9 +185,7 @@ cliTest.for(scenarios)( ); else if (scenario === "mixed XML") expect(result.limitations).toContainEqual( - expect.stringContaining( - "1 XML scalar value(s) could not be established", - ), + expect.stringMatching(/1 XML scalar.*could not be established/iu), ); else expect( diff --git a/tests/acceptance/applications/javascriptIndirectLoaderOwners.test.ts b/tests/acceptance/applications/javascriptIndirectLoaderOwners.test.ts index 886c79d0c7..e96ee76262 100644 --- a/tests/acceptance/applications/javascriptIndirectLoaderOwners.test.ts +++ b/tests/acceptance/applications/javascriptIndirectLoaderOwners.test.ts @@ -417,7 +417,7 @@ cliTest.for( evidence: expect.objectContaining({ coverage: expect.objectContaining({ status: "complete" }), limitations: expect.arrayContaining([ - "Static syntax does not prove that this relationship executes at runtime.", + expect.stringMatching(/static syntax.*not prove.*runtime/iu), ]), }), }), diff --git a/tests/acceptance/applications/keyedArchiveScalarKinds.test.ts b/tests/acceptance/applications/keyedArchiveScalarKinds.test.ts index 8e25eb45b9..b98ff23adf 100644 --- a/tests/acceptance/applications/keyedArchiveScalarKinds.test.ts +++ b/tests/acceptance/applications/keyedArchiveScalarKinds.test.ts @@ -1,3 +1,4 @@ +import { nonFiniteRealLimitation } from "../../../src/domain/apple/plistValue.js"; import { createHash } from "node:crypto"; import { readFile, writeFile } from "node:fs/promises"; import { join } from "node:path"; @@ -132,9 +133,7 @@ cliTest.for(["xml", "binary"] as const)( status: "resolved", }), ); - expect(result.limitations).toContainEqual( - expect.stringContaining("1 non-finite real value(s)"), - ); + expect(result.limitations).toContainEqual(nonFiniteRealLimitation(1)); const page = await run(["--root", "data", "--offset", "1", "--limit", "2"]); expect(page.objects).toEqual(result.objects.slice(1, 3)); @@ -240,8 +239,8 @@ cliTest.for(["xml", "binary"] as const)( expect(JSON.stringify(result.objects.map(({ value }) => value))).toBe( '["$null",{"$plist_type":"real","value":"-0"},0,1.25]', ); - expect(result.limitations).toContain( - '1 negative-zero real value(s) are reported as { "$plist_type": "real", "value": "-0" } because a JSON number cannot carry the sign of zero.', + expect(result.limitations).toContainEqual( + expect.stringMatching(/1 negative.zero.*sign of zero/iu), ); expect(Buffer.compare(await readFile(path), bytes)).toBe(0); }, @@ -413,9 +412,7 @@ cliTest( expect(JSON.stringify(result.objects.map(({ value }) => value))).toBe( '["$null",{"$plist_type":"real","value":"+infinity"},{"$plist_type":"real","value":"-infinity"},{"$plist_type":"real","value":"nan"},1.25]', ); - expect(result.limitations).toContain( - '3 non-finite real value(s) are reported as { "$plist_type": "real", "value": "" } using canonical numeric labels nan, +infinity or -infinity because a JSON number cannot carry them.', - ); + expect(result.limitations).toContain(nonFiniteRealLimitation(3)); expect(result.limitations).not.toContainEqual( expect.stringContaining('"value": null'), ); diff --git a/tests/acceptance/applications/mobileFrameworkConventions.test.ts b/tests/acceptance/applications/mobileFrameworkConventions.test.ts index e99597010b..4a9242e7a8 100644 --- a/tests/acceptance/applications/mobileFrameworkConventions.test.ts +++ b/tests/acceptance/applications/mobileFrameworkConventions.test.ts @@ -117,7 +117,9 @@ cliTest( inventory_complete: true, }, limitations: expect.arrayContaining([ - "A bridge basis is inferred from the native path and is repeated for every managed component.", + expect.stringMatching( + /bridge basis.*inferred.*native path.*every managed component/iu, + ), ]), bridge_candidates: expect.arrayContaining( Object.entries(libraries).flatMap(([native_path, basis]) => diff --git a/tests/acceptance/applications/nativeExportsVersionRequirements.test.ts b/tests/acceptance/applications/nativeExportsVersionRequirements.test.ts index a7c0f9c7d1..ba3bbea7cc 100644 --- a/tests/acceptance/applications/nativeExportsVersionRequirements.test.ts +++ b/tests/acceptance/applications/nativeExportsVersionRequirements.test.ts @@ -33,8 +33,9 @@ cliTest( expect(report.coverage).toMatchObject({ status: "partial", exports: 2, - reason: - "1 export table issue(s) affect reported rows or metadata; see issues", + reason: expect.stringMatching( + /1 export table issue.*reported rows or metadata/iu, + ), }); expect(report.issues).toMatchObject([{ code: "version_unresolved" }]); }, diff --git a/tests/acceptance/applications/packageSpecifierSuffixCli.test.ts b/tests/acceptance/applications/packageSpecifierSuffixCli.test.ts index 33f747788d..b7064838c7 100644 --- a/tests/acceptance/applications/packageSpecifierSuffixCli.test.ts +++ b/tests/acceptance/applications/packageSpecifierSuffixCli.test.ts @@ -423,7 +423,7 @@ cliTest.for(["CLI", "stdio MCP"] as const)( ...(status === "rejected" ? { limitations: expect.arrayContaining([ - "The resolved candidate escapes the canonical artifact root.", + expect.stringMatching(/escapes.*artifact root/iu), ]), } : {}), diff --git a/tests/acceptance/artifactComparisonEspFlash.test.ts b/tests/acceptance/artifactComparisonEspFlash.test.ts index fba78645dd..5d8dbc76b6 100644 --- a/tests/acceptance/artifactComparisonEspFlash.test.ts +++ b/tests/acceptance/artifactComparisonEspFlash.test.ts @@ -203,6 +203,6 @@ it("classifies every partition of two dumps that end inside their last partition ["partitions/ota_1.bin", "changed"], ]); expect(occurrence(left, "partitions/ota_1.bin").limitations).toEqual([ - "partitions/ota_1.bin lies outside the dump: the file holds none of its 65536 bytes.", + expect.stringMatching(/partitions\/ota_1\.bin.*outside.*65536/iu), ]); }); diff --git a/tests/acceptance/artifactComparisonPlist.test.ts b/tests/acceptance/artifactComparisonPlist.test.ts index 872b1e3fc1..3a4e76c940 100644 --- a/tests/acceptance/artifactComparisonPlist.test.ts +++ b/tests/acceptance/artifactComparisonPlist.test.ts @@ -37,7 +37,7 @@ it("compares identical complete plist inventories across CLI and MCP and creates expect(cliInventory.normalized_result).toMatchObject({ coverage: { scope: "root", status: "complete" }, limitations: [ - "Inventory covers root bytes only; child container entries were not examined.", + expect.stringMatching(/root bytes only.*child.*not examined/iu), ], }); @@ -56,7 +56,7 @@ it("compares identical complete plist inventories across CLI and MCP and creates expect(mcpInventory.normalized_result).toMatchObject({ coverage: { scope: "root", status: "complete" }, limitations: [ - "Inventory covers root bytes only; child container entries were not examined.", + expect.stringMatching(/root bytes only.*child.*not examined/iu), ], }); diff --git a/tests/acceptance/artifactExtractionCoverage.test.ts b/tests/acceptance/artifactExtractionCoverage.test.ts index c64420fc69..0b747b82ad 100644 --- a/tests/acceptance/artifactExtractionCoverage.test.ts +++ b/tests/acceptance/artifactExtractionCoverage.test.ts @@ -160,6 +160,12 @@ cliTest.skipIf(process.platform !== "linux").for([false, true])( { evidence: cliEvidence, result: cliExtraction }, { evidence: mcpEvidence, result: mcpExtraction }, ]) { + expect(result.coverage.status).toBe( + withOpaqueName ? "unknown" : "complete", + ); + expect(evidence.limitations).toEqual( + expect.arrayContaining(result.limitations), + ); expect( result.limitations.some((limitation) => limitation.includes("nested archive contents remain"), @@ -176,11 +182,6 @@ cliTest.skipIf(process.platform !== "linux").for([false, true])( expect( limitations.some((value) => value.includes("not valid UTF-8")), ).toBe(withOpaqueName); - expect( - limitations.some((value) => - value.includes("source traversal coverage is unknown"), - ), - ).toBe(withOpaqueName); expect( limitations.some((value) => value.startsWith("All regular files")), ).toBe(!withOpaqueName); @@ -252,6 +253,10 @@ cliTest.for([false, true])( { evidence: cliEvidence, result: cliExtraction }, { evidence: mcpEvidence, result: mcpExtraction }, ]) { + expect(result.coverage.status).toBe(opaque ? "unknown" : "complete"); + expect(evidence.limitations).toEqual( + expect.arrayContaining(result.limitations), + ); expect( result.limitations.some((limitation) => limitation.includes("nested archive contents remain"), @@ -277,11 +282,6 @@ cliTest.for([false, true])( value.includes("PYZ archives remain opaque"), ), ).toBe(opaque); - expect( - limitations.some((value) => - value.includes("source traversal coverage is unknown"), - ), - ).toBe(opaque); expect( limitations.some((value) => value.startsWith("All regular files")), ).toBe(!opaque); diff --git a/tests/acceptance/devicetreeCli.test.ts b/tests/acceptance/devicetreeCli.test.ts index 717d59d064..9b3ac0d4b8 100644 --- a/tests/acceptance/devicetreeCli.test.ts +++ b/tests/acceptance/devicetreeCli.test.ts @@ -178,8 +178,10 @@ cliTest( hex: "", derived: { cells: null }, }); - expect(report.limitations).toContain( - "phandle at /target is not a single cell; this node's reference identity remains unresolved.", + expect(report.limitations).toContainEqual( + expect.stringMatching( + /phandle.*\/target.*not a single cell.*unresolved/iu, + ), ); }, ); diff --git a/tests/acceptance/elfNotesCli.test.ts b/tests/acceptance/elfNotesCli.test.ts index faffdd13aa..806fa08105 100644 --- a/tests/acceptance/elfNotesCli.test.ts +++ b/tests/acceptance/elfNotesCli.test.ts @@ -71,8 +71,8 @@ cliTest( result.json, ); expect(evidence.normalized_result.tables[0]?.name).toBeNull(); - expect(evidence.normalized_result.limitations).toContain( - "ELF section names are incomplete because a name offset is outside its string table.", + expect(evidence.normalized_result.limitations.join(" ")).toMatch( + /name offset.*string table/iu, ); expect(evidence.normalized_result.entries[0]?.gnu?.build_id_hex).toBe( "0123456789abcdef0123456789abcdef01234567", @@ -134,9 +134,7 @@ cliTest( expect(result.exitCode).toBe(1); expect(result.json).toMatchObject({ code: "unsupported_target", - message: expect.stringContaining( - "Compressed ELF note sections are unsupported", - ), + message: expect.stringMatching(/compressed.*note/iu), }); expect(Buffer.compare(await readFile(path), bytes)).toBe(0); }, @@ -155,7 +153,7 @@ cliTest( expect(result.exitCode).toBe(1); expect(result.json).toMatchObject({ code: "unsupported_target", - message: expect.stringContaining("ELF extended numbering is unsupported"), + message: expect.stringMatching(/extended.*number/iu), }); expect((await readFile(path)).equals(before)).toBe(true); }, diff --git a/tests/acceptance/managedCliOutput.test.ts b/tests/acceptance/managedCliOutput.test.ts index 861e3a3017..ffca6fda48 100644 --- a/tests/acceptance/managedCliOutput.test.ts +++ b/tests/acceptance/managedCliOutput.test.ts @@ -44,7 +44,9 @@ cliTest( status === "malformed" ? null : expect.any(String), issue: status === "malformed" - ? "CIL prefix chain at IL offset 0 has no following instruction" + ? expect.stringMatching( + /CIL prefix.*offset 0.*no following instruction/iu, + ) : null, }), }), diff --git a/tests/acceptance/managedSelectedScope.test.ts b/tests/acceptance/managedSelectedScope.test.ts index b4d7e2df45..5eb2789289 100644 --- a/tests/acceptance/managedSelectedScope.test.ts +++ b/tests/acceptance/managedSelectedScope.test.ts @@ -70,13 +70,15 @@ cliTest( ); expect(report.summary).toMatchObject({ added: 0, removed: 0 }); if (index === 0) - expect(report.limitations).toContain( - "Both observations cover only the selected declaring types (Fixture.Inner, Inner); member totals and statuses describe those types, not the whole assembly.", + expect(report.limitations).toContainEqual( + expect.stringMatching( + /selected declaring types.*Fixture\.Inner.*Inner.*not the whole assembly/iu, + ), ); else { expect(report.summary.unknown).toBeGreaterThan(0); - expect(report.limitations).toContain( - "The observations select different declaring types; a member outside the other side's selection is unknown, not added or removed.", + expect(report.limitations).toContainEqual( + expect.stringMatching(/different declaring types.*unknown/iu), ); } } diff --git a/tests/acceptance/peCertificatesCli.test.ts b/tests/acceptance/peCertificatesCli.test.ts index 1a877b942c..cac018d261 100644 --- a/tests/acceptance/peCertificatesCli.test.ts +++ b/tests/acceptance/peCertificatesCli.test.ts @@ -205,8 +205,8 @@ cliTest( evidence.normalized_result.entries[0]?.pkcs7?.certificates[0]?.issuer .text, ).toBe("commonName=Âe, organizationName=REA Tests"); - expect(evidence.normalized_result.limitations).toContain( - "TeletexString values use OpenSSL's ISO-8859-1 compatibility mapping; this is not a full T.61 decoder.", + expect(evidence.normalized_result.limitations).toContainEqual( + expect.stringMatching(/TeletexString.*ISO.8859.1.*not a full T.61/iu), ); }, ); @@ -375,9 +375,7 @@ cliTest.for([ expect(reason).toContain(message); expect(remediation.action).toContain(String(Object.values(limits)[0])); expect(remediation.action).toContain("no runtime override"); - expect(remediation.action).toContain( - "Repeating the unchanged request fails the same way", - ); + expect(remediation.action).toMatch(/unchanged request.*fails/iu); }, ); diff --git a/tests/acceptance/rustBinaryCli.test.ts b/tests/acceptance/rustBinaryCli.test.ts index 02f3cc9164..c2f2a3c20c 100644 --- a/tests/acceptance/rustBinaryCli.test.ts +++ b/tests/acceptance/rustBinaryCli.test.ts @@ -300,8 +300,8 @@ cliTest( }); } else { expect(report.auditable_metadata).toBeNull(); - expect(report.limitations.join("\n")).toContain( - "ELF section names are unavailable because compression type 3 is unsupported", + expect(report.limitations.join("\n")).toMatch( + /section names.*unavailable.*compression type 3.*unsupported/iu, ); expect(report.limitations.join("\n")).toContain( "The presence of .dep-v0 could not be determined", @@ -702,8 +702,9 @@ cliTest( category: "resource_constraint", retryable: false, remediation: { - action: - "The rustc commit-path inventory is a fixed count. Waiting for an active analysis to finish does not raise it. Repeating the unchanged request fails the same way.", + action: expect.stringMatching( + /rustc commit.path inventory.*fixed count/iu, + ), }, details: { operation: "inspect_rust_binary", diff --git a/tests/acceptance/webSessionLifecycleWindow.test.ts b/tests/acceptance/webSessionLifecycleWindow.test.ts index ee30f32e89..dbf294f229 100644 --- a/tests/acceptance/webSessionLifecycleWindow.test.ts +++ b/tests/acceptance/webSessionLifecycleWindow.test.ts @@ -127,8 +127,10 @@ cliTest.for(["CLI", "stdio MCP"] as const)( timestamp: null, }), ); - expect(normalized.limitations).toContain( - "Timeline rows without a comparable producer timestamp have a null timestamp. Buffered events with unknown timing are retained as context only; they do not establish activity inside the selected observation window.", + expect(normalized.limitations).toContainEqual( + expect.stringMatching( + /Buffered events.*unknown timing.*context only.*do not establish.*observation window/iu, + ), ); expect(browser.commands).toContainEqual( expect.objectContaining({ diff --git a/tests/boundary/browser/cdpBrowserProvider.lifecycle_failures.test.ts b/tests/boundary/browser/cdpBrowserProvider.lifecycle_failures.test.ts index e9dcedc9bc..6dc54f2d9e 100644 --- a/tests/boundary/browser/cdpBrowserProvider.lifecycle_failures.test.ts +++ b/tests/boundary/browser/cdpBrowserProvider.lifecycle_failures.test.ts @@ -374,8 +374,10 @@ describe("CdpBrowserProvider: session termination and scope changes", () => { detail: "session_detached", timestamp: null, }); - expect(result.value.limitations).toContain( - "Timeline rows without a comparable producer timestamp have a null timestamp. Buffered events with unknown timing are retained as context only; they do not establish activity inside the selected observation window.", + expect(result.value.limitations).toContainEqual( + expect.stringMatching( + /Buffered events.*unknown timing.*context only.*do not establish.*observation window/iu, + ), ); }); diff --git a/tests/boundary/browser/cdpObservationWindowBuffer.test.ts b/tests/boundary/browser/cdpObservationWindowBuffer.test.ts index 56473ccc2f..4db4c1c5b8 100644 --- a/tests/boundary/browser/cdpObservationWindowBuffer.test.ts +++ b/tests/boundary/browser/cdpObservationWindowBuffer.test.ts @@ -216,8 +216,10 @@ it("buffers clock-response events while dropping lifecycle history before the cl timestamp: 22, }), ); - expect(result.value.limitations).toContain( - "Timeline rows without a comparable producer timestamp have a null timestamp. Buffered events with unknown timing are retained as context only; they do not establish activity inside the selected observation window.", + expect(result.value.limitations).toContainEqual( + expect.stringMatching( + /unknown timing.*context only.*do not establish.*observation window/iu, + ), ); }); diff --git a/tests/boundary/browser/cdpObservationWindowScope.test.ts b/tests/boundary/browser/cdpObservationWindowScope.test.ts index 2b4f177751..66afde5c16 100644 --- a/tests/boundary/browser/cdpObservationWindowScope.test.ts +++ b/tests/boundary/browser/cdpObservationWindowScope.test.ts @@ -179,7 +179,9 @@ it("updates the main frame from a pre-arm root replacement without inventing a n ); expect(observation.limitations).toEqual( expect.arrayContaining([ - expect.stringContaining("Buffered events with unknown timing"), + expect.stringMatching( + /Buffered events.*unknown timing.*context only.*do not establish.*observation window/iu, + ), ]), ); }); @@ -324,8 +326,10 @@ it.each([ expect(observation.timeline).not.toContainEqual( expect.objectContaining({ request_id: "after-terminal-event" }), ); - expect(observation.limitations).toContain( - "Timeline rows without a comparable producer timestamp have a null timestamp. Buffered events with unknown timing are retained as context only; they do not establish activity inside the selected observation window.", + expect(observation.limitations).toContainEqual( + expect.stringMatching( + /Buffered events.*unknown timing.*context only.*do not establish.*observation window/iu, + ), ); }, ); diff --git a/tests/boundary/cli/analysisViewCli.test.ts b/tests/boundary/cli/analysisViewCli.test.ts index df73edb935..bb0ed5d80f 100644 --- a/tests/boundary/cli/analysisViewCli.test.ts +++ b/tests/boundary/cli/analysisViewCli.test.ts @@ -620,7 +620,7 @@ cliTest( "evidence_id", ], reason: "invalid_value", - message: "Evidence semantic identifier does not match its record", + message: expect.stringMatching(/semantic identifier.*not match/iu), }, ], }, @@ -693,9 +693,7 @@ cliTest( expect(result.normalized_result).toMatchObject({ groups: [{ content_types: [], flags: ["single-sample"] }], limitations: expect.arrayContaining([caveat]), - unknowns: [ - "Some retained records have unavailable status or content type values.", - ], + unknowns: [expect.stringMatching(/unavailable status or content type/iu)], }); }, ); diff --git a/tests/boundary/cli/argumentGuidance.test.ts b/tests/boundary/cli/argumentGuidance.test.ts index 5892e685c8..34169496e0 100644 --- a/tests/boundary/cli/argumentGuidance.test.ts +++ b/tests/boundary/cli/argumentGuidance.test.ts @@ -28,8 +28,7 @@ it.each([ const result = JSON.parse(text); expect(result).toMatchObject({ code: "VALIDATION_ERROR", - message: - "REA could not read the command arguments. Check the named argument and its expected type, then try again.", + message: expect.stringMatching(/arguments.*expected type/iu), fieldErrors: expect.any(Array), }); expect(text).not.toContain("Details:"); diff --git a/tests/boundary/cli/browserInputDiagnostics.test.ts b/tests/boundary/cli/browserInputDiagnostics.test.ts index 39e5c1106b..8c988a82e5 100644 --- a/tests/boundary/cli/browserInputDiagnostics.test.ts +++ b/tests/boundary/cli/browserInputDiagnostics.test.ts @@ -170,11 +170,10 @@ describe("browser and Inspector CLI input diagnostics", () => { error: input.error, code: "invalid_request", category: "invalid_input", - message: - "Analysis input is invalid. Check the arguments and try again.", + message: expect.stringMatching(/input.*invalid/iu), retryable: false, remediation: { - action: `Read the declared input schema or CLI usage for ${input.operation}; if using MCP and the catalog may be stale, call tools/list. Correct the listed fields, then retry. Do not repeat the unchanged call.`, + action: expect.stringContaining(input.operation), }, recovery: { kind: "inspect_input_contract", @@ -187,6 +186,9 @@ describe("browser and Inspector CLI input diagnostics", () => { ]), }, }); + expect(result.json).toMatchObject({ + remediation: { action: expect.stringContaining("tools/list") }, + }); expect(result.stdout).not.toContain(malformedEndpoint); expect(result.stderr).not.toContain(malformedEndpoint); }, diff --git a/tests/boundary/cli/dylibResolutionCli.test.ts b/tests/boundary/cli/dylibResolutionCli.test.ts index 7b66aa8adc..67e14c4139 100644 --- a/tests/boundary/cli/dylibResolutionCli.test.ts +++ b/tests/boundary/cli/dylibResolutionCli.test.ts @@ -767,9 +767,12 @@ cliTest( )?.slices[0]?.dyld_environment, ).toEqual(settings); expect( - trace.findings.find(({ kind }) => kind === "dyld-environment-present") - ?.explanation, - ).toContain("entries in this non-executable root are observations"); + trace.findings.find(({ kind }) => kind === "dyld-environment-present"), + ).toMatchObject({ + image: "Contents/Frameworks/libcore.dylib", + basis: "derived", + explanation: expect.stringContaining(settings[0]!), + }); }, ); diff --git a/tests/boundary/cli/electronCli.test.ts b/tests/boundary/cli/electronCli.test.ts index 4b846b6485..5b8e36d678 100644 --- a/tests/boundary/cli/electronCli.test.ts +++ b/tests/boundary/cli/electronCli.test.ts @@ -85,7 +85,7 @@ it( isolation: "none", }, limitations: expect.arrayContaining([ - "Browser host platform and architecture were not observed.", + expect.stringMatching(/platform.*architecture.*not observed/iu), ]), normalized_result: { targets: [{ target_id: "electron-page" }], diff --git a/tests/boundary/cli/explicitSensitiveArgumentsCli.test.ts b/tests/boundary/cli/explicitSensitiveArgumentsCli.test.ts index 2416368621..6e03e89473 100644 --- a/tests/boundary/cli/explicitSensitiveArgumentsCli.test.ts +++ b/tests/boundary/cli/explicitSensitiveArgumentsCli.test.ts @@ -112,8 +112,7 @@ describe("CLI --sensitive-value parser diagnostics", () => { expectExcluded(result, ["mitmproxy"]); expect(JSON.parse(result.stdout)).toMatchObject({ code: "VALIDATION_ERROR", - message: - "REA could not read the command arguments. Check the named argument and its expected type, then try again.", + message: expect.stringMatching(/arguments.*expected type/iu), fieldErrors: [ expect.objectContaining({ code: "invalid_value", path: "format" }), ], diff --git a/tests/boundary/cli/scenarioManifestCoverage.test.ts b/tests/boundary/cli/scenarioManifestCoverage.test.ts index 09c6df079a..78cbd581f8 100644 --- a/tests/boundary/cli/scenarioManifestCoverage.test.ts +++ b/tests/boundary/cli/scenarioManifestCoverage.test.ts @@ -92,8 +92,8 @@ it.each( async ({ url, body }) => { const result = await manifest([url], body); expect(result.records).toEqual([]); - expect(result.limitations).toContain( - "1 retained records without a usable request line are not represented in the manifest.", + expect(result.limitations).toContainEqual( + expect.stringMatching(/1.*request line.*not represented.*manifest/iu), ); expect( result.limitations.filter((limitation) => diff --git a/tests/boundary/filesystem/analysisSnapshotProvenance.test.ts b/tests/boundary/filesystem/analysisSnapshotProvenance.test.ts index e7b3dba979..a13bebf241 100644 --- a/tests/boundary/filesystem/analysisSnapshotProvenance.test.ts +++ b/tests/boundary/filesystem/analysisSnapshotProvenance.test.ts @@ -149,8 +149,8 @@ it("reruns ambiguous imported observations without changing their historical Evi const replayed = await session.execute("inspect_managed_artifact", {}); if (!replayed.ok) throw replayed.error; expect(replayed.value.result).toEqual(observed.value.result); - expect(replayed.value.limitations).toContain( - "Loaded from a local REA analysis snapshot; this call did not re-run the provider.", + expect(replayed.value.limitations).toContainEqual( + expect.stringMatching(/snapshot.*did not re.run the provider/iu), ); const controlClosed = await session.close(); if (!controlClosed.ok) throw controlClosed.error; @@ -178,8 +178,8 @@ it("reruns ambiguous imported observations without changing their historical Evi const fresh = await session.execute("inspect_managed_artifact", {}); if (!fresh.ok) throw fresh.error; expect(fresh.value.result).toEqual(observed.value.result); - expect(fresh.value.limitations).not.toContain( - "Loaded from a local REA analysis snapshot; this call did not re-run the provider.", + expect(fresh.value.limitations).not.toContainEqual( + expect.stringMatching(/snapshot.*did not re.run the provider/iu), ); expect(session.evidenceById(original.evidence_id)).toEqual(original); expect(session.evidenceById(alternative.evidence_id)).toEqual(alternative); diff --git a/tests/boundary/filesystem/android/application.test.ts b/tests/boundary/filesystem/android/application.test.ts index fe7ce32a32..7608105a4b 100644 --- a/tests/boundary/filesystem/android/application.test.ts +++ b/tests/boundary/filesystem/android/application.test.ts @@ -101,7 +101,9 @@ describe("Android application projection", () => { ).toBe(true); expect(left.limitations).toEqual( expect.arrayContaining([ - "A bridge basis is inferred from the native path and is repeated for every managed component.", + expect.stringMatching( + /bridge basis.*inferred.*native path.*every managed component/iu, + ), "ZIP inventory cannot see the APK Signing Block, so an empty signing array does not mean the APK is unsigned.", ]), ); diff --git a/tests/boundary/filesystem/appleApplication.test.ts b/tests/boundary/filesystem/appleApplication.test.ts index a63f1b8b6f..a9a67e6ea8 100644 --- a/tests/boundary/filesystem/appleApplication.test.ts +++ b/tests/boundary/filesystem/appleApplication.test.ts @@ -211,8 +211,8 @@ describe("Apple application projection", () => { application_roots: [], bundles: [], }); - expect(projection.limitations).toContain( - "No application bundle (Payload/*.app or *.app/Contents) was present in the supplied inventory pages.", + expect(projection.limitations).toContainEqual( + expect.stringMatching(/no application bundle.*inventory/iu), ); }); diff --git a/tests/boundary/filesystem/artifactInventoryRegularRoot.test.ts b/tests/boundary/filesystem/artifactInventoryRegularRoot.test.ts index 955832fd42..c0765966e3 100644 --- a/tests/boundary/filesystem/artifactInventoryRegularRoot.test.ts +++ b/tests/boundary/filesystem/artifactInventoryRegularRoot.test.ts @@ -135,7 +135,7 @@ it("retains root identity without asserting absent children for an unclassified ]); expect(inventory.coverage).toEqual({ scope: "root", status: "complete" }); expect(inventory.limitations).toEqual([ - "Inventory covers root bytes only; child container entries were not examined.", + expect.stringMatching(/root bytes only.*child.*not examined/iu), ]); }); diff --git a/tests/boundary/filesystem/artifactZipIntegrity.test.ts b/tests/boundary/filesystem/artifactZipIntegrity.test.ts index eb4b3d7fb8..22507a9253 100644 --- a/tests/boundary/filesystem/artifactZipIntegrity.test.ts +++ b/tests/boundary/filesystem/artifactZipIntegrity.test.ts @@ -314,7 +314,7 @@ it("reports unflagged high-byte ZIP names as CP437 readings with their source by logical_path: "╟╤▒█.txt", hash_status: "verified", limitations: [ - "ZIP name uses CP437 fallback; source encoding unverified; raw hex: c7d1b1db2e747874.", + expect.stringMatching(/CP437.*unverified.*c7d1b1db2e747874/iu), ], }), ); @@ -345,9 +345,7 @@ it("falls back to the stored name and reports bytes when a Unicode Path field CR expect(inventory.occurrences).toContainEqual( expect.objectContaining({ logical_path: "é.txt", - limitations: [ - "ZIP name uses CP437 fallback; source encoding unverified; raw hex: 822e747874.", - ], + limitations: [expect.stringMatching(/CP437.*unverified.*822e747874/iu)], }), ); }); diff --git a/tests/boundary/filesystem/clientLifecycle.test.ts b/tests/boundary/filesystem/clientLifecycle.test.ts index 2a4700e200..6957773103 100644 --- a/tests/boundary/filesystem/clientLifecycle.test.ts +++ b/tests/boundary/filesystem/clientLifecycle.test.ts @@ -1881,8 +1881,9 @@ describe("uninstall configuration preflight", () => { expect.objectContaining({ name: "cursor", status: "failed", - detail: - "Configuration is not valid JSON and was not changed. Repair it, then rerun uninstall.", + detail: expect.stringMatching( + /not valid JSON.*not changed.*repair.*uninstall/iu, + ), }), ); expect(await readFile(config, "utf8")).toBe("not-json"); diff --git a/tests/boundary/filesystem/composedWorkflowEvidenceLinks.test.ts b/tests/boundary/filesystem/composedWorkflowEvidenceLinks.test.ts index 118931412a..2fc5678cdd 100644 --- a/tests/boundary/filesystem/composedWorkflowEvidenceLinks.test.ts +++ b/tests/boundary/filesystem/composedWorkflowEvidenceLinks.test.ts @@ -267,8 +267,8 @@ it("rehydrates the original atomic identity from an authenticated snapshot witho {}, ); if (!execution.ok) return execution; - expect(execution.value.limitations).toContain( - "Loaded from a local REA analysis snapshot; this call did not re-run the provider.", + expect(execution.value.limitations).toContainEqual( + expect.stringMatching(/snapshot.*did not re.run the provider/iu), ); return ok(execution.value.result); }, @@ -391,8 +391,8 @@ it("reruns an ambiguous snapshot binding before selecting composed Evidence link (operation) => operation === "read_binary_inventory_summary", ), ).toHaveLength(1); - expect(after.value.limitations).not.toContain( - "Loaded from a local REA analysis snapshot; this call did not re-run the provider.", + expect(after.value.limitations).not.toContainEqual( + expect.stringMatching(/snapshot.*did not re.run the provider/iu), ); if (after.value.evidenceId === undefined) throw new Error("Fresh provider observation was not committed"); diff --git a/tests/boundary/filesystem/freebuffClientConfiguration.test.ts b/tests/boundary/filesystem/freebuffClientConfiguration.test.ts index 4261ce58d7..e0c6c737af 100644 --- a/tests/boundary/filesystem/freebuffClientConfiguration.test.ts +++ b/tests/boundary/filesystem/freebuffClientConfiguration.test.ts @@ -392,8 +392,8 @@ describe("uninstall applicability for shared FreeBuff storage", () => { expect(zcodeItem?.detail).toContain( "Configuration could not be read. Check file permissions, then rerun uninstall.", ); - expect(zcodeItem?.detail).toContain( - "Configuration is not valid JSON and was not changed. Repair it, then rerun uninstall.", + expect(zcodeItem?.detail).toMatch( + /not valid JSON.*not changed.*repair.*uninstall/iu, ); }); diff --git a/tests/boundary/filesystem/interfaceBuilderBundle.test.ts b/tests/boundary/filesystem/interfaceBuilderBundle.test.ts index b127b73a35..3a26d9760b 100644 --- a/tests/boundary/filesystem/interfaceBuilderBundle.test.ts +++ b/tests/boundary/filesystem/interfaceBuilderBundle.test.ts @@ -1812,8 +1812,8 @@ describe("NIBArchive UIKit event connections", () => { }), ); expect(analysis.graph.truncated).toBe(true); - expect(analysis.limitations).toContain( - "Some recognized NIBArchive connectors declare no resolvable owner; they carry no connection evidence and are reported as omissions.", + expect(analysis.limitations).toContainEqual( + expect.stringMatching(/resolvable owner.*omissions/iu), ); }); }); @@ -1997,8 +1997,8 @@ describe("NIBArchive single-object connectors", () => { }), ); expect(analysis.graph.truncated).toBe(false); - expect(analysis.limitations).not.toContain( - "Some recognized NIBArchive connectors declare no resolvable owner; they carry no connection evidence and are reported as omissions.", + expect(analysis.limitations).not.toContainEqual( + expect.stringMatching(/resolvable owner/iu), ); }); }); @@ -2036,8 +2036,8 @@ describe("NIBArchive connectors with unusable sources", () => { }), ); expect(analysis.graph.truncated).toBe(true); - expect(analysis.limitations).toContain( - "Some recognized NIBArchive connectors declare no resolvable owner; they carry no connection evidence and are reported as omissions.", + expect(analysis.limitations).toContainEqual( + expect.stringMatching(/resolvable owner/iu), ); }); @@ -2091,8 +2091,8 @@ describe("NIBArchive connectors with unusable sources", () => { }), ); expect(analysis.graph.truncated).toBe(true); - expect(analysis.limitations).toContain( - "Some recognized NIBArchive connectors declare no resolvable owner; they carry no connection evidence and are reported as omissions.", + expect(analysis.limitations).toContainEqual( + expect.stringMatching(/resolvable owner/iu), ); }, ); diff --git a/tests/boundary/filesystem/javascript-applications/electronApplicationAnalysis.test.ts b/tests/boundary/filesystem/javascript-applications/electronApplicationAnalysis.test.ts index 2d8725f2d0..0d546fd6c8 100644 --- a/tests/boundary/filesystem/javascript-applications/electronApplicationAnalysis.test.ts +++ b/tests/boundary/filesystem/javascript-applications/electronApplicationAnalysis.test.ts @@ -309,7 +309,7 @@ utilityProcess.fork("./service.js"); authority: "static-relationship-inference", state: "inferred", limitations: expect.arrayContaining([ - "Static syntax does not prove that this relationship executes at runtime.", + expect.stringMatching(/static syntax.*not prove.*runtime/iu), ]), }); expect(utilityLaunches).toEqual( diff --git a/tests/boundary/filesystem/javascript-applications/javascriptModuleRelationships.test.ts b/tests/boundary/filesystem/javascript-applications/javascriptModuleRelationships.test.ts index 1a276c8598..8441ed5f59 100644 --- a/tests/boundary/filesystem/javascript-applications/javascriptModuleRelationships.test.ts +++ b/tests/boundary/filesystem/javascript-applications/javascriptModuleRelationships.test.ts @@ -122,8 +122,8 @@ it.each([ ), ).toEqual([semanticResourceLimitReason(limit)]); expect(evidence.coverage.status).toBe("partial"); - expect(evidence.limitations).toContain( - "Static syntax does not prove that this relationship executes at runtime.", + expect(evidence.limitations).toContainEqual( + expect.stringMatching(/static syntax.*not prove.*runtime/iu), ); expect(evidence.limitations).toContain( "Values and aliases were recovered from inert syntax only; no JavaScript was executed.", diff --git a/tests/boundary/filesystem/processCliFileInput.test.ts b/tests/boundary/filesystem/processCliFileInput.test.ts index db3935b399..8e186cdaac 100644 --- a/tests/boundary/filesystem/processCliFileInput.test.ts +++ b/tests/boundary/filesystem/processCliFileInput.test.ts @@ -381,7 +381,7 @@ describe("process CLI input diagnostics", () => { error: "Process command failed", code: "invalid_request", category: "invalid_input", - message: "Analysis input is invalid. Check the arguments and try again.", + message: expect.stringMatching(/input.*invalid/iu), retryable: false, remediation: { action: expect.stringContaining("Correct the listed fields"), diff --git a/tests/boundary/firmware/firmwareReportFiles.test.ts b/tests/boundary/firmware/firmwareReportFiles.test.ts index 02e23eb52e..7aae3ca6c2 100644 --- a/tests/boundary/firmware/firmwareReportFiles.test.ts +++ b/tests/boundary/firmware/firmwareReportFiles.test.ts @@ -119,8 +119,8 @@ it.each([ expect(projected.remediation.action).toContain("cannot be replayed as-is"); if (state === "oversized") { expect(projected.remediation.action).toContain("no runtime override"); - expect(projected.remediation.action).toContain( - "Repeating the unchanged request fails the same way", + expect(projected.remediation.action).toMatch( + /unchanged request.*fails/iu, ); expect(projected.remediation.action).not.toContain( "Rerun the same REA request", @@ -134,8 +134,8 @@ it.each([ expect(projected.remediation.action).toContain( "Rerun the same REA request", ); - expect(projected.remediation.action).not.toContain( - "Repeating the unchanged request fails the same way", + expect(projected.remediation.action).not.toMatch( + /unchanged request.*fails/iu, ); } }, diff --git a/tests/boundary/go/goBinaryProvider.test.ts b/tests/boundary/go/goBinaryProvider.test.ts index cfda1f065e..ccd4c575d7 100644 --- a/tests/boundary/go/goBinaryProvider.test.ts +++ b/tests/boundary/go/goBinaryProvider.test.ts @@ -115,8 +115,10 @@ it("reports unreadable ELF names alongside the no-record limitation", async () = expect(result.value.limitations).toContain( "No recognized Go build-info record was found in the inspected image; absent metadata does not establish that the binary is not Go.", ); - expect(result.value.limitations).toContain( - "ELF section names are unavailable because compression type 3 is unsupported", + expect(result.value.limitations).toContainEqual( + expect.stringMatching( + /section names.*unavailable.*compression type 3.*unsupported/iu, + ), ); expect(result.value).not.toHaveProperty("section_names_limitation"); }); diff --git a/tests/boundary/inspector/javascriptRuntimeObservation.test.ts b/tests/boundary/inspector/javascriptRuntimeObservation.test.ts index d969ef7fe7..1719680245 100644 --- a/tests/boundary/inspector/javascriptRuntimeObservation.test.ts +++ b/tests/boundary/inspector/javascriptRuntimeObservation.test.ts @@ -62,7 +62,7 @@ test("reports static-layer Evidence digest failure at its caller path", () => { { path: ["static_layers", 0, "analysis", "evidence_id"], reason: "invalid_value", - message: "Evidence semantic identifier does not match its record", + message: expect.stringMatching(/semantic identifier.*not match/iu), }, ], }, diff --git a/tests/boundary/javascriptSemanticTraceEvidence.test.ts b/tests/boundary/javascriptSemanticTraceEvidence.test.ts index 33a464a14b..7d3e9f5a72 100644 --- a/tests/boundary/javascriptSemanticTraceEvidence.test.ts +++ b/tests/boundary/javascriptSemanticTraceEvidence.test.ts @@ -326,8 +326,12 @@ it("traces transformed operands through CLI and advertised MCP output", async () }, ); expect(nested.status).not.toBe("found"); - expect(nested.unknowns.map((unknown) => unknown.detail)).toContain( - "The dynamic call's return dependency on this argument is unknown.", + expect(nested.unknowns).toContainEqual( + expect.objectContaining({ + reason: "dynamic-call", + family: "data-flow", + relation_kinds: ["influences"], + }), ); } finally { await harness.close(); diff --git a/tests/boundary/mcp/analysisViewMcp.test.ts b/tests/boundary/mcp/analysisViewMcp.test.ts index a21e7ae99e..bba6e199ce 100644 --- a/tests/boundary/mcp/analysisViewMcp.test.ts +++ b/tests/boundary/mcp/analysisViewMcp.test.ts @@ -717,7 +717,7 @@ it("reports a tampered inline Evidence ID at its full MCP source path", async () { path: ["source", "evidence", "evidence_id"], reason: "invalid_value", - message: "Evidence semantic identifier does not match its record", + message: expect.stringMatching(/semantic identifier.*not match/iu), }, ], }, @@ -911,9 +911,7 @@ it("admits portable captures before SDK Evidence parsing and preserves capture c expect(result.normalized_result).toMatchObject({ groups: [{ content_types: [], flags: ["single-sample"] }], limitations: expect.arrayContaining([caveat]), - unknowns: [ - "Some retained records have unavailable status or content type values.", - ], + unknowns: [expect.stringMatching(/unavailable status or content type/iu)], }); }); @@ -1532,7 +1530,9 @@ it.each([false, true])( }), ]), limitations: expect.arrayContaining([ - "Some records carry no usable request duration; their completion and overlap remain unknown.", + expect.stringMatching( + /request duration.*completion.*overlap.*unknown/iu, + ), ]), }); expect(parent).toEqual(before); diff --git a/tests/boundary/mcp/appleIpsMcp.test.ts b/tests/boundary/mcp/appleIpsMcp.test.ts index 015e61d7e7..f3c2a4023c 100644 --- a/tests/boundary/mcp/appleIpsMcp.test.ts +++ b/tests/boundary/mcp/appleIpsMcp.test.ts @@ -112,7 +112,9 @@ it("advertises and validates IPS facts and failures through production stdio MCP expect(partial.coverage.type_mismatches).toEqual([ { path: "body.usedImages[0].uuid", - expected: "32 hexadecimal UUID digits with optional canonical hyphens", + expected: expect.stringMatching( + /32 hexadecimal UUID digits.*optional.*hyphens/iu, + ), observed: "malformed UUID string", recorded_value: "malformed/report", }, diff --git a/tests/boundary/mcp/artifactMcp.test.ts b/tests/boundary/mcp/artifactMcp.test.ts index 907937d320..18e49ab20d 100644 --- a/tests/boundary/mcp/artifactMcp.test.ts +++ b/tests/boundary/mcp/artifactMcp.test.ts @@ -284,7 +284,7 @@ it("preserves raw legacy ZIP name bytes and CP437 path interpretation through MC expect.objectContaining({ logical_path: "╟╤▒█.txt", limitations: [ - "ZIP name uses CP437 fallback; source encoding unverified; raw hex: c7d1b1db2e747874.", + expect.stringMatching(/CP437.*unverified.*c7d1b1db2e747874/iu), ], }), ); diff --git a/tests/boundary/mcp/browserScenarioInputDiagnosticsMcp.test.ts b/tests/boundary/mcp/browserScenarioInputDiagnosticsMcp.test.ts index bf6098c6a2..12809f4d2d 100644 --- a/tests/boundary/mcp/browserScenarioInputDiagnosticsMcp.test.ts +++ b/tests/boundary/mcp/browserScenarioInputDiagnosticsMcp.test.ts @@ -146,8 +146,7 @@ cliTest.for(["full", "compact"] as const)( category: "invalid_input", retryable: false, remediation: { - action: - "Read the declared input schema or CLI usage for capture_browser_scenario; if using MCP and the catalog may be stale, call tools/list. Correct the listed fields, then retry. Do not repeat the unchanged call.", + action: expect.stringContaining("capture_browser_scenario"), }, recovery: { kind: "inspect_input_contract", diff --git a/tests/boundary/mcp/callPathSearchExtentMcp.test.ts b/tests/boundary/mcp/callPathSearchExtentMcp.test.ts index 839de13215..5c0ff579c3 100644 --- a/tests/boundary/mcp/callPathSearchExtentMcp.test.ts +++ b/tests/boundary/mcp/callPathSearchExtentMcp.test.ts @@ -127,9 +127,7 @@ it.each([ shortest_hops: null, search_scope: { exhaustive: false }, paths: [], - limitations: [ - "No analyze_function Evidence covers reachable function 0x5000", - ], + limitations: [expect.stringMatching(/no analyze_function.*0x5000/iu)], }); return; } diff --git a/tests/boundary/mcp/cutterMcp.test.ts b/tests/boundary/mcp/cutterMcp.test.ts index 6827837ede..401bfa9f09 100644 --- a/tests/boundary/mcp/cutterMcp.test.ts +++ b/tests/boundary/mcp/cutterMcp.test.ts @@ -49,10 +49,10 @@ const assertUncertainEvidence = async ( message: expect.stringContaining("do not retry automatically"), document_generation: 3, }); - expect(uncertainEvidence.limitations).toContain( - "Command completion or its output could not be confirmed; " + - "do not retry automatically because the command may have produced " + - "partial or persistent effects.", + expect(uncertainEvidence.limitations).toContainEqual( + expect.stringMatching( + /completion.*could not be confirmed.*not retry.*effects/iu, + ), ); }; diff --git a/tests/boundary/mcp/dylibResolutionMcp.test.ts b/tests/boundary/mcp/dylibResolutionMcp.test.ts index e36fba4af0..5ea20698d0 100644 --- a/tests/boundary/mcp/dylibResolutionMcp.test.ts +++ b/tests/boundary/mcp/dylibResolutionMcp.test.ts @@ -572,9 +572,12 @@ it("ignores process settings on an explicitly selected library root through MCP" ?.dyld_environment, ).toEqual(settings); expect( - trace.findings.find(({ kind }) => kind === "dyld-environment-present") - ?.explanation, - ).toContain("entries in this non-executable root are observations"); + trace.findings.find(({ kind }) => kind === "dyld-environment-present"), + ).toMatchObject({ + image: "library.dylib", + basis: "derived", + explanation: expect.stringContaining(settings[0]!), + }); }); }); diff --git a/tests/boundary/mcp/electronMcp.test.ts b/tests/boundary/mcp/electronMcp.test.ts index cca7595b1d..bd5369365b 100644 --- a/tests/boundary/mcp/electronMcp.test.ts +++ b/tests/boundary/mcp/electronMcp.test.ts @@ -343,7 +343,7 @@ it("exposes endpoint-scoped Electron discovery and inspection as Evidence", asyn isolation: "none", }, limitations: expect.arrayContaining([ - "Browser host platform and architecture were not observed.", + expect.stringMatching(/platform.*architecture.*not observed/iu), ]), normalized_result: { targets: [{ target_id: "electron-page" }], diff --git a/tests/boundary/mcp/elfNotesMcp.test.ts b/tests/boundary/mcp/elfNotesMcp.test.ts index 02325edde9..db039f0935 100644 --- a/tests/boundary/mcp/elfNotesMcp.test.ts +++ b/tests/boundary/mcp/elfNotesMcp.test.ts @@ -92,8 +92,8 @@ mcpTest( response.structuredContent, ); expect(evidence.normalized_result.tables[0]?.name).toBeNull(); - expect(evidence.normalized_result.limitations).toContain( - "ELF section names are incomplete because a name is not NUL-terminated.", + expect(evidence.normalized_result.limitations.join(" ")).toMatch( + /name.*not NUL.terminated/iu, ); expect(evidence.normalized_result.entries[0]?.gnu?.build_id_hex).toBe( "0123456789abcdef0123456789abcdef01234567", @@ -126,9 +126,7 @@ mcpTest( expect(response.isError).toBe(true); expect(parseMcpToolError(response).error).toMatchObject({ code: "unsupported_target", - message: expect.stringContaining( - "Compressed ELF note sections are unsupported", - ), + message: expect.stringMatching(/compressed.*note/iu), }); expect(Buffer.compare(await readFile(path), bytes)).toBe(0); } finally { @@ -153,9 +151,7 @@ mcpTest( expect(response.isError).toBe(true); expect(parseMcpToolError(response).error).toMatchObject({ code: "unsupported_target", - message: expect.stringContaining( - "ELF extended numbering is unsupported", - ), + message: expect.stringMatching(/extended.*number/iu), }); expect((await readFile(path)).equals(before)).toBe(true); } finally { diff --git a/tests/boundary/mcp/enhancedTools.callPathsAndBatches.test.ts b/tests/boundary/mcp/enhancedTools.callPathsAndBatches.test.ts index 88361023ea..064f857b21 100644 --- a/tests/boundary/mcp/enhancedTools.callPathsAndBatches.test.ts +++ b/tests/boundary/mcp/enhancedTools.callPathsAndBatches.test.ts @@ -125,7 +125,7 @@ describe("enhanced MCP graph caveats", () => { }); expect(graph.structuredContent).toMatchObject({ limitations: [ - "Artifact identity is unavailable for this observation.", + expect.stringMatching(/artifact identity.*unavailable/iu), "Derived by an REA composed workflow.", `Fixture analysis provider (fixture 1) procedure_callees: ${caveat}`, `Fixture analysis provider (fixture 1) procedure_callees: ${leafCaveat}`, diff --git a/tests/boundary/mcp/enhancedTools.limitations.test.ts b/tests/boundary/mcp/enhancedTools.limitations.test.ts index 4ecc330d68..8067714807 100644 --- a/tests/boundary/mcp/enhancedTools.limitations.test.ts +++ b/tests/boundary/mcp/enhancedTools.limitations.test.ts @@ -39,7 +39,7 @@ it("retains provider coverage caveats in a composed MCP overview without repeate expect(response.isError).not.toBe(true); const evidence = parseEvidence(response.structuredContent); expect(evidence.limitations).toEqual([ - "Artifact identity is unavailable for this observation.", + expect.stringMatching(/artifact identity.*unavailable/iu), "Derived by an REA composed workflow.", `Fixture (fixture 1) current_document, list_procedures, list_segments, list_strings: ${limitation}`, ]); diff --git a/tests/boundary/mcp/functionComparisonCoverageMcp.test.ts b/tests/boundary/mcp/functionComparisonCoverageMcp.test.ts index 127e81e551..868df74f59 100644 --- a/tests/boundary/mcp/functionComparisonCoverageMcp.test.ts +++ b/tests/boundary/mcp/functionComparisonCoverageMcp.test.ts @@ -138,11 +138,11 @@ it("keeps incomplete calls unknown through public comparison and verification wh ); expect(left.subject).not.toBeNull(); expect(right.subject).not.toBeNull(); - expect(left.limitations).not.toContain( - "Artifact identity is unavailable for this observation.", + expect(left.limitations).not.toContainEqual( + expect.stringMatching(/artifact identity.*unavailable/iu), ); - expect(right.limitations).not.toContain( - "Artifact identity is unavailable for this observation.", + expect(right.limitations).not.toContainEqual( + expect.stringMatching(/artifact identity.*unavailable/iu), ); const compared = await client.callTool({ name: "compare_functions", diff --git a/tests/boundary/mcp/gamecubeDolSessionMcp.test.ts b/tests/boundary/mcp/gamecubeDolSessionMcp.test.ts index 383b8795bc..fc654fede1 100644 --- a/tests/boundary/mcp/gamecubeDolSessionMcp.test.ts +++ b/tests/boundary/mcp/gamecubeDolSessionMcp.test.ts @@ -8,7 +8,6 @@ import { expect, it } from "vitest"; import { z } from "zod"; import { createApplicationMcpHarness } from "../../fixtures/applicationMcpHarness.js"; -import { SNAPSHOT_REPLAY_LIMITATION } from "../../../src/application/binary/AnalysisSnapshotCache.js"; import { readAnalysisSnapshot } from "../../../src/application/binary/AnalysisSnapshotFiles.js"; import { toolContract } from "../../../src/contracts/toolContracts.js"; import { parseEvidence } from "../../../src/domain/evidence.js"; @@ -137,7 +136,9 @@ it("saves a GameCube DOL snapshot and replays it after reopening", async () => { expect(original.normalized_result).toBe("address_name"); expect(replayed.subject).toEqual(original.subject); expect(replayed.normalized_result).toEqual(original.normalized_result); - expect(replayed.limitations).toContain(SNAPSHOT_REPLAY_LIMITATION); + expect(replayed.limitations).toContainEqual( + expect.stringMatching(/snapshot.*did not re.run the provider/iu), + ); expect(calls.filter((operation) => operation === "address_name")).toEqual([ "address_name", ]); diff --git a/tests/boundary/mcp/ghidraEvidenceMcp.test.ts b/tests/boundary/mcp/ghidraEvidenceMcp.test.ts index 0232798225..e4207b039d 100644 --- a/tests/boundary/mcp/ghidraEvidenceMcp.test.ts +++ b/tests/boundary/mcp/ghidraEvidenceMcp.test.ts @@ -195,7 +195,7 @@ it("retains terminal-call qualifications across function views and snapshot repl expect(replay.value.result, name).toEqual(value); expect(replay.value.limitations, name).toContain(limitation); expect(replay.value.limitations, name).toContainEqual( - expect.stringContaining("Loaded from a local REA analysis snapshot"), + expect.stringMatching(/snapshot.*did not re.run the provider/iu), ); } expect(harness.calls).toEqual([...values.keys()]); @@ -662,7 +662,7 @@ describe("Ghidra MCP evidence parity", () => { provider: { id: "ghidra", version: "12.1.4" }, normalized_result: expect.stringContaining("return 42"), limitations: expect.arrayContaining([ - expect.stringContaining("not text-equivalent to Hopper"), + expect.stringMatching(/not text.equivalent to Hopper/iu), ]), }); @@ -694,18 +694,22 @@ describe("Ghidra MCP evidence parity", () => { kind: { available: true, provenance: "ghidra-reference-manager" }, }, ], - limitations: expect.arrayContaining([ - expect.stringContaining("indirect flows without target addresses"), - expect.stringContaining( - "not original source or Hopper-equivalent text", - ), - ]), + limitations: functionDossierSchema.parse(ghidraFunctionDossier()) + .limitations, }, limitations: expect.arrayContaining([ - expect.stringContaining("default auto-analysis complete"), - expect.stringContaining("original executable bytes are unchanged"), + expect.stringMatching(/auto.analysis.*complete/iu), + expect.stringMatching(/original executable bytes.*unchanged/iu), ]), }); + expect( + functionDossierSchema.parse(analyzed.normalized_result).limitations, + ).toEqual( + expect.arrayContaining([ + expect.stringMatching(/indirect flows without target addresses/iu), + expect.stringMatching(/not original source.*Hopper.equivalent/iu), + ]), + ); const directAnalyzed = await new EnhancedTools(session).execute( "analyze_function", { procedure: "fixture_main" }, diff --git a/tests/boundary/mcp/ghidraFileOffsetMcp.test.ts b/tests/boundary/mcp/ghidraFileOffsetMcp.test.ts index 5251c07c73..a30e19f262 100644 --- a/tests/boundary/mcp/ghidraFileOffsetMcp.test.ts +++ b/tests/boundary/mcp/ghidraFileOffsetMcp.test.ts @@ -175,7 +175,7 @@ it("reports file-backed, uninitialized and unmapped addresses through MCP withou // Malformed syntax is still an input-contract violation. expect(await mcp.refused("not-an-address")).toMatchObject({ code: "invalid_request", - message: "Analysis input is invalid. Check the arguments and try again.", + message: expect.stringMatching(/input.*invalid/iu), recovery: { kind: "inspect_input_contract", operation: "address_to_file_offset", diff --git a/tests/boundary/mcp/ghidraInventoryMcp.test.ts b/tests/boundary/mcp/ghidraInventoryMcp.test.ts index 02faf8ed06..9670c7b39b 100644 --- a/tests/boundary/mcp/ghidraInventoryMcp.test.ts +++ b/tests/boundary/mcp/ghidraInventoryMcp.test.ts @@ -394,7 +394,7 @@ it("retains missing PDB coverage in empty PE name-search Evidence and snapshot r if (!replay.ok) throw replay.error; expect(replay.value.limitations).toContain(limitation); expect(replay.value.limitations).toContainEqual( - expect.stringContaining("Loaded from a local REA analysis snapshot"), + expect.stringMatching(/snapshot.*did not re.run the provider/iu), ); } finally { await harness.close(); @@ -403,6 +403,7 @@ it("retains missing PDB coverage in empty PE name-search Evidence and snapshot r it("retains defined-string coverage in empty and matching search Evidence", async () => { let output = jsonValueSchema.parse([]); + let stringCoverage: string | undefined; const harness = await connectGhidraMcp("ghidra-string-coverage", () => Promise.resolve(ok(output)), ); @@ -425,11 +426,14 @@ it("retains defined-string coverage in empty and matching search Evidence", asyn expect(evidence.analysis_profile?.parameters).toMatchObject({ string_inventory_evidence: "defined-data-coverage-v1", }); - expect(evidence.limitations).toContainEqual( - expect.stringMatching( - /Only Ghidra-defined string Data.*empty search does not establish/u, + const coverage = evidence.limitations.find((limitation) => + /Only Ghidra-defined string Data.*empty search does not establish/u.test( + limitation, ), ); + if (coverage === undefined) throw new Error("Missing string coverage"); + if (stringCoverage !== undefined) expect(coverage).toBe(stringCoverage); + stringCoverage = coverage; } output = jsonValueSchema.parse([ { address: "0x401000", value: "projection cache ignored" }, @@ -443,8 +447,9 @@ it("retains defined-string coverage in empty and matching search Evidence", asyn matched.structuredContent, ); expect(evidence.normalized_result).toEqual(output); + expect(evidence.limitations).toContain(stringCoverage); expect(evidence.limitations).toContainEqual( - expect.stringContaining("Completeness applies to that inventory"), + expect.stringMatching(/completeness applies to that inventory/iu), ); output = jsonValueSchema.parse([]); const procedures = await harness.mcp.callTool({ @@ -454,7 +459,7 @@ it("retains defined-string coverage in empty and matching search Evidence", asyn expect( sessionEvidence(harness.session, procedures.structuredContent) .limitations, - ).not.toContainEqual(expect.stringContaining("Ghidra-defined string Data")); + ).not.toContainEqual(expect.stringMatching(/Ghidra.defined string Data/iu)); } finally { await harness.close(); } diff --git a/tests/boundary/mcp/ghidraNativeApiMcp.test.ts b/tests/boundary/mcp/ghidraNativeApiMcp.test.ts index 50bc88d184..45e68b85f5 100644 --- a/tests/boundary/mcp/ghidraNativeApiMcp.test.ts +++ b/tests/boundary/mcp/ghidraNativeApiMcp.test.ts @@ -357,7 +357,7 @@ it("retains unavailable Ghidra native API observations across provenance and exa expect(replay.evidence_id).not.toBe(first.evidence_id); expect(replay.limitations).toEqual( expect.arrayContaining([ - expect.stringContaining("Loaded from a local REA analysis snapshot"), + expect.stringMatching(/snapshot.*did not re.run the provider/iu), ]), ); const warmBundle = session.exportEvidenceBundle(); diff --git a/tests/boundary/mcp/nativeMachoSummaryMcp.test.ts b/tests/boundary/mcp/nativeMachoSummaryMcp.test.ts index 131c13adb0..d9a2612e15 100644 --- a/tests/boundary/mcp/nativeMachoSummaryMcp.test.ts +++ b/tests/boundary/mcp/nativeMachoSummaryMcp.test.ts @@ -488,8 +488,8 @@ describe.skipIf(process.platform !== "darwin")( }, }); expect(wrongKind.isError).toBe(true); - expect(JSON.stringify(wrongKind.content)).toContain( - "Mach-O inspection supports summary and macho views.", + expect(JSON.stringify(wrongKind.content)).toMatch( + /summary and macho views/iu, ); }, ); diff --git a/tests/boundary/mcp/nativeUiPartialCallRoutesMcp.test.ts b/tests/boundary/mcp/nativeUiPartialCallRoutesMcp.test.ts index d0ce556ea1..b940feaf95 100644 --- a/tests/boundary/mcp/nativeUiPartialCallRoutesMcp.test.ts +++ b/tests/boundary/mcp/nativeUiPartialCallRoutesMcp.test.ts @@ -409,7 +409,7 @@ it.each(["unavailable", "output"] as const)( resolution: "unresolved", reason: failure === "unavailable" - ? "This analysis is unavailable for the current target. Choose another analysis or target." + ? expect.stringMatching(/unavailable.*target/iu) : expect.stringContaining("unreadable result"), limitations: expect.arrayContaining([ "The provider could not complete callee enumeration.", diff --git a/tests/boundary/mcp/processComparisonDiagnosticsMcp.test.ts b/tests/boundary/mcp/processComparisonDiagnosticsMcp.test.ts index 8e2d88eca3..aab899adb8 100644 --- a/tests/boundary/mcp/processComparisonDiagnosticsMcp.test.ts +++ b/tests/boundary/mcp/processComparisonDiagnosticsMcp.test.ts @@ -274,9 +274,7 @@ it.each(["left", "right"] as const)( issues: [ { path: ["evidence_id"], - message: expect.stringContaining( - "Evidence semantic identifier does not match its record", - ), + message: expect.stringMatching(/semantic identifier.*not match/iu), }, ], }, diff --git a/tests/boundary/mcp/rizinDebuggerCancellationMcp.test.ts b/tests/boundary/mcp/rizinDebuggerCancellationMcp.test.ts index 79ce8626c2..0adcd69230 100644 --- a/tests/boundary/mcp/rizinDebuggerCancellationMcp.test.ts +++ b/tests/boundary/mcp/rizinDebuggerCancellationMcp.test.ts @@ -99,8 +99,10 @@ process.stdin.on("data", (chunk) => { expect(evidence.normalized_result).toMatchObject({ output_invalid_utf8: true, }); - expect(evidence.limitations).toContain( - "Launch provenance records the original and resolved startup paths; it does not establish the current target after unrestricted debugger commands.", + expect(evidence.limitations).toContainEqual( + expect.stringMatching( + /launch provenance.*does not establish.*current target/iu, + ), ); const status = await validateDebuggerStatusResponse( diff --git a/tests/boundary/mcp/rizinPendingOutput.test.ts b/tests/boundary/mcp/rizinPendingOutput.test.ts index ffddfe7227..4486bee9d0 100644 --- a/tests/boundary/mcp/rizinPendingOutput.test.ts +++ b/tests/boundary/mcp/rizinPendingOutput.test.ts @@ -227,8 +227,10 @@ test.each(["_", "__REA_", "marker-prefix"])( normalized_result: { output: expected, completion_status: "unknown" }, raw_result: { output: expected }, }); - expect(result.value.evidence.limitations).toContain( - "Interrupted output may include an incomplete completion-marker prefix; it is retained because it cannot be distinguished from command diagnostics.", + expect(result.value.evidence.limitations).toContainEqual( + expect.stringMatching( + /incomplete completion.marker prefix.*diagnostics/iu, + ), ); } finally { injected.control.exit(); diff --git a/tests/boundary/mcp/rustBinaryMcp.test.ts b/tests/boundary/mcp/rustBinaryMcp.test.ts index 02b806b8c4..c158b2d3c5 100644 --- a/tests/boundary/mcp/rustBinaryMcp.test.ts +++ b/tests/boundary/mcp/rustBinaryMcp.test.ts @@ -116,8 +116,9 @@ mcpTest( category: "resource_constraint", retryable: false, remediation: { - action: - "The rustc commit-path inventory is a fixed count. Waiting for an active analysis to finish does not raise it. Repeating the unchanged request fails the same way.", + action: expect.stringMatching( + /rustc commit.path inventory.*fixed count/iu, + ), }, details: { operation: "inspect_rust_binary", diff --git a/tests/boundary/mcp/selectedSnapshotCleanupMcp.test.ts b/tests/boundary/mcp/selectedSnapshotCleanupMcp.test.ts index afe973c77a..33edbdb33d 100644 --- a/tests/boundary/mcp/selectedSnapshotCleanupMcp.test.ts +++ b/tests/boundary/mcp/selectedSnapshotCleanupMcp.test.ts @@ -166,7 +166,7 @@ it("retains a selected snapshot descriptor through MCP cleanup discovery and sam evidence_links: [original.evidence_id], limitations: [ ...original.limitations, - "Loaded from a local REA analysis snapshot; this call did not re-run the provider.", + expect.stringMatching(/snapshot.*did not re.run the provider/iu), ], }); expect(replayEvidence.evidence_id).not.toBe(original.evidence_id); diff --git a/tests/boundary/providers/CutterBridgeClient.test.ts b/tests/boundary/providers/CutterBridgeClient.test.ts index 1b6a139ead..ee58b4d366 100644 --- a/tests/boundary/providers/CutterBridgeClient.test.ts +++ b/tests/boundary/providers/CutterBridgeClient.test.ts @@ -1490,11 +1490,11 @@ describe("CutterBridgeClient: scenario group 9", () => { }); expect(result.ok).toBe(true); if (!result.ok) throw result.error; - expect(result.value.limitations).toContain( - "The command completed or may have completed, but output exceeded the bridge transport limit and was omitted.", + expect(result.value.limitations).toContainEqual( + expect.stringMatching(/output.*transport limit.*omitted/iu), ); - expect(result.value.limitations).not.toContain( - "Command completion or its output could not be confirmed; do not retry automatically because the command may have produced partial or persistent effects.", + expect(result.value.limitations).not.toContainEqual( + expect.stringMatching(/completion.*could not be confirmed/iu), ); }); }); diff --git a/tests/boundary/providers/CutterBridgeService.test.ts b/tests/boundary/providers/CutterBridgeService.test.ts index b46f9dca32..24a3920e29 100644 --- a/tests/boundary/providers/CutterBridgeService.test.ts +++ b/tests/boundary/providers/CutterBridgeService.test.ts @@ -144,7 +144,9 @@ it("retains uncertain command completion as Evidence without encouraging a retry cutter_version: "Cutter fixture version", }, limitations: expect.arrayContaining([ - "Command completion or its output could not be confirmed; do not retry automatically because the command may have produced partial or persistent effects.", + expect.stringMatching( + /completion.*could not be confirmed.*not retry.*effects/iu, + ), ]), provider: { version: "Cutter fixture version" }, }, diff --git a/tests/boundary/providers/RizinDebugSessionCoverage.part3.test.ts b/tests/boundary/providers/RizinDebugSessionCoverage.part3.test.ts index ad6c551def..db2d2f9eac 100644 --- a/tests/boundary/providers/RizinDebugSessionCoverage.part3.test.ts +++ b/tests/boundary/providers/RizinDebugSessionCoverage.part3.test.ts @@ -50,12 +50,16 @@ it.skipIf(process.platform === "win32")( resolved_path: "/tmp/injected", }); expect(result.value.evidence.limitations).toEqual([ - "The current debugger target identity is not established by the Rizin session identifier.", + expect.stringMatching(/target identity.*not established/iu), rizinCommandTimeoutLimitation(deadline), - "The owned debugger session was stopped because output correlation could no longer be guaranteed.", - "The command is unrestricted and may access local files, the shell, network, or mutate the target.", - "Interrupted output may include an incomplete completion-marker prefix; it is retained because it cannot be distinguished from command diagnostics.", - "Launch provenance records the original and resolved startup paths; it does not establish the current target after unrestricted debugger commands.", + expect.stringMatching(/session.*stopped.*output correlation/iu), + expect.stringMatching(/unrestricted.*shell.*network.*mutate/iu), + expect.stringMatching( + /incomplete completion.marker prefix.*diagnostics/iu, + ), + expect.stringMatching( + /launch provenance.*does not establish.*current target/iu, + ), ]); }, ); @@ -306,11 +310,17 @@ it.skipIf(process.platform === "win32")( }); expect(result.value.evidence.raw_result).toEqual(result.value.result); expect(result.value.evidence.limitations).toEqual([ - "The current debugger target identity is not established by the Rizin session identifier.", - "Rizin exited before the command-completion marker; retained frame output is partial and command completion is unknown.", - "The command is unrestricted and may access local files, the shell, network, or mutate the target.", - "Interrupted output may include an incomplete completion-marker prefix; it is retained because it cannot be distinguished from command diagnostics.", - "Launch provenance records the original and resolved startup paths; it does not establish the current target after unrestricted debugger commands.", + expect.stringMatching(/target identity.*not established/iu), + expect.stringMatching( + /Rizin exited.*output is partial.*completion is unknown/iu, + ), + expect.stringMatching(/unrestricted.*shell.*network.*mutate/iu), + expect.stringMatching( + /incomplete completion.marker prefix.*diagnostics/iu, + ), + expect.stringMatching( + /launch provenance.*does not establish.*current target/iu, + ), ]); } }, diff --git a/tests/boundary/providers/RizinDebugSessionReviewRegressions.test.ts b/tests/boundary/providers/RizinDebugSessionReviewRegressions.test.ts index 92c07fcf84..ae93aff599 100644 --- a/tests/boundary/providers/RizinDebugSessionReviewRegressions.test.ts +++ b/tests/boundary/providers/RizinDebugSessionReviewRegressions.test.ts @@ -447,8 +447,10 @@ process.stdin.on("data", (chunk) => { }, }); if (executed.ok) - expect(executed.value.evidence.limitations).toContain( - "Launch provenance records the original and resolved startup paths; it does not establish the current target after unrestricted debugger commands.", + expect(executed.value.evidence.limitations).toContainEqual( + expect.stringMatching( + /launch provenance.*does not establish.*current target/iu, + ), ); }, ); @@ -907,8 +909,8 @@ process.stdin.on("data", (chunk) => { const partialEvidence = evidenceSchema.parse( exited.error.partialObservation, ); - expect(partialEvidence.limitations.join(" ")).toContain( - "Rizin exited before the command-completion marker; retained frame output is partial and command completion is unknown.", + expect(partialEvidence.limitations.join(" ")).toMatch( + /Rizin exited.*output is partial.*completion is unknown/iu, ); } const writerPid = Number(await readFile(writerPidPath, "utf8")); diff --git a/tests/boundary/providers/RizinTimeoutEvidence.test.ts b/tests/boundary/providers/RizinTimeoutEvidence.test.ts index 8c9d4484b6..4e100b294e 100644 --- a/tests/boundary/providers/RizinTimeoutEvidence.test.ts +++ b/tests/boundary/providers/RizinTimeoutEvidence.test.ts @@ -51,11 +51,13 @@ it("preserves every field of partial Rizin timeout Evidence", () => { unit: "ms", }); expect(evidence.limitations).toEqual([ - "The current debugger target identity is not established by the Rizin session identifier.", + expect.stringMatching(/target identity.*not established/iu), rizinCommandTimeoutLimitation(deadline), - "The owned debugger session was stopped because output correlation could no longer be guaranteed.", - "The command is unrestricted and may access local files, the shell, network, or mutate the target.", - "Interrupted output may include an incomplete completion-marker prefix; it is retained because it cannot be distinguished from command diagnostics.", - "Launch provenance records the original and resolved startup paths; it does not establish the current target after unrestricted debugger commands.", + expect.stringMatching(/session.*stopped.*output correlation/iu), + expect.stringMatching(/unrestricted.*shell.*network.*mutate/iu), + expect.stringMatching(/incomplete completion.marker prefix.*diagnostics/iu), + expect.stringMatching( + /launch provenance.*does not establish.*current target/iu, + ), ]); }); diff --git a/tests/boundary/providers/cutterBridgeRequestLimit.test.ts b/tests/boundary/providers/cutterBridgeRequestLimit.test.ts index 625d34ae52..2c9b64e986 100644 --- a/tests/boundary/providers/cutterBridgeRequestLimit.test.ts +++ b/tests/boundary/providers/cutterBridgeRequestLimit.test.ts @@ -388,7 +388,7 @@ it("accepts a Cutter response at the ceiling and excludes the terminating LF", a message: expect.stringContaining("8192"), }, limitations: expect.arrayContaining([ - "The command completed or may have completed, but output exceeded the bridge transport limit and was omitted.", + expect.stringMatching(/output.*transport limit.*omitted/iu), ]), }, }, diff --git a/tests/boundary/providers/ida/calleeRelationships.test.ts b/tests/boundary/providers/ida/calleeRelationships.test.ts index d459413c15..ca40caf93c 100644 --- a/tests/boundary/providers/ida/calleeRelationships.test.ts +++ b/tests/boundary/providers/ida/calleeRelationships.test.ts @@ -37,8 +37,7 @@ it("keeps unknown caller coverage and callee diagnostics in an ARM64 dossier", a if (!result.ok) throw result.error; expect(result.value.relationshipCoverage).toMatchObject({ status: "unknown", - reason: - "Callers: Upstream xrefs do not establish direct caller relationships.", + reason: expect.stringMatching(/xrefs.*not establish.*caller/iu), diagnostics: expect.arrayContaining([ expect.stringContaining("targets IDA does not recognize as functions"), ]), diff --git a/tests/boundary/providers/managed/managedMemberComparisonFromMetadata.test.ts b/tests/boundary/providers/managed/managedMemberComparisonFromMetadata.test.ts index d703a0b58f..9fb44be6fd 100644 --- a/tests/boundary/providers/managed/managedMemberComparisonFromMetadata.test.ts +++ b/tests/boundary/providers/managed/managedMemberComparisonFromMetadata.test.ts @@ -183,8 +183,10 @@ describe("managed comparison selected scope agreement", () => { expect(explicit.result.selection).not.toEqual(bare.result.selection); const comparison = compareManagedMembers(bare, explicit); expect(comparison.coverage.status).toBe("complete-within-inputs"); - expect(comparison.limitations).toContain( - "Both observations cover only the selected declaring types (Fixture.Inner, Inner); member totals and statuses describe those types, not the whole assembly.", + expect(comparison.limitations).toContainEqual( + expect.stringMatching( + /selected declaring types.*Fixture\.Inner.*Inner.*not the whole assembly/iu, + ), ); }); @@ -220,8 +222,8 @@ describe("managed comparison selected scope agreement", () => { unchanged: 0, }); expect(comparison.matching.unmatched).toBe(4); - expect(comparison.limitations).toContain( - "The observations select different declaring types; a member outside the other side's selection is unknown, not added or removed.", + expect(comparison.limitations).toContainEqual( + expect.stringMatching(/different declaring types.*unknown/iu), ); }); @@ -259,8 +261,8 @@ describe("managed comparison selected scope agreement", () => { "Selected declaring-type scope agreement is unknown", ), ); - expect(comparison.limitations).not.toContain( - "The observations select different declaring types; a member outside the other side's selection is unknown, not added or removed.", + expect(comparison.limitations).not.toContainEqual( + expect.stringMatching(/different declaring types/iu), ); }, ); @@ -1863,8 +1865,8 @@ describe("managed member comparison of selected types", () => { right: null, }); expect(result.coverage.status).toBe("partial"); - expect(result.limitations).toContain( - "The observations select different declaring types; a member outside the other side's selection is unknown, not added or removed.", + expect(result.limitations).toContainEqual( + expect.stringMatching(/different declaring types.*unknown/iu), ); }); @@ -1909,8 +1911,10 @@ describe("managed member comparison of selected types", () => { expect(result.methods[0]).toMatchObject({ status: "unchanged" }); expect(result.coverage.status).toBe("complete-within-inputs"); - expect(result.limitations).toContain( - "Both observations cover only the selected declaring types (Fixture.Program); member totals and statuses describe those types, not the whole assembly.", + expect(result.limitations).toContainEqual( + expect.stringMatching( + /selected declaring types.*Fixture\.Program.*not the whole assembly/iu, + ), ); }); }); @@ -3053,8 +3057,8 @@ describe("managed member comparison of resolved signatures", () => { expect(result.fields).toEqual([ expect.objectContaining({ status: "unchanged" }), ]); - expect(result.limitations).toContain( - "At least one managed member observation does not attest signature type-name resolution, so signatures compare by raw bytes, which name types by build-local metadata row.", + expect(result.limitations).toContainEqual( + expect.stringMatching(/signature type.name resolution.*raw bytes/iu), ); }); }); @@ -3283,8 +3287,8 @@ describe("managed member comparison limitations for resolved signatures", () => }; const result = compareManagedMembers(empty, empty); - expect(result.limitations).not.toContain( - "At least one managed member observation does not attest signature type-name resolution, so signatures compare by raw bytes, which name types by build-local metadata row.", + expect(result.limitations).not.toContainEqual( + expect.stringMatching(/does not attest signature type.name resolution/iu), ); }); }); diff --git a/tests/boundary/providers/native/artifactProvider.extractionCompleteness.test.ts b/tests/boundary/providers/native/artifactProvider.extractionCompleteness.test.ts index bdeebb9144..2ef366f3bb 100644 --- a/tests/boundary/providers/native/artifactProvider.extractionCompleteness.test.ts +++ b/tests/boundary/providers/native/artifactProvider.extractionCompleteness.test.ts @@ -187,9 +187,7 @@ it expect(result.limitations).toContainEqual( expect.stringContaining("not valid UTF-8"), ); - expect(result.limitations).toContainEqual( - expect.stringContaining("source traversal coverage is unknown"), - ); + expect(result.coverage.status).toBe("unknown"); expect( result.limitations.some((value) => value.startsWith("All regular files")), ).toBe(false); @@ -229,8 +227,9 @@ it.each(["inventory", "materialization"] as const)( stage = "materialization"; const result = await materialize(); expect(result.limitations).toContain(limitation); + expect(result.coverage.status).toBe("unknown"); expect(result.limitations).toContainEqual( - expect.stringContaining("source traversal coverage is unknown"), + expect.stringMatching(/traversal coverage.*unknown.*not.*complete/iu), ); expect( result.limitations.some((value) => @@ -289,9 +288,7 @@ it("normalizes an NFD omission once across inventory and materialization", async value.startsWith("Artifact traversal omitted"), ), ).toEqual([omission]); - expect(result.limitations).toContainEqual( - expect.stringContaining("source traversal coverage is unknown"), - ); + expect(result.coverage.status).toBe("unknown"); expect(await readFile(join(output, "known.txt"), "utf8")).toBe( "retained bytes\n", ); diff --git a/tests/boundary/providers/native/lipoFieldOwnership.test.ts b/tests/boundary/providers/native/lipoFieldOwnership.test.ts index f369a691c9..df4eb6eac1 100644 --- a/tests/boundary/providers/native/lipoFieldOwnership.test.ts +++ b/tests/boundary/providers/native/lipoFieldOwnership.test.ts @@ -104,9 +104,7 @@ it.each(["absent", "valid", "uninterpretable"] as const)( expect(entries[0]?.limitations).toEqual( mode === "valid" ? [] - : [ - "Slice range was derived from structural Mach-O facts because lipo omitted it.", - ], + : [expect.stringMatching(/structural Mach.O.*lipo.*omitted/iu)], ); } } finally { diff --git a/tests/boundary/providers/native/machoSliceReader.test.ts b/tests/boundary/providers/native/machoSliceReader.test.ts index ad433a2cfa..2ea1237108 100644 --- a/tests/boundary/providers/native/machoSliceReader.test.ts +++ b/tests/boundary/providers/native/machoSliceReader.test.ts @@ -157,8 +157,10 @@ describe("Mach-O slice reader with system lipo", () => { ["slices/arm64", 4096, 32], ["slices/ppc", 8192, 32], ]); - expect(entries[1]?.limitations).toContain( - "Structural Mach-O facts were unsupported (big-endian Mach-O slices are not supported); slice range is based on lipo output.", + expect(entries[1]?.limitations).toContainEqual( + expect.stringMatching( + /big.endian Mach.O.*not supported.*slice range.*lipo/iu, + ), ); }, ); diff --git a/tests/boundary/providers/native/nativePlistTextSource.test.ts b/tests/boundary/providers/native/nativePlistTextSource.test.ts index b077cc8f2a..2a67cb636e 100644 --- a/tests/boundary/providers/native/nativePlistTextSource.test.ts +++ b/tests/boundary/providers/native/nativePlistTextSource.test.ts @@ -76,7 +76,7 @@ it.for([ reason: "unrepresentable", }, limitations: [ - expect.stringContaining("1 XML scalar value(s) could not be established"), + expect.stringMatching(/1 XML scalar.*could not be established/iu), ], conversions: [], }, diff --git a/tests/boundary/providers/native/plistSourceFormat.test.ts b/tests/boundary/providers/native/plistSourceFormat.test.ts index 54d74b570b..837a57b701 100644 --- a/tests/boundary/providers/native/plistSourceFormat.test.ts +++ b/tests/boundary/providers/native/plistSourceFormat.test.ts @@ -335,7 +335,7 @@ it("skips XML conversion for safe integers, signed zero, exponential reals and n Text: "9007199254740993", }); expect(result.limitations).toEqual([ - '2 negative-zero real value(s) are reported as { "$plist_type": "real", "value": "-0" } because a JSON number cannot carry the sign of zero.', + expect.stringMatching(/2 negative.zero.*sign of zero/iu), ]); expect(result.provenance.map(({ command }) => command)).toEqual([ ["/usr/bin/file", "-b", "$PLIST"],