From 475f09d5ca4f854cbc634074d5534717d58c6664 Mon Sep 17 00:00:00 2001 From: morluto <76467478+morluto@users.noreply.github.com> Date: Sun, 25 Oct 2026 07:31:20 +0800 Subject: [PATCH] chore(lint): enforce buffer and cancellation regression guards --- .oxlintrc.json | 6 + docs/testing.md | 21 ++ scripts/lint/bindings.mjs | 12 ++ scripts/lint/cancellation-cause.d.mts | 4 + scripts/lint/cancellation-cause.mjs | 200 ++++++++++++++++++ scripts/lint/index.mjs | 10 + scripts/lint/no-buffer-deep-equality.d.mts | 4 + scripts/lint/no-buffer-deep-equality.mjs | 166 +++++++++++++++ src/apkSignature/ApkSignatureProvider.test.ts | 2 +- src/apkSignature/apkCertificateFacts.test.ts | 11 +- src/apktool/ApktoolProvider.test.ts | 2 +- .../environment/environmentBuild.test.ts | 10 +- .../environmentBundleWrite.test.ts | 14 +- .../environment/environmentExport.test.ts | 39 ++-- .../environmentInputSnapshot.test.ts | 9 +- .../MachOSliceArtifactReader.test.ts | 2 +- src/artifacts/NodeSeaArtifactReader.test.ts | 7 +- src/artifacts/ZipArtifactReader.test.ts | 4 +- .../apple/KeyedArchiveReader.test.ts | 21 +- src/artifacts/apple/PlistScalars.test.ts | 15 +- .../PyInstallerArtifactReader.test.ts | 49 +++-- .../pyinstaller/PyInstallerBoundaries.test.ts | 4 +- .../PyInstallerFormatBoundaries.test.ts | 15 +- src/artifacts/pyinstaller/PyzArchive.test.ts | 2 +- src/browser/BrowserScenario.test.ts | 19 +- .../protobuf/ProtobufBodyTrailer.test.ts | 6 +- .../network/PlaywrightScenarioNetwork.test.ts | 17 +- src/domain/apple/plistValue.test.ts | 2 +- .../nativeAotPe.discoveryAccounting.test.ts | 6 +- .../native/nativeAotPe.sectionRanges.test.ts | 4 +- src/domain/native/nativeAotPe.test.ts | 6 +- src/dotnet/ManagedMetadataHeaps.test.ts | 9 +- src/dotnet/ManagedPeFixtureLayout.test.ts | 28 ++- src/electron/ElectronFuseArchitecture.test.ts | 2 +- src/flutter/DartElf.test.ts | 4 +- src/ghidra/GhidraLaunchFailure.test.ts | 9 +- .../extensions/GhidraExtensions.test.ts | 9 +- src/gpu/GpuPayloadCodec.test.ts | 9 +- src/hopper/HopperImageCopy.test.ts | 4 +- src/hopper/HopperMachOImage.test.ts | 34 +-- src/ida/IdaSessionClient.test.ts | 6 +- src/native/parsers/plist.test.ts | 2 +- .../pwndbg/CoreFileMappingAuthority.test.ts | 2 +- src/process/ExecFileOutput.test.ts | 6 +- .../ReferenceSourceReaderFile.test.ts | 4 +- src/wasm/WabtArtifactProvider.test.ts | 15 +- src/wasm/WabtString.test.ts | 4 +- tests/acceptance/apkZip64FramingCli.test.ts | 4 +- .../appleBundleTraversalCoverage.test.ts | 2 +- .../applications/binaryPlistDates.test.ts | 19 +- .../hopperPublisherRejectionCli.test.ts | 2 +- .../interfaceBuilderUIKitKeyedArchive.test.ts | 2 +- .../javascriptSourceSelectionCli.test.ts | 4 +- .../applications/jvmAdmissionIdentity.test.ts | 4 +- .../keyedArchiveScalarKinds.test.ts | 8 +- .../keyedArchiveXmlNumbers.test.ts | 4 +- .../keyedArchiveXmlScalars.test.ts | 2 +- .../mobileDirectoryComponents.test.ts | 4 +- .../nativePlistBinaryScalars.test.ts | 4 +- .../applications/nativePlistNesting.test.ts | 2 +- .../applications/nibFieldIdentity.test.ts | 2 +- .../applications/packageUrlUnicodeCli.test.ts | 2 +- .../applications/peResources.test.ts | 8 +- .../resourceOnlyPlistSelection.test.ts | 8 +- .../artifactExtractionCoverage.test.ts | 8 +- tests/acceptance/devicetreeCli.test.ts | 2 +- tests/acceptance/elfNotesCli.test.ts | 6 +- tests/acceptance/erlangBeamCli.test.ts | 11 +- tests/acceptance/goBinaryCli.test.ts | 7 +- .../goBinaryPointerMappingsCli.test.ts | 2 +- tests/acceptance/godotPck.test.ts | 2 +- .../acceptance/harmonyArtifactJourney.test.ts | 2 +- tests/acceptance/il2cppMetadataCli.test.ts | 10 +- tests/acceptance/managedMetadataDelta.test.ts | 2 +- tests/acceptance/managedSelectedScope.test.ts | 4 +- tests/acceptance/modelArtifactCli.test.ts | 4 +- .../acceptance/nativeDispatchSnapshot.test.ts | 8 +- tests/acceptance/nodeSeaCli.test.ts | 4 +- tests/acceptance/pyinstallerArchive.test.ts | 63 ++++-- tests/acceptance/rustBinaryCli.test.ts | 2 +- tests/acceptance/rustUnnamedSections.test.ts | 2 +- .../clientConfigurationDiagnosticsCli.test.ts | 2 +- .../setup/geminiWorkspaceTrust.test.ts | 28 ++- .../setup/selectedSkillSetup.test.ts | 27 ++- .../setup/unqualifiedDoctorSkill.test.ts | 10 +- tests/acceptance/sqliteDatabaseCli.test.ts | 10 +- .../tools/nativeFunctionMatching.test.ts | 2 +- .../tools/protobufDescriptorFiles.test.ts | 8 +- .../tools/protobufDescriptorIdentity.test.ts | 8 +- .../tools/protobufRegistryIdentity.test.ts | 8 +- .../RecordedCrashStructureBindings.test.ts | 4 +- .../recordedCrashProvider.test.ts | 16 +- tests/boundary/browser/sazWebCapture.test.ts | 10 +- .../cli/artifactCallerSelection.test.ts | 2 +- .../cli/commandEvidenceDelivery.test.ts | 2 +- .../boundary/cli/environmentBuildCli.test.ts | 10 +- .../boundary/cli/environmentExportCli.test.ts | 30 ++- .../cli/environmentExportInputsCli.test.ts | 9 +- .../cli/idaDoctorRegistration.test.ts | 4 +- .../cli/il2cppSharedSuffixNames.test.ts | 2 +- .../cli/keyedArchivePrototypeKeyCli.test.ts | 2 +- .../cli/managedMemberSummaryCli.test.ts | 2 +- tests/boundary/cli/nativePlistCli.test.ts | 2 +- .../boundary/cli/referenceSourceUtf8.test.ts | 7 +- .../evm/evmoleInterfaceProvider.test.ts | 2 +- .../appleDispatchMetadataStrings.test.ts | 4 +- .../artifactLiteralBackslash.test.ts | 4 +- .../filesystem/artifactZipIntegrity.test.ts | 2 +- .../filesystem/asarHeaderAdmission.test.ts | 2 +- .../filesystem/asarListingAdmission.test.ts | 18 +- .../boundaryLintConfiguration.test.ts | 82 +++++++ .../filesystem/boundedFileBytes.test.ts | 5 +- .../filesystem/bufferDeepEqualityLint.test.ts | 137 ++++++++++++ .../filesystem/cancellationCauseLint.test.ts | 135 ++++++++++++ .../filesystem/ciPortableBuildCache.test.ts | 4 +- .../claudeDesktopClientConfiguration.test.ts | 2 +- .../filesystem/clientConfigurationBom.test.ts | 29 ++- .../clientConfigurationTransactions.test.ts | 4 +- .../boundary/filesystem/dosComTarget.test.ts | 4 +- .../filesystem/fileHandleChunks.test.ts | 11 +- .../ghidraProfileArtifactOwnership.test.ts | 2 +- .../keyedArchiveNumberMetadata.test.ts | 2 +- .../keyedArchiveScalarIdentity.test.ts | 2 +- .../filesystem/rawBinaryTarget.test.ts | 2 +- .../regularFileCancellation.test.ts | 2 +- .../safeOutputTreePublication.test.ts | 9 +- .../setupClientConfigCleanupOwnership.test.ts | 6 +- tests/boundary/filesystem/setupSkill.test.ts | 4 +- .../filesystem/setupSkillPublication.test.ts | 12 +- .../filesystem/stableArtifact.test.ts | 9 +- .../filesystem/staticCheckReports.test.ts | 7 +- .../filesystem/targetDiagnostics.test.ts | 2 +- .../filesystem/verifierCommandOutput.test.ts | 19 +- .../filesystem/verifierFileSnapshots.test.ts | 4 +- .../filesystem/webScriptExport.test.ts | 60 ++++-- .../firmware/firmwarePublicationPaths.test.ts | 2 +- .../firmware/firmwareReportFiles.test.ts | 2 +- .../firmwareWorkspaceRecovery.test.ts | 2 +- tests/boundary/gpu/codeObjects.test.ts | 4 +- .../boundary/il2cpp/il2cppBinaryFile.test.ts | 4 +- .../il2cpp/il2cppMappingOwnership.test.ts | 10 +- .../mcp/asarListingAdmissionMcp.test.ts | 2 +- .../mcp/asarUnpackedExtractionMcp.test.ts | 4 +- tests/boundary/mcp/elfNotesMcp.test.ts | 4 +- .../boundary/mcp/environmentBundleMcp.test.ts | 17 +- tests/boundary/mcp/ghidraReopen.test.ts | 2 +- tests/boundary/mcp/goBinaryViews.test.ts | 2 +- tests/boundary/mcp/peLoadConfigMcp.test.ts | 2 +- .../mcp/selectedSnapshotCleanupMcp.test.ts | 4 +- tests/boundary/mcp/sessionMcp.test.ts | 2 +- tests/boundary/mcp/wasmArtifactMcp.test.ts | 9 +- .../mcp/webScriptSelectionMcp.test.ts | 9 +- tests/boundary/pcap/pcapProvider.test.ts | 15 +- .../process/modelFixtureShutdown.test.ts | 4 +- .../nativeVerifierCommandOwnership.test.ts | 2 +- .../nativeVerifierPreflightOwnership.test.ts | 4 +- .../process/packagePackIsolation.test.ts | 2 +- .../process/packedBridgeVerification.test.ts | 4 +- .../process/verifierCommandSemantics.test.ts | 10 +- .../adb/adbHashCleanupConsumers.test.ts | 9 +- .../adb/adbInputSnapshotOwnership.test.ts | 22 +- .../adbScreenPublicationCancellation.test.ts | 24 ++- .../ghidra/ghidraTargetSnapshot.test.ts | 4 +- .../providers/hopper/bridgeLauncher.test.ts | 2 +- ...actProvider.extractionCompleteness.test.ts | 18 +- .../artifactProvider.machOSlices.test.ts | 2 +- .../artifactProvider.zipCancellation.test.ts | 9 +- .../native/nativePlistTextSource.test.ts | 2 +- .../nativeSignatureTargetBinding.test.ts | 19 +- .../linuxHopperPublisherVerification.test.ts | 4 +- .../signals/recordingProvider.test.ts | 2 +- .../sqlite/sqliteDatabaseInspection.test.ts | 2 +- .../sqlite/sqliteDatabaseProvider.test.ts | 22 +- .../sqlite/sqliteDatabaseSnapshot.test.ts | 10 +- tests/fixtures/machoArmSlices.ts | 4 +- .../artifactFileSourceLifecycle.test.ts | 2 +- .../asarSnapshotOperation.test.ts | 4 +- .../cutterDescriptorLifetime.test.ts | 7 +- .../ghidraImportSnapshotLifetime.test.ts | 2 +- .../mainStartupCancellation.test.ts | 4 +- .../regularFileNoFollowFallback.test.ts | 4 +- .../setupConfigurationConflicts.test.ts | 2 +- 182 files changed, 1653 insertions(+), 497 deletions(-) create mode 100644 scripts/lint/bindings.mjs create mode 100644 scripts/lint/cancellation-cause.d.mts create mode 100644 scripts/lint/cancellation-cause.mjs create mode 100644 scripts/lint/index.mjs create mode 100644 scripts/lint/no-buffer-deep-equality.d.mts create mode 100644 scripts/lint/no-buffer-deep-equality.mjs create mode 100644 tests/boundary/filesystem/boundaryLintConfiguration.test.ts create mode 100644 tests/boundary/filesystem/bufferDeepEqualityLint.test.ts create mode 100644 tests/boundary/filesystem/cancellationCauseLint.test.ts diff --git a/.oxlintrc.json b/.oxlintrc.json index a4618ad114..0a760d3d6b 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -1,7 +1,13 @@ { "$schema": "./node_modules/oxlint/configuration_schema.json", "plugins": ["typescript", "import", "unicorn", "oxc"], + "jsPlugins": [{ "name": "rea", "specifier": "./scripts/lint/index.mjs" }], "rules": { + "rea/no-buffer-deep-equality": "error", + "rea/cancellation-cause": "error", + "no-async-promise-executor": "error", + "no-unreachable": "error", + "no-unsafe-optional-chaining": "error", "complexity": ["warn", 20], "max-depth": ["warn", 4], "max-lines": [ diff --git a/docs/testing.md b/docs/testing.md index b7b8f0750d..7af365f6f2 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -146,6 +146,27 @@ boundaries and architecture guards enforce their declared import and source rules. Passing them does not prove that two parsers agree, a wrapper is still needed at runtime, or cleanup has one owner. +The lint gate treats `no-async-promise-executor`, `no-unreachable`, and +`no-unsafe-optional-chaining` as errors. These catch lost async executor failures, +dead code, and unsafe uses of optional-chain results. `no-unsafe-finally` remains +a warning because cleanup paths can deliberately throw aggregated failures; +review whether they preserve the primary failure. Two local rules +cover narrower observed failures: `rea/no-buffer-deep-equality` flags Vitest +deep comparisons of Buffers from recognized constructors and file reads, +including simple `const` aliases; `rea/cancellation-cause` requires an explicit +cause when a directly checked caller `AbortSignal` is converted to an +`AnalysisCancelledError`. The rules do not infer arbitrary Buffer types, +follow indirect cancellation helpers, or prove that an explicit cause equals +the caller's reason. Keep focused boundary and lifecycle tests for those facts. + +Type-aware linting remains outside the routine gate: a full source, test, and +script scan exceeded the bounded process-family memory envelope, including +with Go runtime limits. Future scoped evaluation can consider +`typescript/no-floating-promises`, `typescript/return-await` with +`error-handling-correctness-only`, `typescript/no-misused-promises`, and +`typescript/switch-exhaustiveness-check`. None replaces behavioral checks for +source identity, omission versus unknown, or Evidence provenance. + Routine iterations and rebases need focused regressions and relevant checks. Before handing off a PR, run `npm run check` and generated-document checks when applicable; CI owns the full suite and coverage. Use the full local gate for diff --git a/scripts/lint/bindings.mjs b/scripts/lint/bindings.mjs new file mode 100644 index 0000000000..400304c6a0 --- /dev/null +++ b/scripts/lint/bindings.mjs @@ -0,0 +1,12 @@ +/** Resolve the lexical binding instead of treating a matching name as an API. */ +export const findBinding = (sourceCode, identifier) => { + for ( + let scope = sourceCode.getScope(identifier); + scope !== null; + scope = scope.upper + ) { + const variable = scope.set.get(identifier.name); + if (variable !== undefined) return variable; + } + return undefined; +}; diff --git a/scripts/lint/cancellation-cause.d.mts b/scripts/lint/cancellation-cause.d.mts new file mode 100644 index 0000000000..faf40667bb --- /dev/null +++ b/scripts/lint/cancellation-cause.d.mts @@ -0,0 +1,4 @@ +import type { RuleTester } from "oxlint/plugins-dev"; + +declare const cancellationCauseRule: Parameters[1]; +export default cancellationCauseRule; diff --git a/scripts/lint/cancellation-cause.mjs b/scripts/lint/cancellation-cause.mjs new file mode 100644 index 0000000000..050c76df0b --- /dev/null +++ b/scripts/lint/cancellation-cause.mjs @@ -0,0 +1,200 @@ +import { findBinding } from "./bindings.mjs"; + +/** + * Require an explicit cancellation reason when a directly checked caller signal + * is converted to an AnalysisCancelledError. This is deliberately syntactic: + * internal shutdown and cancellation forwarded through other owners are outside + * the rule. + */ + +const coreModule = /(?:^|\/)analysisErrorCore(?:\.js|\.ts)?$/; + +const unwrapChain = (node) => + node?.type === "ChainExpression" ? node.expression : node; + +const propertyName = (property) => + property.computed + ? property.property.type === "Literal" + ? property.property.value + : undefined + : property.property.name; + +const annotationType = (node) => { + const annotation = node?.typeAnnotation?.typeAnnotation; + return annotation?.type === "TSTypeReference" && + annotation.typeName.type === "Identifier" + ? annotation.typeName + : undefined; +}; + +const soleDefinition = (variable) => + variable?.defs.length === 1 ? variable.defs[0] : undefined; + +const isKnownType = (node, kind, context) => { + const type = annotationType(node); + if (type === undefined) return false; + const definition = soleDefinition(findBinding(context.sourceCode, type)); + if (kind === "AbortSignal") + return type.name === "AbortSignal" && definition === undefined; + return ( + definition?.type === "ImportBinding" && + definition.parent?.type === "ImportDeclaration" && + /(?:^|\/)AnalysisProvider(?:\.js|\.ts)?$/.test( + definition.parent.source.value, + ) && + definition.node.type === "ImportSpecifier" && + definition.node.imported.name === "ExecutionOptions" + ); +}; + +const unmodified = (variable) => + variable !== undefined && + variable.references.every( + (reference) => !reference.isWrite() || reference.init, + ); + +const isOptionsSignal = (node, context, seen) => { + node = unwrapChain(node); + if (node?.type !== "MemberExpression" || propertyName(node) !== "signal") + return false; + return isKnownBinding(node.object, "ExecutionOptions", context, seen); +}; + +const isKnownBinding = (node, kind, context, seen) => { + if (node?.type !== "Identifier") return false; + const variable = findBinding(context.sourceCode, node); + if (!unmodified(variable) || seen.has(variable)) return false; + seen.add(variable); + const definition = soleDefinition(variable); + if (definition?.type === "Parameter") + return isKnownType(definition.name, kind, context); + if (definition?.type !== "Variable" || definition.parent?.kind !== "const") + return false; + const declaration = definition.node; + if (isKnownType(declaration.id, kind, context)) return true; + return kind === "AbortSignal" + ? isKnownSignal(declaration.init, context, seen) + : false; +}; + +const isKnownSignal = (node, context, seen = new Set()) => + isKnownBinding(unwrapChain(node), "AbortSignal", context, seen) || + isOptionsSignal(node, context, seen); + +const isAbortRead = (node, context) => { + node = unwrapChain(node); + return ( + node?.type === "MemberExpression" && + propertyName(node) === "aborted" && + isKnownSignal(node.object, context) + ); +}; + +const isTrueLiteral = (node) => node?.type === "Literal" && node.value === true; + +const positiveAbortGuard = (test, context) => { + if (isAbortRead(test, context)) return true; + if ( + test?.type === "BinaryExpression" && + (test.operator === "===" || test.operator === "==") + ) + return ( + (isAbortRead(test.left, context) && isTrueLiteral(test.right)) || + (isTrueLiteral(test.left) && isAbortRead(test.right, context)) + ); + if (test?.type === "LogicalExpression" && test.operator === "&&") + return ( + positiveAbortGuard(test.left, context) || + positiveAbortGuard(test.right, context) + ); + return false; +}; + +const isCanonicalConstructor = (node, context) => { + if (node?.type !== "Identifier") return false; + const definition = soleDefinition(findBinding(context.sourceCode, node)); + return ( + definition?.type === "ImportBinding" && + definition.parent?.type === "ImportDeclaration" && + coreModule.test(definition.parent.source.value) && + definition.node.type === "ImportSpecifier" && + definition.node.imported.name === "AnalysisCancelledError" + ); +}; + +const hasExplicitCause = (node) => { + const options = node.arguments[1]; + if (options !== undefined && options.type !== "ObjectExpression") return true; // Forwarded context is owned elsewhere. + if (options === undefined) return false; + return options.properties.some( + (property) => + property.type === "SpreadElement" || + (property.type === "Property" && + (property.computed + ? property.key.type !== "Literal" || property.key.value === "cause" + : (property.key.type === "Identifier" + ? property.key.name + : property.key.value) === "cause")), + ); +}; + +const inspectExpression = (expression, context) => { + if (expression?.type === "NewExpression") { + if ( + isCanonicalConstructor(expression.callee, context) && + !hasExplicitCause(expression) + ) + context.report({ + node: expression, + messageId: "missingCause", + }); + return; + } + if (expression?.type === "CallExpression") { + // Common Result adapter: return err(new AnalysisCancelledError(...)). + for (const argument of expression.arguments) + inspectExpression(argument, context); + } else if (expression?.type === "AssignmentExpression") { + inspectExpression(expression.right, context); + } +}; + +const inspectBranch = (branch, context) => { + if (branch.type === "BlockStatement") { + for (const statement of branch.body) inspectBranch(statement, context); + } else if (branch.type === "VariableDeclaration") { + for (const declaration of branch.declarations) + inspectExpression(declaration.init, context); + } else if ( + branch.type === "ThrowStatement" || + branch.type === "ReturnStatement" + ) { + inspectExpression(branch.argument, context); + } else if (branch.type === "ExpressionStatement") { + inspectExpression(branch.expression, context); + } +}; + +const cancellationCauseRule = { + meta: { + type: "problem", + docs: { + description: + "Preserve a directly checked caller AbortSignal reason in AnalysisCancelledError", + }, + messages: { + missingCause: + "Pass the checked AbortSignal's reason as an explicit cancellation cause.", + }, + }, + create(context) { + return { + IfStatement(node) { + if (positiveAbortGuard(node.test, context)) + inspectBranch(node.consequent, context); + }, + }; + }, +}; + +export default cancellationCauseRule; diff --git a/scripts/lint/index.mjs b/scripts/lint/index.mjs new file mode 100644 index 0000000000..c04cbd95c4 --- /dev/null +++ b/scripts/lint/index.mjs @@ -0,0 +1,10 @@ +import noBufferDeepEquality from "./no-buffer-deep-equality.mjs"; +import cancellationCause from "./cancellation-cause.mjs"; + +export default { + meta: { name: "rea" }, + rules: { + "no-buffer-deep-equality": noBufferDeepEquality, + "cancellation-cause": cancellationCause, + }, +}; diff --git a/scripts/lint/no-buffer-deep-equality.d.mts b/scripts/lint/no-buffer-deep-equality.d.mts new file mode 100644 index 0000000000..08eb75e22b --- /dev/null +++ b/scripts/lint/no-buffer-deep-equality.d.mts @@ -0,0 +1,4 @@ +import type { RuleTester } from "oxlint/plugins-dev"; + +declare const noBufferDeepEquality: Parameters[1]; +export default noBufferDeepEquality; diff --git a/scripts/lint/no-buffer-deep-equality.mjs b/scripts/lint/no-buffer-deep-equality.mjs new file mode 100644 index 0000000000..8aaff86bb3 --- /dev/null +++ b/scripts/lint/no-buffer-deep-equality.mjs @@ -0,0 +1,166 @@ +import { findBinding } from "./bindings.mjs"; + +const memberName = (node) => + node?.type === "MemberExpression" && + !node.computed && + node.property.type === "Identifier" + ? node.property.name + : null; + +const unwrap = (node) => { + while ( + node?.type === "TSAsExpression" || + node?.type === "TSSatisfiesExpression" || + node?.type === "TSNonNullExpression" || + node?.type === "TSTypeAssertion" || + node?.type === "ParenthesizedExpression" + ) + node = node.expression; + return node; +}; + +const importedKind = (variable) => { + const definition = variable?.defs[0]; + if (definition?.type !== "ImportBinding") return null; + const source = definition.parent?.source?.value; + const node = definition.node; + if (node?.type === "ImportNamespaceSpecifier") { + if (source === "node:fs/promises") return "fsPromises"; + if (source === "node:fs") return "fs"; + return null; + } + if (node?.type !== "ImportSpecifier") return null; + const imported = node.imported?.name ?? node.imported?.value; + if (source === "vitest" && imported === "expect") return "expect"; + if (source === "node:buffer" && imported === "Buffer") return "Buffer"; + if (source === "node:fs/promises" && imported === "readFile") + return "readFile"; + if (source === "node:fs" && imported === "readFileSync") + return "readFileSync"; + return null; +}; + +const constInitializer = (variable) => { + if (variable?.defs.length !== 1) return null; + const definition = variable.defs[0]; + if ( + definition?.type !== "Variable" || + definition.node?.type !== "VariableDeclarator" || + definition.parent?.kind !== "const" || + variable.references.some( + (reference) => reference.isWrite() && !reference.init, + ) + ) + return null; + return definition.node.init; +}; + +/** Flag byte comparisons that make Vitest enumerate a Node Buffer. */ +const noBufferDeepEquality = { + meta: { + type: "suggestion", + messages: { + nativeBytes: + "Compare Node Buffers with Buffer.equals or Buffer.compare; deep equality can enumerate every byte and exhaust memory.", + }, + }, + create(context) { + const { sourceCode } = context; + const symbolKind = (expression, seen = new Set()) => { + const node = unwrap(expression); + if (node?.type === "Identifier") { + const variable = findBinding(sourceCode, node); + if ( + (variable === undefined || variable.defs.length === 0) && + sourceCode.isGlobalReference(node) + ) + return node.name === "Buffer" ? "Buffer" : null; + const imported = importedKind(variable); + if (imported !== null) return imported; + if (variable === undefined || seen.has(variable)) return null; + const initializer = constInitializer(variable); + if (initializer === null) return null; + seen.add(variable); + return symbolKind(initializer, seen); + } + const property = memberName(node); + if (property === null) return null; + const owner = symbolKind(node.object, seen); + if (owner === "fsPromises" && property === "readFile") return "readFile"; + if (owner === "fs" && property === "readFileSync") return "readFileSync"; + if ( + owner === "Buffer" && + ["from", "alloc", "allocUnsafe", "allocUnsafeSlow", "concat"].includes( + property, + ) + ) + return "bufferFactory"; + return null; + }; + const isBuffer = (expression, seen = new Set(), awaited = false) => { + const node = unwrap(expression); + if (node?.type === "AwaitExpression") + return isBuffer(node.argument, seen, true); + if (node?.type === "Identifier") { + const variable = findBinding(sourceCode, node); + if (variable === undefined || seen.has(variable)) return false; + const initializer = constInitializer(variable); + if (initializer === null) return false; + seen.add(variable); + return isBuffer(initializer, seen, awaited); + } + if (node?.type !== "CallExpression" || node.optional) return false; + const kind = symbolKind(node.callee); + return ( + kind === "bufferFactory" || + ((kind === "readFileSync" || (kind === "readFile" && awaited)) && + node.arguments.length === 1 && + node.arguments[0].type !== "SpreadElement") + ); + }; + const expectedCall = (object, settlement = null) => { + const node = unwrap(object); + const property = memberName(node); + if (property === "not") return expectedCall(node.object, settlement); + if (property === "resolves" || property === "rejects") + return expectedCall(node.object, property); + return node?.type === "CallExpression" && + !node.optional && + symbolKind(node.callee) === "expect" + ? { call: node, settlement } + : null; + }; + return { + CallExpression(node) { + if (node.optional) return; + const matcher = memberName(node.callee); + if (matcher !== "toEqual" && matcher !== "toStrictEqual") return; + const assertion = expectedCall(node.callee.object); + if ( + assertion === null || + ![1, 2].includes(assertion.call.arguments.length) || + assertion.call.arguments.some( + (argument) => argument.type === "SpreadElement", + ) || + node.arguments.length !== 1 + ) + return; + if ( + (assertion.settlement !== "rejects" && + isBuffer( + assertion.call.arguments[0], + new Set(), + assertion.settlement === "resolves", + )) || + isBuffer(node.arguments[0]) + ) + context.report({ + node: node.callee.property, + messageId: "nativeBytes", + }); + }, + }; + }, +}; + +export default noBufferDeepEquality; diff --git a/src/apkSignature/ApkSignatureProvider.test.ts b/src/apkSignature/ApkSignatureProvider.test.ts index c9619fc471..f8568b38a4 100644 --- a/src/apkSignature/ApkSignatureProvider.test.ts +++ b/src/apkSignature/ApkSignatureProvider.test.ts @@ -524,7 +524,7 @@ const registerSignatureInputAdmissionCases = () => { corrupt.writeUInt32LE(0x06064b51, endOffset); const restored = Buffer.from(corrupt); restored.writeUInt32LE(0x06064b50, endOffset); - expect(restored).toEqual(valid); + expect(Buffer.compare(restored, valid)).toBe(0); for (const [name, bytes] of [ ["valid", valid], ["restored", restored], diff --git a/src/apkSignature/apkCertificateFacts.test.ts b/src/apkSignature/apkCertificateFacts.test.ts index b5d2adfa9d..8764ed4c84 100644 --- a/src/apkSignature/apkCertificateFacts.test.ts +++ b/src/apkSignature/apkCertificateFacts.test.ts @@ -18,10 +18,13 @@ it("binds readable certificate fields to the exact carried DER despite embedded createHash("sha256").update(outer).digest("hex"), ); expect( - new X509Certificate( - `-----BEGIN CERTIFICATE-----\n${Buffer.from(outer).toString("base64")}\n-----END CERTIFICATE-----\n`, - ).raw, - ).toEqual(Buffer.from(outer)); + Buffer.compare( + new X509Certificate( + `-----BEGIN CERTIFICATE-----\n${Buffer.from(outer).toString("base64")}\n-----END CERTIFICATE-----\n`, + ).raw, + Buffer.from(outer), + ), + ).toBe(0); } }); it.each([ diff --git a/src/apktool/ApktoolProvider.test.ts b/src/apktool/ApktoolProvider.test.ts index 707bd933ed..0c1b1e8424 100644 --- a/src/apktool/ApktoolProvider.test.ts +++ b/src/apktool/ApktoolProvider.test.ts @@ -250,7 +250,7 @@ sdkInfo: coverage: "complete", }, }); - expect(await readFile(apk.path)).toEqual(original); + expect(Buffer.compare(await readFile(apk.path), original)).toBe(0); }); it.each([ diff --git a/src/application/environment/environmentBuild.test.ts b/src/application/environment/environmentBuild.test.ts index 275071433f..f62f5f5302 100644 --- a/src/application/environment/environmentBuild.test.ts +++ b/src/application/environment/environmentBuild.test.ts @@ -150,9 +150,13 @@ const sameTree = async (left: string, right: string): Promise => { const files = await listFiles(left); expect(await listFiles(right)).toEqual(files); for (const file of files) - expect(await readFile(join(left, file)), file).toEqual( - await readFile(join(right, file)), - ); + expect( + Buffer.compare( + await readFile(join(left, file)), + await readFile(join(right, file)), + ), + file, + ).toBe(0); return files.length; }; diff --git a/src/application/environment/environmentBundleWrite.test.ts b/src/application/environment/environmentBundleWrite.test.ts index 8422eb3de8..592316fcea 100644 --- a/src/application/environment/environmentBundleWrite.test.ts +++ b/src/application/environment/environmentBundleWrite.test.ts @@ -66,8 +66,11 @@ it("retains the admitted tarball descriptor and refuses a pathname replacement d error: { _tag: "AnalysisArtifactChangedError", path: tarball }, }); expect( - await readFile(join(directory, "envs/grafana-example/rea-agents.tgz")), - ).toEqual(originalBytes); + Buffer.compare( + await readFile(join(directory, "envs/grafana-example/rea-agents.tgz")), + originalBytes, + ), + ).toBe(0); } finally { writer.mockRestore(); } @@ -158,8 +161,11 @@ it("uses confirmed publication after the output owner's descriptor cleanup compl }); expect(result).toMatchObject({ ok: true, value: { status: "exported" } }); expect( - await readFile(join(directory, "envs/grafana-example/evidence.json")), - ).toEqual(inputs.value.evidence.bytes); + Buffer.compare( + await readFile(join(directory, "envs/grafana-example/evidence.json")), + inputs.value.evidence.bytes, + ), + ).toBe(0); } finally { commit.mockRestore(); } diff --git a/src/application/environment/environmentExport.test.ts b/src/application/environment/environmentExport.test.ts index d14c72b3d2..1691944ab5 100644 --- a/src/application/environment/environmentExport.test.ts +++ b/src/application/environment/environmentExport.test.ts @@ -62,13 +62,19 @@ describe("readEnvironmentExportInputs", () => { ); if (!inputs.ok) throw inputs.error; expect(inputs.value.manifest.path).toBe(files.manifestPath); - expect(inputs.value.manifest.bytes).toEqual( - await readFile(files.manifestPath), - ); + expect( + Buffer.compare( + inputs.value.manifest.bytes, + await readFile(files.manifestPath), + ), + ).toBe(0); expect(inputs.value.manifest.value).toEqual(fixture.manifest); - expect(inputs.value.evidence.bytes).toEqual( - await readFile(files.capturePath), - ); + expect( + Buffer.compare( + inputs.value.evidence.bytes, + await readFile(files.capturePath), + ), + ).toBe(0); expect(inputs.value.evidence.value).toEqual(fixture.capture); }); @@ -151,11 +157,17 @@ const registerBundlePublicationCases = () => { (await readFile(join(directory, file.path))).length, ); expect( - await readFile(join(directory, "envs/grafana-example/manifest.json")), - ).toEqual(await readFile(files.manifestPath)); + Buffer.compare( + await readFile(join(directory, "envs/grafana-example/manifest.json")), + await readFile(files.manifestPath), + ), + ).toBe(0); expect( - await readFile(join(directory, "envs/grafana-example/evidence.json")), - ).toEqual(input.evidence.bytes); + Buffer.compare( + await readFile(join(directory, "envs/grafana-example/evidence.json")), + input.evidence.bytes, + ), + ).toBe(0); expect(await listFiles(root)).toEqual( expect.arrayContaining(["capture.json", "manifest.json"]), ); @@ -183,8 +195,11 @@ const registerBundlePublicationCases = () => { bytes: 7, }); expect( - await readFile(join(directory, "envs/replay-copy/rea-agents.tgz")), - ).toEqual(await readFile(tarball)); + Buffer.compare( + await readFile(join(directory, "envs/replay-copy/rea-agents.tgz")), + await readFile(tarball), + ), + ).toBe(0); expect( await readFile(join(directory, "envs/replay-copy/Dockerfile"), "utf8"), ).toContain("COPY rea-agents.tgz /tmp/"); diff --git a/src/application/environment/environmentInputSnapshot.test.ts b/src/application/environment/environmentInputSnapshot.test.ts index cfb3510077..6739e6e73e 100644 --- a/src/application/environment/environmentInputSnapshot.test.ts +++ b/src/application/environment/environmentInputSnapshot.test.ts @@ -43,9 +43,12 @@ it("seals privately parsed files and shares reported trees across both Evidence expect(webNetworkCaptureSchema.parse(normalized).records[0]?.reported).toBe( reported, ); - expect(inputs.value.evidence.bytes).toEqual( - await readFile(files.capturePath), - ); + expect( + Buffer.compare( + inputs.value.evidence.bytes, + await readFile(files.capturePath), + ), + ).toBe(0); expect( immutableJsonSnapshotMember( inputs.value.evidence.value, diff --git a/src/artifacts/MachOSliceArtifactReader.test.ts b/src/artifacts/MachOSliceArtifactReader.test.ts index 85c6a2537f..be5d1216a0 100644 --- a/src/artifacts/MachOSliceArtifactReader.test.ts +++ b/src/artifacts/MachOSliceArtifactReader.test.ts @@ -371,7 +371,7 @@ describe("MachOSliceArtifactReader", () => { const verifiedRead = await reader.open(verifiedArmEntry); for await (const chunk of verifiedRead) verifiedChunks.push(Buffer.from(chunk)); - expect(Buffer.concat(verifiedChunks)).toEqual(arm); + expect(Buffer.compare(Buffer.concat(verifiedChunks), arm)).toBe(0); const truncatedController = new AbortController(); const truncated = await reader.open( diff --git a/src/artifacts/NodeSeaArtifactReader.test.ts b/src/artifacts/NodeSeaArtifactReader.test.ts index 56c7fceb5b..7c4690b824 100644 --- a/src/artifacts/NodeSeaArtifactReader.test.ts +++ b/src/artifacts/NodeSeaArtifactReader.test.ts @@ -408,8 +408,11 @@ it("extracts the main script and assets without executing them", async () => { await readFile(join(extracted.output_root, "assets/message.txt"), "utf8"), ).toBe("hello"); expect( - await readFile(join(extracted.output_root, "__node_sea__/code-cache")), - ).toEqual(Buffer.from("cache")); + Buffer.compare( + await readFile(join(extracted.output_root, "__node_sea__/code-cache")), + Buffer.from("cache"), + ), + ).toBe(0); const extractedInventory = await scanCanonicalArtifactInventory( extracted.output_root, ); diff --git a/src/artifacts/ZipArtifactReader.test.ts b/src/artifacts/ZipArtifactReader.test.ts index be60df9835..79da39efd1 100644 --- a/src/artifacts/ZipArtifactReader.test.ts +++ b/src/artifacts/ZipArtifactReader.test.ts @@ -103,7 +103,9 @@ describe("ZipArtifactReader positional reads", () => { const chunks: Buffer[] = []; for await (const chunk of await reader.open(entry)) chunks.push(Buffer.from(chunk)); - expect(Buffer.concat(chunks)).toEqual(Buffer.from(fixture.content)); + expect( + Buffer.compare(Buffer.concat(chunks), Buffer.from(fixture.content)), + ).toBe(0); expect(reads.mock.calls.length).toBeGreaterThan(3); } finally { reads.mockRestore(); diff --git a/src/artifacts/apple/KeyedArchiveReader.test.ts b/src/artifacts/apple/KeyedArchiveReader.test.ts index 39b4de420a..93bef7e930 100644 --- a/src/artifacts/apple/KeyedArchiveReader.test.ts +++ b/src/artifacts/apple/KeyedArchiveReader.test.ts @@ -578,7 +578,7 @@ describe("binary keyed archive dates", () => { dateScalar("2001-01-01T00:00:00.100Z"), ); expect(graph.limitations).not.toContain(unknownDateLimitation(1)); - expect(bytes).toEqual(pythonBinaryDateControl); + expect(Buffer.compare(bytes, pythonBinaryDateControl)).toBe(0); expect(createHash("sha256").update(bytes).digest("hex")).toBe( "39f08db8341bd74220352dfd559485515cce1e6035e08f27a59ab8f856374a30", ); @@ -593,7 +593,9 @@ describe("binary keyed archive dates", () => { dateScalar("2001-01-01T00:00:00.100Z"), ); expect(inspected.limitations).not.toContain(unknownDateLimitation(1)); - expect(await readFile(join(root, "archive.plist"))).toEqual(bytes); + expect( + Buffer.compare(await readFile(join(root, "archive.plist")), bytes), + ).toBe(0); }); it.each( @@ -612,7 +614,7 @@ describe("binary keyed archive dates", () => { `binary plist date marker 0x${marker.toString(16)} is invalid`, ), ); - expect(bytes).toEqual(original); + expect(Buffer.compare(bytes, original)).toBe(0); if (marker === 0x30) expect(createHash("sha256").update(bytes).digest("hex")).toBe( "ac5696b866c312feb53c2e82edd8fea79dbeb572eb1b07e4291603faa41e8c0e", @@ -631,7 +633,7 @@ describe("binary keyed archive dates", () => { reason: "format", message: `Cannot decode selected Foundation keyed archive: binary plist date marker 0x${marker.toString(16)} is invalid`, }); - expect(await readFile(path)).toEqual(bytes); + expect(Buffer.compare(await readFile(path), bytes)).toBe(0); }, ); @@ -649,7 +651,7 @@ describe("binary keyed archive dates", () => { expect(graph.limitations.join("\n")).not.toContain( "number metadata could not be completely observed", ); - expect(submillisecond).toEqual(original); + expect(Buffer.compare(submillisecond, original)).toBe(0); expect(createHash("sha256").update(submillisecond).digest("hex")).toBe( "ab69ba6e8034c6f60b828ee323a5a3e81bae3e7657477e0ebfbfdb93d53ed2ce", ); @@ -662,7 +664,12 @@ describe("binary keyed archive dates", () => { }); expect(inspected.objects[1]).toMatchObject(dateScalar(null)); expect(inspected.limitations).toContain(unknownDateLimitation(1)); - expect(await readFile(join(root, "archive.plist"))).toEqual(submillisecond); + expect( + Buffer.compare( + await readFile(join(root, "archive.plist")), + submillisecond, + ), + ).toBe(0); const mixed = Buffer.from( buildBinary({ @@ -697,7 +704,7 @@ describe("binary keyed archive dates", () => { { $plist_type: "date", iso8601: null }, ]); expect(mixedGraph.limitations).toContain(unknownDateLimitation(2)); - expect(mixed).toEqual(mixedOriginal); + expect(Buffer.compare(mixed, mixedOriginal)).toBe(0); }); }); diff --git a/src/artifacts/apple/PlistScalars.test.ts b/src/artifacts/apple/PlistScalars.test.ts index 1074d8c2d6..41d9a434da 100644 --- a/src/artifacts/apple/PlistScalars.test.ts +++ b/src/artifacts/apple/PlistScalars.test.ts @@ -321,9 +321,12 @@ describe("binary plist number rewriting", () => { uidOffsetsToNormalize: [], }), ).toBe(bytes); - expect(bytes.subarray(13, 30)).toEqual( - Buffer.from([0x14, ...Array(16).fill(0xff)]), - ); + expect( + Buffer.compare( + bytes.subarray(13, 30), + Buffer.from([0x14, ...Array(16).fill(0xff)]), + ), + ).toBe(0); }); it("does not rewrite a wide integer when a date aliases its payload", () => { @@ -362,7 +365,7 @@ describe("binary plist number rewriting", () => { }, { $plist_type: "date", iso8601: null }, ]); - expect(bytes).toEqual(original); + expect(Buffer.compare(bytes, original)).toBe(0); expect(bytes[11]).toBe(0x14); expect(bytes[18]).toBe(0x33); }); @@ -424,7 +427,7 @@ describe("binary plist shared object-table entries", () => { expect(numbers.limitations().join("\n")).not.toContain( "could not be completely observed", ); - expect(bytes).toEqual(original); + expect(Buffer.compare(bytes, original)).toBe(0); }); it("rewrites a shared 16-byte integer once for every entry", () => { @@ -450,7 +453,7 @@ describe("binary plist shared object-table entries", () => { { $plist_type: "integer", decimal: "18446744073709551618" }, { $plist_type: "integer", decimal: "1152921504606846979" }, ]); - expect(bytes).toEqual(original); + expect(Buffer.compare(bytes, original)).toBe(0); }); it("records a shared date once and keeps its sibling exact", () => { diff --git a/src/artifacts/pyinstaller/PyInstallerArtifactReader.test.ts b/src/artifacts/pyinstaller/PyInstallerArtifactReader.test.ts index c3d759f57a..3e79b0b211 100644 --- a/src/artifacts/pyinstaller/PyInstallerArtifactReader.test.ts +++ b/src/artifacts/pyinstaller/PyInstallerArtifactReader.test.ts @@ -373,14 +373,19 @@ it("extracts derived members into the safe output tree", async () => { path, snapshot, ); - expect(await readFile(join(output, "hello"))).toEqual(derived(script)); + expect( + Buffer.compare(await readFile(join(output, "hello")), derived(script)), + ).toBe(0); expect( extracted.artifacts.find((artifact) => artifact.relative_path === "hello") ?.sha256, ).toBe(sha256(derived(script))); - expect(await readFile(join(output, "PYZ.pyz", "app.pyc"))).toEqual( - derived(Buffer.from("app-body")), - ); + expect( + Buffer.compare( + await readFile(join(output, "PYZ.pyz", "app.pyc")), + derived(Buffer.from("app-body")), + ), + ).toBe(0); }); it("reports encrypted PYZ entries and refuses to extract them", async () => { @@ -509,7 +514,9 @@ it.each([false, true])( path, snapshot, ); - expect(await readFile(join(outputRoot, "PYZ.pyz"))).toEqual(payload); + expect( + Buffer.compare(await readFile(join(outputRoot, "PYZ.pyz")), payload), + ).toBe(0); }, ); @@ -808,7 +815,9 @@ it("projects CArchive type n entries as symlinks", async () => { await expect(readFile(join(output, "libfoo.so"))).rejects.toMatchObject({ code: "ENOENT", }); - expect(await readFile(join(output, "data.bin"))).toEqual(payload); + expect( + Buffer.compare(await readFile(join(output, "data.bin")), payload), + ).toBe(0); }); it.each([false, true])( @@ -948,10 +957,15 @@ it("inventories and extracts a Windows member name under its canonical path", as path, snapshot, ); - expect(await readFile(join(output, "pkg", "data.bin"))).toEqual(payload); - expect(await readFile(join(output, "PYZ.pyz", "res", "icon.bin"))).toEqual( - icon, - ); + expect( + Buffer.compare(await readFile(join(output, "pkg", "data.bin")), payload), + ).toBe(0); + expect( + Buffer.compare( + await readFile(join(output, "PYZ.pyz", "res", "icon.bin")), + icon, + ), + ).toBe(0); }); it("preserves distinct POSIX backslash and slash member paths", async () => { @@ -1008,8 +1022,15 @@ it("preserves distinct POSIX backslash and slash member paths", async () => { path, snapshot, ); - expect(await readFile(join(output, backslashName))).toEqual(backslashPayload); - expect(await readFile(join(output, "a", "b.txt"))).toEqual(slashPayload); + expect( + Buffer.compare( + await readFile(join(output, backslashName)), + backslashPayload, + ), + ).toBe(0); + expect( + Buffer.compare(await readFile(join(output, "a", "b.txt")), slashPayload), + ).toBe(0); }); it("reads a PE signature beyond the carrier prefix before projecting paths", async () => { @@ -1304,7 +1325,9 @@ it("uses known PYZ magic for separate CArchive scripts when the marshal table st path, snapshot, ); - expect(await readFile(join(output, "hello"))).toEqual(derived(script)); + expect( + Buffer.compare(await readFile(join(output, "hello")), derived(script)), + ).toBe(0); }); it("retains colliding CArchive rows and PYZ children but refuses extraction", async () => { diff --git a/src/artifacts/pyinstaller/PyInstallerBoundaries.test.ts b/src/artifacts/pyinstaller/PyInstallerBoundaries.test.ts index 808b1f8bfc..8c46ce1593 100644 --- a/src/artifacts/pyinstaller/PyInstallerBoundaries.test.ts +++ b/src/artifacts/pyinstaller/PyInstallerBoundaries.test.ts @@ -656,7 +656,9 @@ for (const cancel of [false, true]) { } else { expect(result.ok).toBe(true); if (!result.ok) throw result.cause; - expect(result.value.subarray(16)).toEqual(Buffer.from("body")); + expect( + Buffer.compare(result.value.subarray(16), Buffer.from("body")), + ).toBe(0); } await reader.close(); expect(attempts).toBe(failClose ? 2 : 1); diff --git a/src/artifacts/pyinstaller/PyInstallerFormatBoundaries.test.ts b/src/artifacts/pyinstaller/PyInstallerFormatBoundaries.test.ts index 1e70f61986..898a0995fc 100644 --- a/src/artifacts/pyinstaller/PyInstallerFormatBoundaries.test.ts +++ b/src/artifacts/pyinstaller/PyInstallerFormatBoundaries.test.ts @@ -572,7 +572,9 @@ for (const typeCode of ["o", "d"] as const) { path, inventory, ); - expect(await readFile(join(output, realPath))).toEqual(payload); + expect( + Buffer.compare(await readFile(join(output, realPath)), payload), + ).toBe(0); }, ); }); @@ -795,8 +797,11 @@ it.each(["x", "s", "empty-module"])( inventory, ); expect( - (await readFile(join(output, "PYZ.pyz/app.pyc"))).subarray(16), - ).toEqual(Buffer.from("readable module")); + Buffer.compare( + (await readFile(join(output, "PYZ.pyz/app.pyc"))).subarray(16), + Buffer.from("readable module"), + ), + ).toBe(0); }, ); }, @@ -1380,7 +1385,9 @@ it.each([true, false])( source, inventory, ); - expect(await readFile(join(output, path))).toEqual(file.payload); + expect( + Buffer.compare(await readFile(join(output, path)), file.payload), + ).toBe(0); }, ); }, diff --git a/src/artifacts/pyinstaller/PyzArchive.test.ts b/src/artifacts/pyinstaller/PyzArchive.test.ts index 6ea1fedf16..e553589d5b 100644 --- a/src/artifacts/pyinstaller/PyzArchive.test.ts +++ b/src/artifacts/pyinstaller/PyzArchive.test.ts @@ -29,7 +29,7 @@ it("lists modules from a supported PYZ and leaves an unknown magic opaque", asyn ); expect(opaque.ok).toBe(true); if (!opaque.ok || opaque.value.kind !== "opaque") return; - expect(opaque.value.pythonMagic).toEqual(Buffer.alloc(4)); + expect(Buffer.compare(opaque.value.pythonMagic, Buffer.alloc(4))).toBe(0); expect(opaque.value.limitation).toContain("left opaque"); }); diff --git a/src/browser/BrowserScenario.test.ts b/src/browser/BrowserScenario.test.ts index a30c82f85c..16a7b00293 100644 --- a/src/browser/BrowserScenario.test.ts +++ b/src/browser/BrowserScenario.test.ts @@ -102,20 +102,21 @@ describe("browser scenario secret redaction", () => { "raw A/B? remains case-sensitive", ); expect( - secrets.redactBytes( + Buffer.compare( + secrets.redactBytes( + Buffer.concat([ + Buffer.from([0xff, 0]), + Buffer.from("a%2fb%3F"), + Buffer.from([0x80]), + ]), + ), Buffer.concat([ Buffer.from([0xff, 0]), - Buffer.from("a%2fb%3F"), + Buffer.from("[REDACTED:session]"), Buffer.from([0x80]), ]), ), - ).toEqual( - Buffer.concat([ - Buffer.from([0xff, 0]), - Buffer.from("[REDACTED:session]"), - Buffer.from([0x80]), - ]), - ); + ).toBe(0); expect( secrets.sanitizeUrl("https://app.example.test/?token=a%2fb%3F").url, ).toBe("https://app.example.test/?token=[REDACTED:session]"); diff --git a/src/browser/history/protobuf/ProtobufBodyTrailer.test.ts b/src/browser/history/protobuf/ProtobufBodyTrailer.test.ts index ec58d042a9..7989c1a673 100644 --- a/src/browser/history/protobuf/ProtobufBodyTrailer.test.ts +++ b/src/browser/history/protobuf/ProtobufBodyTrailer.test.ts @@ -51,7 +51,9 @@ it("rejects forbidden trailer controls while retaining valid earlier trailers", expect(result.frames[0]?.trailers).toEqual([ { name: "grpc-message", value: "kept" }, ]); - expect(input.subarray(5)).toEqual(Buffer.from(text, "utf8")); + expect(Buffer.compare(input.subarray(5), Buffer.from(text, "utf8"))).toBe( + 0, + ); } }); @@ -116,7 +118,7 @@ it.each(["grpc-web", "grpc-web-text"] as const)( ], }); expect(result.examined_bytes).toBe(binary.length); - expect(binary.subarray(-trailer.length)).toEqual(trailer); + expect(Buffer.compare(binary.subarray(-trailer.length), trailer)).toBe(0); }, ); diff --git a/src/browser/network/PlaywrightScenarioNetwork.test.ts b/src/browser/network/PlaywrightScenarioNetwork.test.ts index ec7c18186c..d74a9a14e5 100644 --- a/src/browser/network/PlaywrightScenarioNetwork.test.ts +++ b/src/browser/network/PlaywrightScenarioNetwork.test.ts @@ -401,13 +401,16 @@ it("preserves duplicate headers and binary bytes, redacting credentials and decl ); if (content?.kind !== "network-content" || content.body.state !== "captured") throw new Error("Missing response"); - expect(Buffer.from(content.body.content, "base64")).toEqual( - Buffer.concat([ - Buffer.from([0xff, 0x00]), - Buffer.from("[REDACTED:declared]"), - Buffer.from([0xfe]), - ]), - ); + expect( + Buffer.compare( + Buffer.from(content.body.content, "base64"), + Buffer.concat([ + Buffer.from([0xff, 0x00]), + Buffer.from("[REDACTED:declared]"), + Buffer.from([0xfe]), + ]), + ), + ).toBe(0); expect(content.body.redacted).toBe(true); expect(content.headers).toEqual({ state: "captured", diff --git a/src/domain/apple/plistValue.test.ts b/src/domain/apple/plistValue.test.ts index a06899531e..01e9ce026b 100644 --- a/src/domain/apple/plistValue.test.ts +++ b/src/domain/apple/plistValue.test.ts @@ -236,5 +236,5 @@ it.each([ expect(projected.nonFiniteRealCount).toBe(4); expect(projected.negativeZeroRealCount).toBe(1); expect(observed).toHaveLength(5); - expect(bytes).toEqual(original); + expect(Buffer.compare(bytes, original)).toBe(0); }); diff --git a/src/domain/native/nativeAotPe.discoveryAccounting.test.ts b/src/domain/native/nativeAotPe.discoveryAccounting.test.ts index 6985e152af..9a40ff0ce3 100644 --- a/src/domain/native/nativeAotPe.discoveryAccounting.test.ts +++ b/src/domain/native/nativeAotPe.discoveryAccounting.test.ts @@ -57,7 +57,7 @@ describe("NativeAOT candidate work accounting", () => { }, }); expect(summary.coverage.work_units_examined).toBeLessThan(64 * 1024); - expect(bytes).toEqual(before); + expect(Buffer.compare(bytes, before)).toBe(0); }); it.each(["slots", "interfaces"] as const)( @@ -85,7 +85,7 @@ describe("NativeAOT candidate work accounting", () => { scan_bytes_omitted: 0, }, }); - expect(bytes).toEqual(before); + expect(Buffer.compare(bytes, before)).toBe(0); }, ); @@ -112,6 +112,6 @@ describe("NativeAOT candidate work accounting", () => { "0x1400022c0", "0x140002400", ]); - expect(bytes).toEqual(before); + expect(Buffer.compare(bytes, before)).toBe(0); }); }); diff --git a/src/domain/native/nativeAotPe.sectionRanges.test.ts b/src/domain/native/nativeAotPe.sectionRanges.test.ts index 6efaf1caf6..75d482ccd0 100644 --- a/src/domain/native/nativeAotPe.sectionRanges.test.ts +++ b/src/domain/native/nativeAotPe.sectionRanges.test.ts @@ -27,7 +27,7 @@ it.each(["before-data", "after-data"] as const)( expect( result.readTypeDetail("0x140002280", NATIVE_AOT_MAX_REPORT_BYTES), ).toBeUndefined(); - expect(bytes).toEqual(before); + expect(Buffer.compare(bytes, before)).toBe(0); }, ); @@ -110,7 +110,7 @@ it("preserves source section order while admitting a unique unsorted mapping", ( status: "complete", method_tables: 3, }); - expect(bytes).toEqual(before); + expect(Buffer.compare(bytes, before)).toBe(0); }); it("does not confuse raw alignment padding with virtual section ownership", () => { diff --git a/src/domain/native/nativeAotPe.test.ts b/src/domain/native/nativeAotPe.test.ts index 282dc1498e..ebcdccf20c 100644 --- a/src/domain/native/nativeAotPe.test.ts +++ b/src/domain/native/nativeAotPe.test.ts @@ -64,7 +64,7 @@ describe("NativeAOT indexed MethodTable closure", () => { "0x1400022c0", ...expected, ]); - expect(bytes).toEqual(before); + expect(Buffer.compare(bytes, before)).toBe(0); }, ); @@ -91,7 +91,7 @@ describe("read-only NativeAOT PE parser", () => { SUMMARY_BUDGET, "0x140000000", ); - expect(bytes).toEqual(before); + expect(Buffer.compare(bytes, before)).toBe(0); expect(parsed.summary).toMatchObject({ format: "dotnet-nativeaot", status: "complete", @@ -227,7 +227,7 @@ describe("read-only NativeAOT PE parser", () => { nativeAotPeDigest(bytes), SUMMARY_BUDGET, ); - expect(bytes).toEqual(before); + expect(Buffer.compare(bytes, before)).toBe(0); expect(parsed.summary).toMatchObject({ status: "partial", derived_overlay: null, diff --git a/src/dotnet/ManagedMetadataHeaps.test.ts b/src/dotnet/ManagedMetadataHeaps.test.ts index 9bb3321210..469f547b34 100644 --- a/src/dotnet/ManagedMetadataHeaps.test.ts +++ b/src/dotnet/ManagedMetadataHeaps.test.ts @@ -47,9 +47,12 @@ describe("managed metadata heaps", () => { blob: null, } as unknown as ManagedMetadataLayout; - expect(readMetadataBlob(Buffer.alloc(0), layout, 0, 0)).toEqual( - Buffer.alloc(0), - ); + expect( + Buffer.compare( + readMetadataBlob(Buffer.alloc(0), layout, 0, 0), + Buffer.alloc(0), + ), + ).toBe(0); expect(() => readMetadataBlob(Buffer.alloc(0), layout, 1, 0)).toThrowError( expect.objectContaining({ issue: expect.objectContaining({ diff --git a/src/dotnet/ManagedPeFixtureLayout.test.ts b/src/dotnet/ManagedPeFixtureLayout.test.ts index 99d30a77c6..ecf2fa32fc 100644 --- a/src/dotnet/ManagedPeFixtureLayout.test.ts +++ b/src/dotnet/ManagedPeFixtureLayout.test.ts @@ -41,11 +41,20 @@ describe("managed PE fixture layout", () => { }); expect( - image.subarray(rawOffset(0x2800), rawOffset(0x2800) + firstBody.length), - ).toEqual(firstBody); + Buffer.compare( + image.subarray(rawOffset(0x2800), rawOffset(0x2800) + firstBody.length), + firstBody, + ), + ).toBe(0); expect( - image.subarray(rawOffset(0x2840), rawOffset(0x2840) + secondBody.length), - ).toEqual(secondBody); + Buffer.compare( + image.subarray( + rawOffset(0x2840), + rawOffset(0x2840) + secondBody.length, + ), + secondBody, + ), + ).toBe(0); const inspected = inspectManagedMembersBytes( image, managedPeFixtureTarget(image), @@ -92,11 +101,14 @@ describe("managed PE fixture layout", () => { expect(layout.cli.resources.size).toBe(resourceData.length + 4); expect(image.readUInt32LE(directoryOffset)).toBe(resourceData.length); expect( - image.subarray( - directoryOffset + 4, - directoryOffset + 4 + resourceData.length, + Buffer.compare( + image.subarray( + directoryOffset + 4, + directoryOffset + 4 + resourceData.length, + ), + resourceData, ), - ).toEqual(resourceData); + ).toBe(0); expect(inspection.classification.status).toBe("managed"); expect(inspection.resources).toMatchObject([ { diff --git a/src/electron/ElectronFuseArchitecture.test.ts b/src/electron/ElectronFuseArchitecture.test.ts index aeafce7c36..b9df79e185 100644 --- a/src/electron/ElectronFuseArchitecture.test.ts +++ b/src/electron/ElectronFuseArchitecture.test.ts @@ -114,5 +114,5 @@ const assertArchitecture = async ( expect( report.slices[0]?.sentinels[0]?.fuses.map((fuse) => fuse.raw_byte), ).toEqual(OBSIDIAN_FUSE_BYTES); - expect(await readFile(path)).toEqual(image.bytes); + expect(Buffer.compare(await readFile(path), image.bytes)).toBe(0); }; diff --git a/src/flutter/DartElf.test.ts b/src/flutter/DartElf.test.ts index 573121171a..c726225078 100644 --- a/src/flutter/DartElf.test.ts +++ b/src/flutter/DartElf.test.ts @@ -112,7 +112,7 @@ describe("readElfSymbol", () => { const image = strippedImageWithSections([[tag, 0xffffffffffffffffn]]); const original = Buffer.from(image); expect(await readElfSymbols(image, names)).toEqual(control); - expect(image).toEqual(original); + expect(Buffer.compare(image, original)).toBe(0); }, ); @@ -202,7 +202,7 @@ describe("dynamic symbols without a SHT_DYNSYM section", () => { expect(control.symbols).toHaveLength(2); const before = Buffer.from(image); expect(await readElfSymbols(image, names)).toEqual(control); - expect(image).toEqual(before); + expect(Buffer.compare(image, before)).toBe(0); }, ); diff --git a/src/ghidra/GhidraLaunchFailure.test.ts b/src/ghidra/GhidraLaunchFailure.test.ts index 425f817f03..7f6d226de1 100644 --- a/src/ghidra/GhidraLaunchFailure.test.ts +++ b/src/ghidra/GhidraLaunchFailure.test.ts @@ -136,10 +136,11 @@ describe("Ghidra launch failure diagnostics", () => { expect(outcome?.["stderrTruncated"]).toBe(true); expect(outcome?.["outputTruncated"]).toBe(false); expect(outcome?.["stderrBytes"]).toBe(oversized.length); - expect((outcome?.["stderr"] as string).length).toBeLessThanOrEqual( - EXPECTED_OUTPUT_BOUND_BYTES, - ); - expect(outcome?.["stderr"] as string).not.toContain("UNRETAINED-TAIL"); + const stderr = outcome?.["stderr"]; + if (typeof stderr !== "string") + throw new Error("Expected launcher stderr"); + expect(stderr.length).toBeLessThanOrEqual(EXPECTED_OUTPUT_BOUND_BYTES); + expect(stderr).not.toContain("UNRETAINED-TAIL"); } finally { await rm(javaHome, { recursive: true, force: true }); await rm(runtimeRoot, { recursive: true, force: true }); diff --git a/src/ghidra/extensions/GhidraExtensions.test.ts b/src/ghidra/extensions/GhidraExtensions.test.ts index 61edea4beb..2df197d977 100644 --- a/src/ghidra/extensions/GhidraExtensions.test.ts +++ b/src/ghidra/extensions/GhidraExtensions.test.ts @@ -161,9 +161,12 @@ describe("optional Ghidra extension boundary", () => { resources, ); expect(snapshots[0]?.path).toBe(join(runtime, "extensions/nativeaot.jar")); - expect(await readFile(join(runtime, "extensions/nativeaot.jar"))).toEqual( - bytes, - ); + expect( + Buffer.compare( + await readFile(join(runtime, "extensions/nativeaot.jar")), + bytes, + ), + ).toBe(0); await expect( snapshotGhidraExtensions([extension], runtime, resources), ).rejects.toMatchObject({ diff --git a/src/gpu/GpuPayloadCodec.test.ts b/src/gpu/GpuPayloadCodec.test.ts index 465fc725c4..6b1f11f9aa 100644 --- a/src/gpu/GpuPayloadCodec.test.ts +++ b/src/gpu/GpuPayloadCodec.test.ts @@ -7,9 +7,12 @@ it.each(["zlib", "zstd"] as const)( const source = Buffer.from("kernel"), encode = codec === "zstd" ? zstdCompressSync : deflateSync, compressed = encode(source); - expect(await decodeGpuPayload(compressed, codec, source.length)).toEqual( - source, - ); + expect( + Buffer.compare( + await decodeGpuPayload(compressed, codec, source.length), + source, + ), + ).toBe(0); await expect( decodeGpuPayload( Buffer.concat([compressed, Buffer.from("junk")]), diff --git a/src/hopper/HopperImageCopy.test.ts b/src/hopper/HopperImageCopy.test.ts index a0e57bfc35..1a3cbc01ea 100644 --- a/src/hopper/HopperImageCopy.test.ts +++ b/src/hopper/HopperImageCopy.test.ts @@ -37,8 +37,8 @@ it("whole executable copying rejects pre-copy replacement and retains captured b ); expect(captured).toEqual({ ok: true, value: capturedPath }); await writeFile(source, replacement); - expect(await readFile(capturedPath)).toEqual(selected); - expect(await readFile(source)).toEqual(replacement); + expect(Buffer.compare(await readFile(capturedPath), selected)).toBe(0); + expect(Buffer.compare(await readFile(source), replacement)).toBe(0); } finally { await resources.close(); } diff --git a/src/hopper/HopperMachOImage.test.ts b/src/hopper/HopperMachOImage.test.ts index 3a90caa6c9..fcac956979 100644 --- a/src/hopper/HopperMachOImage.test.ts +++ b/src/hopper/HopperMachOImage.test.ts @@ -385,7 +385,7 @@ it("copies exactly the selected FAT32 slice bytes and verifies the source", asyn arm64eOffset, arm64eOffset + FAT32_SLICE_SIZE, ); - expect(await readFile(destination)).toEqual(copied); + expect(Buffer.compare(await readFile(destination), copied)).toBe(0); }); it("retains the caller reason when a FAT copy is cancelled after its final write", async () => { @@ -421,12 +421,15 @@ it("retains the caller reason when a FAT copy is cancelled after its final write expect(prepared.error._tag).toBe("HopperCancelledError"); expect(prepared.error.cause).toBe(reason); // The private session directory remains the owner of partial output. - await expect(readFile(destination)).resolves.toEqual( - fixture.container.subarray( - fixture.sliceOffsets[0], - fixture.sliceOffsets[0]! + FAT32_SLICE_SIZE, + expect( + Buffer.compare( + await readFile(destination), + fixture.container.subarray( + fixture.sliceOffsets[0], + fixture.sliceOffsets[0]! + FAT32_SLICE_SIZE, + ), ), - ); + ).toBe(0); }); it("retains a failed prepared-image close through launcher cleanup and blocks another launch", async () => { @@ -522,9 +525,12 @@ it("retains a failed prepared-image close through launcher cleanup and blocks an expect(initial.error.cleanupIncomplete).toBe(false); expect(blocked.error.cleanupIncomplete).toBe(false); expect(cleanupPending.error.cleanupIncomplete).toBe(false); - await expect( - readFile(join(sessionDirectory, "image.macho")), - ).resolves.toEqual(bytes); + expect( + Buffer.compare( + await readFile(join(sessionDirectory, "image.macho")), + bytes, + ), + ).toBe(0); }); it("observes caller cancellation that arrives during final source-handle cleanup", async () => { @@ -574,9 +580,9 @@ it("observes caller cancellation that arrives during final source-handle cleanup if (prepared.ok) throw new Error("Finalization cancellation was ignored"); expect(prepared.error._tag).toBe("HopperCancelledError"); expect(prepared.error.cause).toBe(reason); - await expect(readFile(join(directory, "image.macho"))).resolves.toEqual( - bytes, - ); + expect( + Buffer.compare(await readFile(join(directory, "image.macho")), bytes), + ).toBe(0); }); it("rejects FAT32 preparation when the source table changed after resolution", async () => { @@ -646,7 +652,7 @@ it("copies an admitted thin Mach-O through the owned verified-image path", async ); if (!prepared.ok) throw new Error(prepared.error.userMessage); expect(prepared.value).toBe(destination); - expect(await readFile(destination)).toEqual(bytes); + expect(Buffer.compare(await readFile(destination), bytes)).toBe(0); expect(target.path).toBe(path); }); @@ -777,6 +783,6 @@ it("retains an owned partial thin image and exact cancellation reason after a wr if (prepared.ok) throw new Error("Cancelled partial copy was accepted"); expect(prepared.error._tag).toBe("HopperCancelledError"); expect(prepared.error.cause).toBe(reason); - await expect(readFile(destination)).resolves.toEqual(partial); + expect(Buffer.compare(await readFile(destination), partial)).toBe(0); await expect(access(destination)).resolves.toBeUndefined(); }); diff --git a/src/ida/IdaSessionClient.test.ts b/src/ida/IdaSessionClient.test.ts index a7d24f9ac6..59908c79ff 100644 --- a/src/ida/IdaSessionClient.test.ts +++ b/src/ida/IdaSessionClient.test.ts @@ -1147,7 +1147,9 @@ describe("headless IDA session ownership and cleanup", () => { }); if (!result.ok) throw result.error; expect(producer.inputPath).not.toBe(target.path); - expect(await readFile(producer.inputPath)).toEqual(original); + expect(Buffer.compare(await readFile(producer.inputPath), original)).toBe( + 0, + ); expect( functionDossierSchema .parse(result.value.result) @@ -1167,7 +1169,7 @@ describe("headless IDA session ownership and cleanup", () => { producer.calls.find(({ name }) => name === "idb_close")?.args.save, ).toBe(false); expect(await readdir(root)).toEqual(["target.elf"]); - expect(await readFile(target.path)).toEqual(original); + expect(Buffer.compare(await readFile(target.path), original)).toBe(0); }); it("retains the private workspace and reports cleanup failure when worker release is unverified", async () => { const { producer, client, root } = await fixture("headless"); diff --git a/src/native/parsers/plist.test.ts b/src/native/parsers/plist.test.ts index 1e6ef21077..69f24d1d7d 100644 --- a/src/native/parsers/plist.test.ts +++ b/src/native/parsers/plist.test.ts @@ -106,7 +106,7 @@ const parseWithoutMutatingSource = (bytes: Buffer) => { const parsed = parsePlistBinary(bytes); expect(parsed.ok).toBe(true); if (!parsed.ok) throw parsed.error; - expect(bytes).toEqual(original); + expect(Buffer.compare(bytes, original)).toBe(0); return parsed.value; }; diff --git a/src/native/pwndbg/CoreFileMappingAuthority.test.ts b/src/native/pwndbg/CoreFileMappingAuthority.test.ts index 25dc0bae61..4f7e12c198 100644 --- a/src/native/pwndbg/CoreFileMappingAuthority.test.ts +++ b/src/native/pwndbg/CoreFileMappingAuthority.test.ts @@ -43,7 +43,7 @@ describe("core-only NT_FILE authority", () => { expect(result.ok).toBe( ["/usr/bin/app", "//usr/lib/libc.so", "/a/.../b"].includes(path), ); - expect(bytes).toEqual(original); + expect(Buffer.compare(bytes, original)).toBe(0); if (!result.ok) expect(result.error).toMatchObject({ _tag: "AnalysisCapabilityUnavailableError", diff --git a/src/process/ExecFileOutput.test.ts b/src/process/ExecFileOutput.test.ts index 21f2f826ba..4aacc960c4 100644 --- a/src/process/ExecFileOutput.test.ts +++ b/src/process/ExecFileOutput.test.ts @@ -51,8 +51,10 @@ describe("execFileOutput", () => { "process.stdout.write(Buffer.from([0x00, 0xff, 0x41])); process.stderr.write(Buffer.from([0xfe]));", ]); - expect(result.stdout).toEqual(Buffer.from([0x00, 0xff, 0x41])); - expect(result.stderr).toEqual(Buffer.from([0xfe])); + expect(Buffer.compare(result.stdout, Buffer.from([0x00, 0xff, 0x41]))).toBe( + 0, + ); + expect(Buffer.compare(result.stderr, Buffer.from([0xfe]))).toBe(0); }); it("reports raw byte counts when UTF-8 decoding changes the captured text", async () => { diff --git a/src/reference/ReferenceSourceReaderFile.test.ts b/src/reference/ReferenceSourceReaderFile.test.ts index 9f4a20dcd1..205e2f8904 100644 --- a/src/reference/ReferenceSourceReaderFile.test.ts +++ b/src/reference/ReferenceSourceReaderFile.test.ts @@ -26,7 +26,9 @@ it("retains all bytes across short reads through the declared size", async () => expectedSize: BigInt(bytes.length), }); if (result.status === "failed") throw result.entry; - expect(Buffer.concat(result.chunks, result.total)).toEqual(bytes); + expect( + Buffer.compare(Buffer.concat(result.chunks, result.total), bytes), + ).toBe(0); }); it("reports growth instead of continuing to consume a changed file", async () => { diff --git a/src/wasm/WabtArtifactProvider.test.ts b/src/wasm/WabtArtifactProvider.test.ts index 5cbb8cf284..bd70e0894c 100644 --- a/src/wasm/WabtArtifactProvider.test.ts +++ b/src/wasm/WabtArtifactProvider.test.ts @@ -327,7 +327,9 @@ it.each([ expect(report.tool_profile.commands[0]?.arguments).toContain( "--ignore-custom-section-errors", ); - expect(await readFile(fixture.path)).toEqual(fixture.bytes); + expect(Buffer.compare(await readFile(fixture.path), fixture.bytes)).toBe( + 0, + ); expect(report.headers).toBe( dump.slice(0, dump.indexOf("Section Details:\n")), ); @@ -722,7 +724,12 @@ const launchWabtOutcome = async ( const { bytes, dump, controller } = fixture; const version = input.arguments[0] === "--version"; if (!version) { - expect(await readFile(join(input.cwd ?? "", "module.wasm"))).toEqual(bytes); + expect( + Buffer.compare( + await readFile(join(input.cwd ?? "", "module.wasm")), + bytes, + ), + ).toBe(0); if (mode === "changed-snapshot") { const snapshot = join(input.cwd ?? "", "module.wasm"); await chmod(snapshot, 0o600); @@ -821,7 +828,7 @@ it("enforces a selected command deadline and completes a corrected retry after c const report = wasmArtifactSchema.parse(corrected.value.result); expect(report.tool_profile.timeout_ms).toBe(2_000); expect(report.limitations.join(" ")).not.toContain("30-second"); - await expect(readFile(fixture.path)).resolves.toEqual(fixture.bytes); + expect(Buffer.compare(await readFile(fixture.path), fixture.bytes)).toBe(0); }); const glueSourcesForOutcome = (mode: OutcomeMode) => { @@ -1006,7 +1013,7 @@ const runOwnedArtifactOutcome = async ( ); await assertSnapshotCleanupOutcome(mode, result, provider, runtimePath); await expect(access(runtimePath)).rejects.toMatchObject({ code: "ENOENT" }); - expect(await readFile(path)).toEqual(bytes); + expect(Buffer.compare(await readFile(path), bytes)).toBe(0); if (acquiredPid !== undefined) { const pid = acquiredPid; expect(() => process.kill(pid, 0)).toThrow(); diff --git a/src/wasm/WabtString.test.ts b/src/wasm/WabtString.test.ts index bdbaf40afe..a0c2d7cea2 100644 --- a/src/wasm/WabtString.test.ts +++ b/src/wasm/WabtString.test.ts @@ -17,7 +17,9 @@ it("frames escaped quotes and trailing backslashes without consuming the next fo const encoded = JSON.stringify(value); const source = encoded + ' (export "next" (func 0))'; expect(watStringEnd(source, 1)).toBe(encoded.length); - expect(watString(source, 1).bytes).toEqual(Buffer.from(value)); + expect( + Buffer.compare(watString(source, 1).bytes, Buffer.from(value)), + ).toBe(0); } } }); diff --git a/tests/acceptance/apkZip64FramingCli.test.ts b/tests/acceptance/apkZip64FramingCli.test.ts index ea02f724a7..e75291f992 100644 --- a/tests/acceptance/apkZip64FramingCli.test.ts +++ b/tests/acceptance/apkZip64FramingCli.test.ts @@ -58,7 +58,7 @@ const expectUnsignedZip64 = async ( runtime_execution: "not-performed", }); } - expect(await readFile(path)).toEqual(Buffer.from(bytes)); + expect(Buffer.compare(await readFile(path), Buffer.from(bytes))).toBe(0); }; const expectRefusal = async ( @@ -75,7 +75,7 @@ const expectRefusal = async ( const mcpError = parseMcpToolError(response).error; expect(mcpError).toMatchObject(expected); expect(mcpError.message).toBe(cliError.message); - expect(await readFile(path)).toEqual(Buffer.from(bytes)); + expect(Buffer.compare(await readFile(path), Buffer.from(bytes))).toBe(0); }; cliTest( diff --git a/tests/acceptance/applications/appleBundleTraversalCoverage.test.ts b/tests/acceptance/applications/appleBundleTraversalCoverage.test.ts index d368d5c479..e2a0ce2cb8 100644 --- a/tests/acceptance/applications/appleBundleTraversalCoverage.test.ts +++ b/tests/acceptance/applications/appleBundleTraversalCoverage.test.ts @@ -84,7 +84,7 @@ cliTest.skipIf(process.platform === "win32")( "Contents/Resources/Panel.nib: Symlink target was not followed or disclosed.", ); } - expect(await readFile(link)).toEqual(fixture.nib); + expect(Buffer.compare(await readFile(link), fixture.nib)).toBe(0); }, ); diff --git a/tests/acceptance/applications/binaryPlistDates.test.ts b/tests/acceptance/applications/binaryPlistDates.test.ts index bdc78b8f4d..80f756eeca 100644 --- a/tests/acceptance/applications/binaryPlistDates.test.ts +++ b/tests/acceptance/applications/binaryPlistDates.test.ts @@ -204,12 +204,19 @@ cliTest( ), ).toEqual(ibResult); - expect(await readFile(archivePath)).toEqual(bytes); - expect(await readFile(malformedArchivePath)).toEqual(malformedBytes); + expect(Buffer.compare(await readFile(archivePath), bytes)).toBe(0); expect( - await readFile(join(malformedApp, "Contents/Resources/Main.nib")), - ).toEqual(bytes); - expect(await readFile(malformedNib)).toEqual(malformedBytes); + Buffer.compare(await readFile(malformedArchivePath), malformedBytes), + ).toBe(0); + expect( + Buffer.compare( + await readFile(join(malformedApp, "Contents/Resources/Main.nib")), + bytes, + ), + ).toBe(0); + expect(Buffer.compare(await readFile(malformedNib), malformedBytes)).toBe( + 0, + ); expect(createHash("sha256").update(bytes).digest("hex")).toBe( ibResult.documents[0]?.archive_sha256, ); @@ -265,7 +272,7 @@ cliTest( parseEvidence(validated).normalized_result, ); expect(mcpResult).toEqual(cliResult); - expect(await readFile(archivePath)).toEqual(bytes); + expect(Buffer.compare(await readFile(archivePath), bytes)).toBe(0); }, 60_000, ); diff --git a/tests/acceptance/applications/hopperPublisherRejectionCli.test.ts b/tests/acceptance/applications/hopperPublisherRejectionCli.test.ts index 9e22759839..1da519cac5 100644 --- a/tests/acceptance/applications/hopperPublisherRejectionCli.test.ts +++ b/tests/acceptance/applications/hopperPublisherRejectionCli.test.ts @@ -61,6 +61,6 @@ cliTest }, }); await expect(access(marker)).rejects.toMatchObject({ code: "ENOENT" }); - expect(await readFile(target)).toEqual(original); + expect(Buffer.compare(await readFile(target), original)).toBe(0); }, ); diff --git a/tests/acceptance/applications/interfaceBuilderUIKitKeyedArchive.test.ts b/tests/acceptance/applications/interfaceBuilderUIKitKeyedArchive.test.ts index 5f86aa9ea9..0b137347b8 100644 --- a/tests/acceptance/applications/interfaceBuilderUIKitKeyedArchive.test.ts +++ b/tests/acceptance/applications/interfaceBuilderUIKitKeyedArchive.test.ts @@ -243,7 +243,7 @@ cliTest.for(["xml", "binary"] as const)( }), ); expect(report.graph.truncated).toBe(false); - expect(await readFile(path)).toEqual(bytes); + expect(Buffer.compare(await readFile(path), bytes)).toBe(0); }, ); diff --git a/tests/acceptance/applications/javascriptSourceSelectionCli.test.ts b/tests/acceptance/applications/javascriptSourceSelectionCli.test.ts index 9fc6f49f6f..1a8eb73ba1 100644 --- a/tests/acceptance/applications/javascriptSourceSelectionCli.test.ts +++ b/tests/acceptance/applications/javascriptSourceSelectionCli.test.ts @@ -92,7 +92,9 @@ cliTest.for([ expect(properties?.parse_status).toBe( path === "main.mjs" ? "complete" : null, ); - expect(await readFile(join(directory, path))).toEqual(bytes); + expect(Buffer.compare(await readFile(join(directory, path)), bytes)).toBe( + 0, + ); } const fullResponse = await cli.run({ arguments: [command, input, "--format", "json"], diff --git a/tests/acceptance/applications/jvmAdmissionIdentity.test.ts b/tests/acceptance/applications/jvmAdmissionIdentity.test.ts index e13ec98933..4aaf6148b3 100644 --- a/tests/acceptance/applications/jvmAdmissionIdentity.test.ts +++ b/tests/acceptance/applications/jvmAdmissionIdentity.test.ts @@ -28,12 +28,12 @@ cliTest( }); expect(analyzed.exitCode, analyzed.stderr).toBe(1); expect(analyzed.json).toMatchObject(refusal); - expect(await readFile(path)).toEqual(bytes); + expect(Buffer.compare(await readFile(path), bytes)).toBe(0); const opened = await call("open_binary", { path }); expect(opened.isError, JSON.stringify(opened)).toBe(true); expect(parseMcpToolError(opened).error).toMatchObject(refusal); - expect(await readFile(path)).toEqual(bytes); + expect(Buffer.compare(await readFile(path), bytes)).toBe(0); } }, ); diff --git a/tests/acceptance/applications/keyedArchiveScalarKinds.test.ts b/tests/acceptance/applications/keyedArchiveScalarKinds.test.ts index 7d9322d523..8e25eb45b9 100644 --- a/tests/acceptance/applications/keyedArchiveScalarKinds.test.ts +++ b/tests/acceptance/applications/keyedArchiveScalarKinds.test.ts @@ -154,7 +154,7 @@ cliTest.for(["xml", "binary"] as const)( status: "resolved", }), ]); - expect(await readFile(path)).toEqual(bytes); + expect(Buffer.compare(await readFile(path), bytes)).toBe(0); }, ); @@ -243,7 +243,7 @@ cliTest.for(["xml", "binary"] as const)( 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(await readFile(path)).toEqual(bytes); + expect(Buffer.compare(await readFile(path), bytes)).toBe(0); }, ); @@ -346,7 +346,7 @@ cliTest("preserves binary IEEE non-finite scalar identity", async ({ cli }) => { parseEvidence(mcp.structuredContent).normalized_result, ), ).toEqual(result); - expect(await readFile(path)).toEqual(bytes); + expect(Buffer.compare(await readFile(path), bytes)).toBe(0); }); cliTest( @@ -419,6 +419,6 @@ cliTest( expect(result.limitations).not.toContainEqual( expect.stringContaining('"value": null'), ); - expect(await readFile(path)).toEqual(bytes); + expect(Buffer.compare(await readFile(path), bytes)).toBe(0); }, ); diff --git a/tests/acceptance/applications/keyedArchiveXmlNumbers.test.ts b/tests/acceptance/applications/keyedArchiveXmlNumbers.test.ts index 418f86845c..0c10ff8019 100644 --- a/tests/acceptance/applications/keyedArchiveXmlNumbers.test.ts +++ b/tests/acceptance/applications/keyedArchiveXmlNumbers.test.ts @@ -130,7 +130,7 @@ cliTest.for([ /non-finite|completely observed/u.test(note), ), ).toBe(false); - expect(await readFile(path)).toEqual(bytes); + expect(Buffer.compare(await readFile(path), bytes)).toBe(0); }, ); @@ -178,7 +178,7 @@ cliTest( }, }, }); - expect(await readFile(path)).toEqual(bytes); + expect(Buffer.compare(await readFile(path), bytes)).toBe(0); } }, ); diff --git a/tests/acceptance/applications/keyedArchiveXmlScalars.test.ts b/tests/acceptance/applications/keyedArchiveXmlScalars.test.ts index 301bfb1589..73da482ced 100644 --- a/tests/acceptance/applications/keyedArchiveXmlScalars.test.ts +++ b/tests/acceptance/applications/keyedArchiveXmlScalars.test.ts @@ -105,6 +105,6 @@ cliTest( parseEvidence(reply.structuredContent).normalized_result, ), ).toEqual(result); - expect(await readFile(path)).toEqual(bytes); + expect(Buffer.compare(await readFile(path), bytes)).toBe(0); }, ); diff --git a/tests/acceptance/applications/mobileDirectoryComponents.test.ts b/tests/acceptance/applications/mobileDirectoryComponents.test.ts index ee45298353..fab73ee2b5 100644 --- a/tests/acceptance/applications/mobileDirectoryComponents.test.ts +++ b/tests/acceptance/applications/mobileDirectoryComponents.test.ts @@ -207,7 +207,7 @@ cliTest.for(fixtures)( mode === "files" ? childPackages : [], ); } - expect(await readFile(path)).toEqual(Buffer.from(bytes)); + expect(Buffer.compare(await readFile(path), Buffer.from(bytes))).toBe(0); } }, ); @@ -351,7 +351,7 @@ cliTest.for([ } } if (encrypted) expect(Object.values(graph.components).flat()).toEqual([]); - expect(await readFile(path)).toEqual(Buffer.from(bytes)); + expect(Buffer.compare(await readFile(path), Buffer.from(bytes))).toBe(0); } }, ); diff --git a/tests/acceptance/applications/nativePlistBinaryScalars.test.ts b/tests/acceptance/applications/nativePlistBinaryScalars.test.ts index c68cd82cce..c14bea7051 100644 --- a/tests/acceptance/applications/nativePlistBinaryScalars.test.ts +++ b/tests/acceptance/applications/nativePlistBinaryScalars.test.ts @@ -174,8 +174,8 @@ describe.skipIf(process.platform !== "darwin")( provenance: [{ tool: "file" }], limitations: [], }); - expect(await readFile(binaryPath)).toEqual(binaryBytes); - expect(await readFile(xmlPath)).toEqual(xmlBytes); + expect(Buffer.compare(await readFile(binaryPath), binaryBytes)).toBe(0); + expect(Buffer.compare(await readFile(xmlPath), xmlBytes)).toBe(0); }, 60_000, ); diff --git a/tests/acceptance/applications/nativePlistNesting.test.ts b/tests/acceptance/applications/nativePlistNesting.test.ts index a7f4488fcc..faa2624865 100644 --- a/tests/acceptance/applications/nativePlistNesting.test.ts +++ b/tests/acceptance/applications/nativePlistNesting.test.ts @@ -89,7 +89,7 @@ describe.skipIf(process.platform !== "darwin")( environment, }); const response = await call("inspect_plist", { path }); - expect(await readFile(path)).toEqual(bytes); + expect(Buffer.compare(await readFile(path), bytes)).toBe(0); if (depth === 999) { expect(output.exitCode, output.stderr).toBe(1); const expectedFailure = { diff --git a/tests/acceptance/applications/nibFieldIdentity.test.ts b/tests/acceptance/applications/nibFieldIdentity.test.ts index 166583ffbb..44c31f5315 100644 --- a/tests/acceptance/applications/nibFieldIdentity.test.ts +++ b/tests/acceptance/applications/nibFieldIdentity.test.ts @@ -194,7 +194,7 @@ cliTest( expect(report.limitations).toContainEqual( expect.stringContaining("NSString NS.bytes is not valid UTF-8"), ); - expect(await readFile(path)).toEqual(archive); + expect(Buffer.compare(await readFile(path), archive)).toBe(0); }, 60_000, ); diff --git a/tests/acceptance/applications/packageUrlUnicodeCli.test.ts b/tests/acceptance/applications/packageUrlUnicodeCli.test.ts index 5dcdd46db6..40dc5ec6b3 100644 --- a/tests/acceptance/applications/packageUrlUnicodeCli.test.ts +++ b/tests/acceptance/applications/packageUrlUnicodeCli.test.ts @@ -277,6 +277,6 @@ cliTest.for(["CLI", "stdio MCP"] as const)( }), ); } - expect(await readFile(archive)).toEqual(bytes); + expect(Buffer.compare(await readFile(archive), bytes)).toBe(0); }, ); diff --git a/tests/acceptance/applications/peResources.test.ts b/tests/acceptance/applications/peResources.test.ts index 8e50fe2a5b..154101e0d4 100644 --- a/tests/acceptance/applications/peResources.test.ts +++ b/tests/acceptance/applications/peResources.test.ts @@ -132,7 +132,7 @@ const expectPeWordAlignedRecordsAcrossAdapters = async (input: { contract.outputSchema.parse(response.structuredContent) .normalized_result, ).toEqual(evidence.normalized_result); - expect(await readFile(path)).toEqual(fixture.bytes); + expect(Buffer.compare(await readFile(path), fixture.bytes)).toBe(0); } } }; @@ -227,7 +227,7 @@ const expectPeMappingOwnershipAcrossAdapters = async ( }, ], }); - expect(await readFile(path)).toEqual(bytes); + expect(Buffer.compare(await readFile(path), bytes)).toBe(0); } }; @@ -308,7 +308,7 @@ const expectPeInventoriesAcrossAdapters = async ( expect(contract.outputSchema.parse(JSON.parse(text.text)).evidence_id).toBe( evidence.evidence_id, ); - expect(await readFile(path)).toEqual(fixture.bytes); + expect(Buffer.compare(await readFile(path), fixture.bytes)).toBe(0); } }; @@ -406,7 +406,7 @@ const expectPeEmptyBranchesAcrossAdapters = async ( expect(evidence.subject?.digest?.sha256).toBe( createHash("sha256").update(fixture.bytes).digest("hex"), ); - expect(await readFile(path)).toEqual(fixture.bytes); + expect(Buffer.compare(await readFile(path), fixture.bytes)).toBe(0); } }; diff --git a/tests/acceptance/applications/resourceOnlyPlistSelection.test.ts b/tests/acceptance/applications/resourceOnlyPlistSelection.test.ts index e5a22ed9be..69103a471a 100644 --- a/tests/acceptance/applications/resourceOnlyPlistSelection.test.ts +++ b/tests/acceptance/applications/resourceOnlyPlistSelection.test.ts @@ -69,7 +69,11 @@ it.skipIf(process.platform !== "darwin")( code: "invalid_request", details: { issues: [{ path: ["path"] }] }, }); - expect(await readFile(sourcePath)).toEqual(Buffer.from(source)); - expect(await readFile(decoyPath)).toEqual(Buffer.from(decoy)); + expect( + Buffer.compare(await readFile(sourcePath), Buffer.from(source)), + ).toBe(0); + expect(Buffer.compare(await readFile(decoyPath), Buffer.from(decoy))).toBe( + 0, + ); }, ); diff --git a/tests/acceptance/artifactExtractionCoverage.test.ts b/tests/acceptance/artifactExtractionCoverage.test.ts index 9a0ff8ce4b..9047b52f04 100644 --- a/tests/acceptance/artifactExtractionCoverage.test.ts +++ b/tests/acceptance/artifactExtractionCoverage.test.ts @@ -81,7 +81,9 @@ cliTest.skipIf(process.platform !== "linux").for([false, true])( result.artifacts.map(({ relative_path }) => relative_path), ).toEqual([...files.keys()]); for (const [path, bytes] of files) - expect(await readFile(join(result.output_root, path))).toEqual(bytes); + expect( + Buffer.compare(await readFile(join(result.output_root, path)), bytes), + ).toBe(0); for (const limitations of [result.limitations, evidence.limitations]) { expect( limitations.some((value) => value.includes("not valid UTF-8")), @@ -171,7 +173,9 @@ cliTest.for([false, true])( result.artifacts.map(({ relative_path }) => relative_path), ).toEqual(files.map(([path]) => path)); for (const [path, bytes] of files) { - expect(await readFile(join(result.output_root, path))).toEqual(bytes); + expect( + Buffer.compare(await readFile(join(result.output_root, path)), bytes), + ).toBe(0); expect( result.artifacts.find((file) => file.relative_path === path), ).toMatchObject({ diff --git a/tests/acceptance/devicetreeCli.test.ts b/tests/acceptance/devicetreeCli.test.ts index 07396f9061..b82039b3ce 100644 --- a/tests/acceptance/devicetreeCli.test.ts +++ b/tests/acceptance/devicetreeCli.test.ts @@ -38,7 +38,7 @@ cliTest("inspects a relative DTB and preserves the source", async ({ cli }) => { summary: { model: "Test board" }, coverage: { examined_nodes: 5 }, }); - expect(await readFile(path)).toEqual(bytes); + expect(Buffer.compare(await readFile(path), bytes)).toBe(0); }); cliTest( "does not resolve through a legacy phandle when the canonical value is malformed", diff --git a/tests/acceptance/elfNotesCli.test.ts b/tests/acceptance/elfNotesCli.test.ts index 02bca6dddf..13b31943a3 100644 --- a/tests/acceptance/elfNotesCli.test.ts +++ b/tests/acceptance/elfNotesCli.test.ts @@ -51,7 +51,7 @@ cliTest( expect(evidence.normalized_result.entries[0]?.gnu?.build_id_hex).toBe( "0123456789abcdef0123456789abcdef01234567", ); - expect(await readFile(path)).toEqual(bytes); + expect(Buffer.compare(await readFile(path), bytes)).toBe(0); } }, ); @@ -82,7 +82,7 @@ cliTest( examined_tables: 1, examined_entries: 1, }); - expect(await readFile(path)).toEqual(bytes); + expect(Buffer.compare(await readFile(path), bytes)).toBe(0); }, ); @@ -138,7 +138,7 @@ cliTest( "Compressed ELF note sections are unsupported", ), }); - expect(await readFile(path)).toEqual(bytes); + expect(Buffer.compare(await readFile(path), bytes)).toBe(0); }, ); diff --git a/tests/acceptance/erlangBeamCli.test.ts b/tests/acceptance/erlangBeamCli.test.ts index e0fbf2ba16..36cfdd4676 100644 --- a/tests/acceptance/erlangBeamCli.test.ts +++ b/tests/acceptance/erlangBeamCli.test.ts @@ -39,9 +39,12 @@ const extendedFirstAtomLength = (): Buffer => { const payloadOffset = chunkOffset + 8; const payloadSize = control.readUInt32BE(chunkOffset + 4); const payloadEnd = payloadOffset + payloadSize; - expect(control.subarray(payloadOffset + 4, payloadOffset + 6)).toEqual( - Buffer.from([0x08, 0x10]), - ); + expect( + Buffer.compare( + control.subarray(payloadOffset + 4, payloadOffset + 6), + Buffer.from([0x08, 0x10]), + ), + ).toBe(0); const payload = Buffer.concat([ control.subarray(payloadOffset, payloadOffset + 4), Buffer.from([0x18, 0x00, 0x10]), @@ -153,7 +156,7 @@ cliTest( }), }), ); - expect(await readFile(path)).toEqual(bytes); + expect(Buffer.compare(await readFile(path), bytes)).toBe(0); } }, ); diff --git a/tests/acceptance/goBinaryCli.test.ts b/tests/acceptance/goBinaryCli.test.ts index d52a729242..134b8f01b7 100644 --- a/tests/acceptance/goBinaryCli.test.ts +++ b/tests/acceptance/goBinaryCli.test.ts @@ -550,8 +550,11 @@ for (const format of ["elf", "pe", "macho"] as const) { .toString("utf8"), ).toBe(report.build_info.go_version); expect( - fixture.bytes.subarray(module.offset, module.offset + module.bytes), - ).toEqual(Buffer.from(report.build_info.module_bytes_base64, "base64")); + Buffer.compare( + fixture.bytes.subarray(module.offset, module.offset + module.bytes), + Buffer.from(report.build_info.module_bytes_base64, "base64"), + ), + ).toBe(0); }, ); } diff --git a/tests/acceptance/goBinaryPointerMappingsCli.test.ts b/tests/acceptance/goBinaryPointerMappingsCli.test.ts index 6fc239d49a..6fc14c7a57 100644 --- a/tests/acceptance/goBinaryPointerMappingsCli.test.ts +++ b/tests/acceptance/goBinaryPointerMappingsCli.test.ts @@ -56,7 +56,7 @@ cliTest( const response = await call("inspect_go_binary", { path }); expect(response.isError, JSON.stringify(response)).not.toBe(true); expect(response.structuredContent).toEqual(evidence); - expect(await readFile(path)).toEqual(fixture.bytes); + expect(Buffer.compare(await readFile(path), fixture.bytes)).toBe(0); } const damaged = createGoCrossLoadPointerFixture("contents"); diff --git a/tests/acceptance/godotPck.test.ts b/tests/acceptance/godotPck.test.ts index cbe60ab632..1a1a53d4c0 100644 --- a/tests/acceptance/godotPck.test.ts +++ b/tests/acceptance/godotPck.test.ts @@ -343,7 +343,7 @@ cliTest( ); const before = await readFile(bundle); expect((await cli.run({ arguments: args })).exitCode).toBe(1); - expect(await readFile(bundle)).toEqual(before); + expect(Buffer.compare(await readFile(bundle), before)).toBe(0); await unlink(source); const query = join(root, "query.json"); await writeFile( diff --git a/tests/acceptance/harmonyArtifactJourney.test.ts b/tests/acceptance/harmonyArtifactJourney.test.ts index 0370307438..59fa30b5ff 100644 --- a/tests/acceptance/harmonyArtifactJourney.test.ts +++ b/tests/acceptance/harmonyArtifactJourney.test.ts @@ -188,7 +188,7 @@ test("preserves Harmony archive formats through the public CLI/MCP lifecycle", a expect(empty.structuredContent).toMatchObject({ result: { open: false }, }); - expect(await readFile(path)).toEqual(Buffer.from(bytes)); + expect(Buffer.compare(await readFile(path), Buffer.from(bytes))).toBe(0); } } finally { await client.close(); diff --git a/tests/acceptance/il2cppMetadataCli.test.ts b/tests/acceptance/il2cppMetadataCli.test.ts index 866388a812..18d69fbcc3 100644 --- a/tests/acceptance/il2cppMetadataCli.test.ts +++ b/tests/acceptance/il2cppMetadataCli.test.ts @@ -78,7 +78,7 @@ cliTest( }); expect(response.isError).not.toBe(true); expect(parseCliMetadata(response.structuredContent)).toEqual(report); - expect(await readFile(path)).toEqual(bytes); + expect(Buffer.compare(await readFile(path), bytes)).toBe(0); } for (const [records, declaredCount, declaredBytes] of [ [0, 1, 0], @@ -123,7 +123,7 @@ cliTest( }); expect(response.isError).toBe(true); expect(parseMcpToolError(response).error).toMatchObject(expected); - expect(await readFile(path)).toEqual(fixture.bytes); + expect(Buffer.compare(await readFile(path), fixture.bytes)).toBe(0); } }, 30000, @@ -682,7 +682,7 @@ cliTest( const before = await readFile(bundle); const overwrite = await runFileCli(cli, { arguments: args }); expect(overwrite.exitCode).toBe(1); - expect(await readFile(bundle)).toEqual(before); + expect(Buffer.compare(await readFile(bundle), before)).toBe(0); const saved = await readEvidenceBundle(bundle); if (!saved.ok) throw saved.error; const evidence = saved.value.records[0]; @@ -928,7 +928,7 @@ cliTest( sha256: createHash("sha256").update(bytes).digest("hex"), bytes: bytes.length, }); - expect(await readFile(binaryPath)).toEqual(bytes); + expect(Buffer.compare(await readFile(binaryPath), bytes)).toBe(0); return report; }; const control = await inspect(createIl2cppBinaryFixture()); @@ -1026,7 +1026,7 @@ cliTest( expect(response.isError, JSON.stringify(response)).not.toBe(true); expect(parseCliMetadata(response.structuredContent)).toEqual(report); } - expect(await readFile(binaryPath)).toEqual(bytes); + expect(Buffer.compare(await readFile(binaryPath), bytes)).toBe(0); } await client.ping(); }, diff --git a/tests/acceptance/managedMetadataDelta.test.ts b/tests/acceptance/managedMetadataDelta.test.ts index 77ed826ce1..ef20016df5 100644 --- a/tests/acceptance/managedMetadataDelta.test.ts +++ b/tests/acceptance/managedMetadataDelta.test.ts @@ -127,7 +127,7 @@ cliTest( parseEvidence(response.structuredContent).normalized_result, ).toEqual(evidence.normalized_result); } - expect(await readFile(path)).toEqual(bytes); + expect(Buffer.compare(await readFile(path), bytes)).toBe(0); await rm(path); } const compared = await call("compare_managed_members", { diff --git a/tests/acceptance/managedSelectedScope.test.ts b/tests/acceptance/managedSelectedScope.test.ts index 9bc1172f86..b4d7e2df45 100644 --- a/tests/acceptance/managedSelectedScope.test.ts +++ b/tests/acceptance/managedSelectedScope.test.ts @@ -48,7 +48,7 @@ cliTest( expect(report.coverage.state).toBe("complete"); sources.push(evidence); } - expect(await readFile(path)).toEqual(bytes); + expect(Buffer.compare(await readFile(path), bytes)).toBe(0); await rm(path); const call = await cli.sharedMcpCall!(); for (const [index, right] of sources.slice(1).entries()) { @@ -129,7 +129,7 @@ cliTest( whole.push(evidence); } } - expect(await readFile(path)).toEqual(content); + expect(Buffer.compare(await readFile(path), content)).toBe(0); await rm(path); } const left = managedMemberInspectionSchema.parse( diff --git a/tests/acceptance/modelArtifactCli.test.ts b/tests/acceptance/modelArtifactCli.test.ts index acdcebaf2a..a2eb97524b 100644 --- a/tests/acceptance/modelArtifactCli.test.ts +++ b/tests/acceptance/modelArtifactCli.test.ts @@ -163,7 +163,7 @@ cliTest( coverage, }); expect(summary.summary.limitations).toContainEqual(limitation); - expect(await readFile(path)).toEqual(bytes); + expect(Buffer.compare(await readFile(path), bytes)).toBe(0); await unlink(path); const source = { @@ -419,7 +419,7 @@ cliTest( reason.includes("have released") || reason.includes("has released"), )) expect(summary.summary.limitations).toContain(limitation); - expect(await readFile(path)).toEqual(fixture.bytes); + expect(Buffer.compare(await readFile(path), fixture.bytes)).toBe(0); await unlink(path); await assertRetainedLayoutViews( call, diff --git a/tests/acceptance/nativeDispatchSnapshot.test.ts b/tests/acceptance/nativeDispatchSnapshot.test.ts index a0e6207c83..aa57f1115e 100644 --- a/tests/acceptance/nativeDispatchSnapshot.test.ts +++ b/tests/acceptance/nativeDispatchSnapshot.test.ts @@ -113,8 +113,8 @@ cliTest.skipIf(process.platform !== "darwin")( ).toEqual( savedSnapshot.value.workflow_entries[0]?.execution.analysis_profile, ); - expect(await readFile(first)).toEqual(Buffer.from(bytes)); - expect(await readFile(second)).toEqual(Buffer.from(bytes)); + expect(Buffer.compare(await readFile(first), Buffer.from(bytes))).toBe(0); + expect(Buffer.compare(await readFile(second), Buffer.from(bytes))).toBe(0); }, 30000, ); @@ -170,7 +170,7 @@ cliTest.skipIf(process.platform !== "darwin")( }, }); } - expect(await readFile(target)).toEqual(bytes); + expect(Buffer.compare(await readFile(target), bytes)).toBe(0); await client.ping(); }, 30000, @@ -304,7 +304,7 @@ cliTest.skipIf(process.platform !== "darwin")( }); expect(reobserved.isError, JSON.stringify(reobserved)).not.toBe(true); expect(parseEvidence(reobserved.structuredContent)).toEqual(mcpEvidence); - expect(await readFile(target)).toEqual(Buffer.from(bytes)); + expect(Buffer.compare(await readFile(target), Buffer.from(bytes))).toBe(0); await client.ping(); }, 30000, diff --git a/tests/acceptance/nodeSeaCli.test.ts b/tests/acceptance/nodeSeaCli.test.ts index 4b63a99720..a28623d3e5 100644 --- a/tests/acceptance/nodeSeaCli.test.ts +++ b/tests/acceptance/nodeSeaCli.test.ts @@ -228,7 +228,9 @@ cliTest( expected.map((item) => item.path).sort(), ); for (const { path, body } of expected) - expect(await readFile(join(result.output_root, path))).toEqual(body); + expect( + Buffer.compare(await readFile(join(result.output_root, path)), body), + ).toBe(0); }; const inspected = await cli.run({ diff --git a/tests/acceptance/pyinstallerArchive.test.ts b/tests/acceptance/pyinstallerArchive.test.ts index 0fdad05fe0..eb27a10e3c 100644 --- a/tests/acceptance/pyinstallerArchive.test.ts +++ b/tests/acceptance/pyinstallerArchive.test.ts @@ -289,16 +289,29 @@ cliTest( ); try { expect( - await readFile(join(result.output_root, "pkg", "data.bin")), - ).toEqual(stored); + Buffer.compare( + await readFile(join(result.output_root, "pkg", "data.bin")), + stored, + ), + ).toBe(0); expect( - await readFile(join(result.output_root, "PYZ.pyz", "res", "icon.bin")), - ).toEqual(icon); + Buffer.compare( + await readFile( + join(result.output_root, "PYZ.pyz", "res", "icon.bin"), + ), + icon, + ), + ).toBe(0); expect( - ( - await readFile(join(result.output_root, "PYZ.pyz", "pkg", "mod.pyc")) - ).subarray(16), - ).toEqual(module); + Buffer.compare( + ( + await readFile( + join(result.output_root, "PYZ.pyz", "pkg", "mod.pyc"), + ) + ).subarray(16), + module, + ), + ).toBe(0); } finally { await rm(result.output_root, { recursive: true, force: true }); } @@ -321,9 +334,12 @@ it("extracts legacy and modern CArchive modules with one pyc header through the ); try { for (const module of fixture.modules) { - expect(await readFile(join(extraction.output_root, module.name))).toEqual( - module.bytes, - ); + expect( + Buffer.compare( + await readFile(join(extraction.output_root, module.name)), + module.bytes, + ), + ).toBe(0); expect( extraction.artifacts.find((item) => item.relative_path === module.name), ).toMatchObject({ @@ -465,9 +481,12 @@ it("extracts CArchive members through extract-artifact", async () => { expect( await readFile(join(extracted.output_root, "PYZ.pyz", "app.pyc")), ).toHaveLength(16 + Buffer.from("app-body").length); - expect(await readFile(join(extracted.output_root, "hello"))).toEqual( - Buffer.from("print('hi')"), - ); + expect( + Buffer.compare( + await readFile(join(extracted.output_root, "hello")), + Buffer.from("print('hi')"), + ), + ).toBe(0); expect(await readdir(extracted.output_root)).not.toContain("__pyinstaller__"); expect( extracted.artifacts.some((item) => @@ -564,11 +583,17 @@ it("preserves a PYZ namespace declaration beside a physical CArchive file at the expect.arrayContaining(["PYZ.pyz/ns", "healthy.txt"]), ); expect( - await readFile(join(extracted.output_root, "PYZ.pyz", "ns")), - ).toEqual(Buffer.from("outer data")); - expect(await readFile(join(extracted.output_root, "healthy.txt"))).toEqual( - Buffer.from("healthy sibling"), - ); + Buffer.compare( + await readFile(join(extracted.output_root, "PYZ.pyz", "ns")), + Buffer.from("outer data"), + ), + ).toBe(0); + expect( + Buffer.compare( + await readFile(join(extracted.output_root, "healthy.txt")), + Buffer.from("healthy sibling"), + ), + ).toBe(0); } finally { await rm(extracted.output_root, { recursive: true, force: true }); } diff --git a/tests/acceptance/rustBinaryCli.test.ts b/tests/acceptance/rustBinaryCli.test.ts index 1a4cb4a718..ae409a734d 100644 --- a/tests/acceptance/rustBinaryCli.test.ts +++ b/tests/acceptance/rustBinaryCli.test.ts @@ -586,7 +586,7 @@ cliTest( { stage: "denied", handleId: 0, open: true }, { stage: "released", handleId: 0, fd: -1 }, ]); - expect(await readFile(path)).toEqual(bytes); + expect(Buffer.compare(await readFile(path), bytes)).toBe(0); }, ); diff --git a/tests/acceptance/rustUnnamedSections.test.ts b/tests/acceptance/rustUnnamedSections.test.ts index f6be212e94..5e50bb2c56 100644 --- a/tests/acceptance/rustUnnamedSections.test.ts +++ b/tests/acceptance/rustUnnamedSections.test.ts @@ -52,7 +52,7 @@ cliTest( const response = await call("inspect_rust_binary", { path }); expect(response.isError, JSON.stringify(response)).not.toBe(true); expect(response.structuredContent).toEqual(evidence); - expect(await readFile(path)).toEqual(bytes); + expect(Buffer.compare(await readFile(path), bytes)).toBe(0); } await client.ping(); }, diff --git a/tests/acceptance/setup/clientConfigurationDiagnosticsCli.test.ts b/tests/acceptance/setup/clientConfigurationDiagnosticsCli.test.ts index 8ecf960dbb..3aa9a21432 100644 --- a/tests/acceptance/setup/clientConfigurationDiagnosticsCli.test.ts +++ b/tests/acceptance/setup/clientConfigurationDiagnosticsCli.test.ts @@ -166,7 +166,7 @@ for (const scenario of cases) }); if (!("syntax" in scenario)) expect(uninstall.stdout).not.toContain("not valid JSON"); - expect(await readFile(config)).toEqual(original); + expect(Buffer.compare(await readFile(config), original)).toBe(0); expect(await readdir(dirname(config))).toEqual([ scenario.path.split("/").at(-1), ]); diff --git a/tests/acceptance/setup/geminiWorkspaceTrust.test.ts b/tests/acceptance/setup/geminiWorkspaceTrust.test.ts index a1d19c0125..32d4711cff 100644 --- a/tests/acceptance/setup/geminiWorkspaceTrust.test.ts +++ b/tests/acceptance/setup/geminiWorkspaceTrust.test.ts @@ -63,7 +63,9 @@ cliTest( code: "ENOENT", }); expect(await readFile(trust, "utf8")).toBe(text); - expect(await readFile(fixture.user)).toEqual(fixture.originalUser); + expect( + Buffer.compare(await readFile(fixture.user), fixture.originalUser), + ).toBe(0); } const workspaceText = "{ not json"; @@ -92,7 +94,9 @@ cliTest( }); expect(await readFile(fixture.workspace, "utf8")).toBe(workspaceText); expect(await readFile(trust, "utf8")).toBe("{ not json"); - expect(await readFile(fixture.user)).toEqual(fixture.originalUser); + expect( + Buffer.compare(await readFile(fixture.user), fixture.originalUser), + ).toBe(0); }, ); @@ -135,8 +139,12 @@ for (const scenario of [ ]), }, }); - expect(await readFile(fixture.workspace)).toEqual(malformed); - expect(await readFile(fixture.user)).toEqual(fixture.originalUser); + expect(Buffer.compare(await readFile(fixture.workspace), malformed)).toBe( + 0, + ); + expect( + Buffer.compare(await readFile(fixture.user), fixture.originalUser), + ).toBe(0); await expect( access(`${fixture.workspace}.rea.backup`), ).rejects.toMatchObject({ @@ -162,7 +170,9 @@ for (const scenario of [ ]), }, }); - expect(await readFile(fixture.workspace)).toEqual(excluded); + expect( + Buffer.compare(await readFile(fixture.workspace), excluded), + ).toBe(0); } }, ); @@ -208,8 +218,12 @@ cliTest( ]), }, }); - expect(await readFile(fixture.workspace)).toEqual(original); - expect(await readFile(fixture.user)).toEqual(fixture.originalUser); + expect(Buffer.compare(await readFile(fixture.workspace), original)).toBe( + 0, + ); + expect( + Buffer.compare(await readFile(fixture.user), fixture.originalUser), + ).toBe(0); await expect( access(`${fixture.workspace}.rea.backup`), ).rejects.toMatchObject({ diff --git a/tests/acceptance/setup/selectedSkillSetup.test.ts b/tests/acceptance/setup/selectedSkillSetup.test.ts index 8c76e1ac68..9dd3dad5b8 100644 --- a/tests/acceptance/setup/selectedSkillSetup.test.ts +++ b/tests/acceptance/setup/selectedSkillSetup.test.ts @@ -173,12 +173,18 @@ cliTest( }), ]), }); - expect(await readFile(join(selected, "SKILL.md"))).toEqual( - await readFile(join(source, "SKILL.md")), - ); - expect(await readFile(join(managed, "native-and-artifacts.md"))).toEqual( - before, - ); + expect( + Buffer.compare( + await readFile(join(selected, "SKILL.md")), + await readFile(join(source, "SKILL.md")), + ), + ).toBe(0); + expect( + Buffer.compare( + await readFile(join(managed, "native-and-artifacts.md")), + before, + ), + ).toBe(0); expect((await lstat(join(selected, "references"))).isSymbolicLink()).toBe( true, ); @@ -247,9 +253,12 @@ cliTest( })) { if (!entry.isFile()) continue; const path = relative(bundle, join(entry.parentPath, entry.name)); - expect(await readFile(join(selected, path))).toEqual( - await readFile(join(bundle, path)), - ); + expect( + Buffer.compare( + await readFile(join(selected, path)), + await readFile(join(bundle, path)), + ), + ).toBe(0); } }; await assertAligned(); diff --git a/tests/acceptance/setup/unqualifiedDoctorSkill.test.ts b/tests/acceptance/setup/unqualifiedDoctorSkill.test.ts index 36a1a63f21..606ba8c39e 100644 --- a/tests/acceptance/setup/unqualifiedDoctorSkill.test.ts +++ b/tests/acceptance/setup/unqualifiedDoctorSkill.test.ts @@ -42,7 +42,7 @@ cliTest( }, }, }); - expect(await readFile(skill)).toEqual(original); + expect(Buffer.compare(await readFile(skill), original)).toBe(0); const audit = await run(["doctor"]); expect(audit.json).toMatchObject({ @@ -54,7 +54,7 @@ cliTest( }, }, }); - expect(await readFile(skill)).toEqual(original); + expect(Buffer.compare(await readFile(skill), original)).toBe(0); await mkdir(join(home, ".cursor")); const withMarker = await run(["doctor"]); @@ -67,7 +67,7 @@ cliTest( }, }, }); - expect(await readFile(skill)).toEqual(original); + expect(Buffer.compare(await readFile(skill), original)).toBe(0); await writeFile( skill, @@ -102,7 +102,7 @@ cliTest( }, }, }); - expect(await readFile(reference)).toEqual(retained); + expect(Buffer.compare(await readFile(reference), retained)).toBe(0); } const emptyHome = await createTestTempDirectory( @@ -146,6 +146,6 @@ cliTest( healthy: false, identity: { skill: { state: "stale", installed_version: null } }, }); - expect(await readFile(foreignSkill)).toEqual(foreignBytes); + expect(Buffer.compare(await readFile(foreignSkill), foreignBytes)).toBe(0); }, ); diff --git a/tests/acceptance/sqliteDatabaseCli.test.ts b/tests/acceptance/sqliteDatabaseCli.test.ts index 83b129c77e..b3b1c16a05 100644 --- a/tests/acceptance/sqliteDatabaseCli.test.ts +++ b/tests/acceptance/sqliteDatabaseCli.test.ts @@ -322,7 +322,7 @@ cliTest.runIf(SQLITE_NATIVE_LIMITS_AVAILABLE)( { path, table: "selected", columns: ["ID"] }, ]) expect((await call("inspect_sqlite_database", input)).isError).toBe(true); - expect(await readFile(path)).toEqual(before); + expect(Buffer.compare(await readFile(path), before)).toBe(0); }, ); @@ -632,7 +632,7 @@ cliTest }); expect(response.isError, JSON.stringify(response)).not.toBe(true); expect(response.structuredContent).toEqual(result.json); - expect(await readFile(path)).toEqual(original); + expect(Buffer.compare(await readFile(path), original)).toBe(0); }, ); @@ -1074,7 +1074,7 @@ cliTest.runIf(SQLITE_NATIVE_LIMITS_AVAILABLE)( }, }, }); - expect(await readFile(path)).toEqual(before); + expect(Buffer.compare(await readFile(path), before)).toBe(0); }, ); @@ -1136,7 +1136,7 @@ cliTest.runIf(SQLITE_NATIVE_LIMITS_AVAILABLE)( expect(response.isError, JSON.stringify(response)).not.toBe(true); expect(response.structuredContent).toEqual(result.json); } - expect(await readFile(path)).toEqual(original); + expect(Buffer.compare(await readFile(path), original)).toBe(0); }, ); @@ -1338,7 +1338,7 @@ cliTest.runIf(SQLITE_NATIVE_LIMITS_AVAILABLE)( const response = await call("inspect_sqlite_database", { path }); expect(response.isError, JSON.stringify(response)).not.toBe(true); expect(response.structuredContent).toEqual(result.json); - expect(await readFile(path)).toEqual(original); + expect(Buffer.compare(await readFile(path), original)).toBe(0); } }, ); diff --git a/tests/acceptance/tools/nativeFunctionMatching.test.ts b/tests/acceptance/tools/nativeFunctionMatching.test.ts index 3afed436ac..8c083945ff 100644 --- a/tests/acceptance/tools/nativeFunctionMatching.test.ts +++ b/tests/acceptance/tools/nativeFunctionMatching.test.ts @@ -162,7 +162,7 @@ const verifyPortableCli = async ( expect(ids.has(dependency.evidence_id)).toBe(true); expect(ids.has(unrelated.evidence_id)).toBe(false); expect(ids.has(evidenceId)).toBe(true); - expect(await readFile(bundlePath)).toEqual(original); + expect(Buffer.compare(await readFile(bundlePath), original)).toBe(0); const incomplete = await cli.run({ arguments: [ "match-native-functions", diff --git a/tests/acceptance/tools/protobufDescriptorFiles.test.ts b/tests/acceptance/tools/protobufDescriptorFiles.test.ts index 80c6354139..6b964a085a 100644 --- a/tests/acceptance/tools/protobufDescriptorFiles.test.ts +++ b/tests/acceptance/tools/protobufDescriptorFiles.test.ts @@ -198,7 +198,9 @@ cliTest( }, }); } - expect(await readFile(descriptor)).toEqual(Buffer.from(bytes)); + expect( + Buffer.compare(await readFile(descriptor), Buffer.from(bytes)), + ).toBe(0); expect(await readFile(capture, "utf8")).toBe(text); } }, @@ -295,7 +297,9 @@ cliTest( expect(parseEvidence(response.structuredContent).evidence_id).toBe( evidence.evidence_id, ); - expect(await readFile(descriptor)).toEqual(Buffer.from(bytes)); + expect( + Buffer.compare(await readFile(descriptor), Buffer.from(bytes)), + ).toBe(0); } }, ); diff --git a/tests/acceptance/tools/protobufDescriptorIdentity.test.ts b/tests/acceptance/tools/protobufDescriptorIdentity.test.ts index b19dbf8315..cef90a4968 100644 --- a/tests/acceptance/tools/protobufDescriptorIdentity.test.ts +++ b/tests/acceptance/tools/protobufDescriptorIdentity.test.ts @@ -165,7 +165,9 @@ cliTest( protobuf_coverage: { status: "partial", inspected_records: 0 }, }, }); - expect(await readFile(descriptor)).toEqual(Buffer.from(bytes)); + expect( + Buffer.compare(await readFile(descriptor), Buffer.from(bytes)), + ).toBe(0); } expect(await readFile(capture, "utf8")).toBe(text); }, @@ -257,6 +259,8 @@ cliTest( expect(parseEvidence(response.structuredContent).evidence_id).toBe( evidence.evidence_id, ); - expect(await readFile(descriptor)).toEqual(Buffer.from(definition)); + expect( + Buffer.compare(await readFile(descriptor), Buffer.from(definition)), + ).toBe(0); }, ); diff --git a/tests/acceptance/tools/protobufRegistryIdentity.test.ts b/tests/acceptance/tools/protobufRegistryIdentity.test.ts index 6c2004f7dc..f6cc0bb65b 100644 --- a/tests/acceptance/tools/protobufRegistryIdentity.test.ts +++ b/tests/acceptance/tools/protobufRegistryIdentity.test.ts @@ -149,7 +149,9 @@ cliTest( parseMcpToolError(response).error, `${scope}-${reverse}`, ).toMatchObject(expected); - expect(await readFile(descriptor)).toEqual(Buffer.from(bytes)); + expect( + Buffer.compare(await readFile(descriptor), Buffer.from(bytes)), + ).toBe(0); } expect(await readFile(capture, "utf8")).toBe(text); }, @@ -216,6 +218,8 @@ cliTest( { number: 1, schema_name: fieldName, schema_value: "1" }, ]); } - expect(await readFile(descriptor)).toEqual(Buffer.from(bytes)); + expect(Buffer.compare(await readFile(descriptor), Buffer.from(bytes))).toBe( + 0, + ); }, ); diff --git a/tests/boundary/binaryDiagnostics/RecordedCrashStructureBindings.test.ts b/tests/boundary/binaryDiagnostics/RecordedCrashStructureBindings.test.ts index 6db6ada328..38c0dd9489 100644 --- a/tests/boundary/binaryDiagnostics/RecordedCrashStructureBindings.test.ts +++ b/tests/boundary/binaryDiagnostics/RecordedCrashStructureBindings.test.ts @@ -60,7 +60,7 @@ it.each([ report.decoder_diagnostics, ), ).not.toThrow(); - expect(snapshot).toEqual(original); + expect(Buffer.compare(snapshot, original)).toBe(0); expect(report.note_interpretation_completeness).toBe("unknown"); expect(report.live_process_identity).toBe("unknown"); }, @@ -178,6 +178,6 @@ it.each(["/usr/bin/fixture", "../../fixture", "/a/../fixture"])( expect(validateCoreFileMappingAuthority(report, snapshot).ok).toBe( path === "/usr/bin/fixture", ); - expect(snapshot).toEqual(original); + expect(Buffer.compare(snapshot, original)).toBe(0); }, ); diff --git a/tests/boundary/binaryDiagnostics/recordedCrashProvider.test.ts b/tests/boundary/binaryDiagnostics/recordedCrashProvider.test.ts index 46343156b4..f2d3d05db6 100644 --- a/tests/boundary/binaryDiagnostics/recordedCrashProvider.test.ts +++ b/tests/boundary/binaryDiagnostics/recordedCrashProvider.test.ts @@ -159,7 +159,9 @@ it const request = requestSchema.parse( JSON.parse(await readFile(spawn.arguments.at(-1) ?? "", "utf8")), ); - expect(await readFile(request.snapshot_path)).toEqual(bytes); + expect( + Buffer.compare(await readFile(request.snapshot_path), bytes), + ).toBe(0); const payload = structuredClone(value); alterRecordedCrashReply(payload, scenario); const { @@ -201,7 +203,7 @@ it capturedOutput: { stdout: "recorded warning" }, }, }); - expect(await readFile(path)).toEqual(bytes); + expect(Buffer.compare(await readFile(path), bytes)).toBe(0); await expect(access(owned)).rejects.toMatchObject({ code: "ENOENT" }); }, ); @@ -257,7 +259,9 @@ it const request = requestSchema.parse( JSON.parse(await readFile(spawn.arguments.at(-1) ?? "", "utf8")), ); - expect(await readFile(request.snapshot_path)).toEqual(bytes); + expect( + Buffer.compare(await readFile(request.snapshot_path), bytes), + ).toBe(0); if (snapshotPath === "") { snapshotPath = request.snapshot_path; owned = spawn.cwd ?? ""; @@ -341,14 +345,14 @@ it } if (scenario === "cancelled" || scenario === "cancelled-exec") expectDebuggerStopped(debuggerProcess); - expect(await readFile(path)).toEqual(bytes); + expect(Buffer.compare(await readFile(path), bytes)).toBe(0); if ( scenario === "cleanup-failure" || scenario === "decoder-cleanup-failure" || scenario === "cancelled-exec-foreign" ) { await expect(access(owned)).resolves.toBeUndefined(); - expect(await readFile(snapshotPath)).toEqual(bytes); + expect(Buffer.compare(await readFile(snapshotPath), bytes)).toBe(0); const refused = await provider.inspect({ path, include_debugger_context: true, @@ -538,7 +542,7 @@ it.runIf(host)( include_debugger_context: true, pwntools_timeout_ms: 1_500, }); - expect(await readFile(path)).toEqual(bytes); + expect(Buffer.compare(await readFile(path), bytes)).toBe(0); if (result.ok) throw new Error("expected debugger timeout"); const projected = projectAnalysisError(result.error); expect(projected.code).toBe("provider_timeout"); diff --git a/tests/boundary/browser/sazWebCapture.test.ts b/tests/boundary/browser/sazWebCapture.test.ts index 1b5fff10c6..084bfa84b0 100644 --- a/tests/boundary/browser/sazWebCapture.test.ts +++ b/tests/boundary/browser/sazWebCapture.test.ts @@ -286,7 +286,7 @@ it("checks trailer credentials before hidden metadata blocks analysis and withho ); expect(opaque.records[0]?.protobuf).toBeUndefined(); expect(opaque.protobuf_coverage?.status).toBe("partial"); - expect(await readFile(path)).toEqual(bytes); + expect(Buffer.compare(await readFile(path), bytes)).toBe(0); }); it("keeps automatic framing applicability unknown when SAZ Content-Type is excluded or conflicting", async () => { @@ -394,7 +394,7 @@ it("keeps automatic framing applicability unknown when SAZ Content-Type is exclu expect(explicit.records[0]?.protobuf).toMatchObject([ { status: "complete", fields: [{ number: 1, value: "1" }] }, ]); - expect(await readFile(path)).toEqual(bytes); + expect(Buffer.compare(await readFile(path), bytes)).toBe(0); }); it("inspects native SAZ through the public CLI and MCP with equal evidence, bytes and archive coordinates", async () => { @@ -470,7 +470,7 @@ it("inspects native SAZ through the public CLI and MCP with equal evidence, byte expect(capture.artifact.sha256).toBe( createHash("sha256").update(bytes).digest("hex"), ); - expect(await readFile(path)).toEqual(bytes); + expect(Buffer.compare(await readFile(path), bytes)).toBe(0); const { client, call } = await connectLocalToolsMcp(); const tool = (await client.listTools()).tools.find( @@ -624,7 +624,7 @@ it("keeps CRLF separators inside a body after LF-only HTTP headers", async () => bytes: body.length, sha256: createHash("sha256").update(body).digest("hex"), }); - expect(await readFile(path)).toEqual(bytes); + expect(Buffer.compare(await readFile(path), bytes)).toBe(0); }); it("reports zero-byte SAZ HTTP members as null instead of rejecting the archive", async () => { @@ -890,7 +890,7 @@ it.each([ expect(error.details).not.toHaveProperty("partial_observation"); expect(mcpFailure).not.toHaveProperty("normalized_result"); expect(mcpFailure.error.details).not.toHaveProperty("partial_observation"); - expect(await readFile(path)).toEqual(bytes); + expect(Buffer.compare(await readFile(path), bytes)).toBe(0); } expect(JSON.stringify(result)).not.toContain("malformed-private-input"); }); diff --git a/tests/boundary/cli/artifactCallerSelection.test.ts b/tests/boundary/cli/artifactCallerSelection.test.ts index 72c86395ce..ae5f76bbe8 100644 --- a/tests/boundary/cli/artifactCallerSelection.test.ts +++ b/tests/boundary/cli/artifactCallerSelection.test.ts @@ -62,7 +62,7 @@ cliTest( action: "Evidence file already exists. Choose another output path.", }, }); - expect(await readFile(output)).toEqual(before); + expect(Buffer.compare(await readFile(output), before)).toBe(0); const next = await cli.run({ arguments: [ "inspect-artifact", diff --git a/tests/boundary/cli/commandEvidenceDelivery.test.ts b/tests/boundary/cli/commandEvidenceDelivery.test.ts index 1f04135019..f6bcf935a4 100644 --- a/tests/boundary/cli/commandEvidenceDelivery.test.ts +++ b/tests/boundary/cli/commandEvidenceDelivery.test.ts @@ -199,7 +199,7 @@ it.each(kinds)( detail: "summary", evidence_output: destination, }); - expect(await readFile(destination)).toEqual(original); + expect(Buffer.compare(await readFile(destination), original)).toBe(0); expect(output).toHaveProperty("code"); expect(output).not.toHaveProperty("summary"); expect(output).toHaveProperty("details.partial_observation.evidence_id"); diff --git a/tests/boundary/cli/environmentBuildCli.test.ts b/tests/boundary/cli/environmentBuildCli.test.ts index 206ec83960..3a3de02e6c 100644 --- a/tests/boundary/cli/environmentBuildCli.test.ts +++ b/tests/boundary/cli/environmentBuildCli.test.ts @@ -88,9 +88,13 @@ cliTest( expect(files).toEqual(await listFiles(join(root, "three-step"))); expect(files).toHaveLength(7); for (const file of files) - expect(await readFile(join(root, "built", file)), file).toEqual( - await readFile(join(root, "three-step", file)), - ); + expect( + Buffer.compare( + await readFile(join(root, "built", file)), + await readFile(join(root, "three-step", file)), + ), + file, + ).toBe(0); expect(built.json).toMatchObject({ tool_count: (exported.json as { tool_count: number }).tool_count, files: (exported.json as { files: unknown[] }).files, diff --git a/tests/boundary/cli/environmentExportCli.test.ts b/tests/boundary/cli/environmentExportCli.test.ts index 97fd6ba47d..23395c9db1 100644 --- a/tests/boundary/cli/environmentExportCli.test.ts +++ b/tests/boundary/cli/environmentExportCli.test.ts @@ -116,11 +116,17 @@ cliTest( .sort(); expect(written).toEqual(BUNDLE_FILES); expect( - await readFile(join(directory, "envs/grafana-example/manifest.json")), - ).toEqual(await readFile(files.manifestPath)); + Buffer.compare( + await readFile(join(directory, "envs/grafana-example/manifest.json")), + await readFile(files.manifestPath), + ), + ).toBe(0); expect( - await readFile(join(directory, "envs/grafana-example/evidence.json")), - ).toEqual(await readFile(files.capturePath)); + Buffer.compare( + await readFile(join(directory, "envs/grafana-example/evidence.json")), + await readFile(files.capturePath), + ), + ).toBe(0); const dockerfile = await readFile( join(directory, "envs/grafana-example/Dockerfile"), "utf8", @@ -253,8 +259,11 @@ cliTest.skipIf(process.platform === "win32")( ).toBeGreaterThan(0); } expect( - await readFile(join(directory, "envs/grafana-example/evidence.json")), - ).toEqual(await readFile(files.capturePath)); + Buffer.compare( + await readFile(join(directory, "envs/grafana-example/evidence.json")), + await readFile(files.capturePath), + ), + ).toBe(0); }, ); @@ -290,10 +299,13 @@ cliTest.skipIf(process.platform === "win32")( package_source: { kind: "tarball", path: packagePath }, }); expect( - await readFile( - join(physical, "bundle/envs/grafana-example/rea-agents.tgz"), + Buffer.compare( + await readFile( + join(physical, "bundle/envs/grafana-example/rea-agents.tgz"), + ), + bytes, ), - ).toEqual(bytes); + ).toBe(0); await expect( readFile(join(root, "bundle/README.md")), ).rejects.toMatchObject({ code: "ENOENT" }); diff --git a/tests/boundary/cli/environmentExportInputsCli.test.ts b/tests/boundary/cli/environmentExportInputsCli.test.ts index 17ebf3cc53..cf1e986ac9 100644 --- a/tests/boundary/cli/environmentExportInputsCli.test.ts +++ b/tests/boundary/cli/environmentExportInputsCli.test.ts @@ -49,9 +49,12 @@ for (const role of ["manifest", "evidence"] as const) { timeoutMs: 30_000, }); expect(await readFile(marker, "utf8")).toBe("read"); - expect(await readFile(input)).toEqual( - Buffer.concat([before, Buffer.from(" ")]), - ); + expect( + Buffer.compare( + await readFile(input), + Buffer.concat([before, Buffer.from(" ")]), + ), + ).toBe(0); expect(result.exitCode, result.stderr).toBe(1); expect(result.json).toMatchObject({ code: "invalid_request", diff --git a/tests/boundary/cli/idaDoctorRegistration.test.ts b/tests/boundary/cli/idaDoctorRegistration.test.ts index e7965c8663..1424087761 100644 --- a/tests/boundary/cli/idaDoctorRegistration.test.ts +++ b/tests/boundary/cli/idaDoctorRegistration.test.ts @@ -82,7 +82,9 @@ cliTest.skipIf(process.platform !== "darwin").for([true, false])( }), ]), }); - expect(await readFile(registration)).toEqual(registrationBytes); + expect( + Buffer.compare(await readFile(registration), registrationBytes), + ).toBe(0); expect(await readdir(physical)).toEqual(before); if (usable) { expect(await readdir(parent)).toEqual([]); diff --git a/tests/boundary/cli/il2cppSharedSuffixNames.test.ts b/tests/boundary/cli/il2cppSharedSuffixNames.test.ts index f8166b4abb..21b761268c 100644 --- a/tests/boundary/cli/il2cppSharedSuffixNames.test.ts +++ b/tests/boundary/cli/il2cppSharedSuffixNames.test.ts @@ -59,7 +59,7 @@ cliTest( expect(mcpOutput.isError, JSON.stringify(mcpOutput)).not.toBe(true); const mcpResult = parseResult(mcpOutput.structuredContent); expect(mcpResult).toEqual(cliResult); - expect(await readFile(path)).toEqual(bytes); + expect(Buffer.compare(await readFile(path), bytes)).toBe(0); } }, ); diff --git a/tests/boundary/cli/keyedArchivePrototypeKeyCli.test.ts b/tests/boundary/cli/keyedArchivePrototypeKeyCli.test.ts index d730ee2326..5d3b15bd69 100644 --- a/tests/boundary/cli/keyedArchivePrototypeKeyCli.test.ts +++ b/tests/boundary/cli/keyedArchivePrototypeKeyCli.test.ts @@ -117,7 +117,7 @@ cliTest( ), ).toEqual(result); } - expect(await readFile(path)).toEqual(bytes); + expect(Buffer.compare(await readFile(path), bytes)).toBe(0); } }, ); diff --git a/tests/boundary/cli/managedMemberSummaryCli.test.ts b/tests/boundary/cli/managedMemberSummaryCli.test.ts index 82cfbecbe5..7756fca24c 100644 --- a/tests/boundary/cli/managedMemberSummaryCli.test.ts +++ b/tests/boundary/cli/managedMemberSummaryCli.test.ts @@ -346,7 +346,7 @@ cliTest( expect(result.json).not.toHaveProperty("evidence_id"); expect(result.json).not.toHaveProperty("normalized_result"); } - expect(await readFile(path)).toEqual(bytes); + expect(Buffer.compare(await readFile(path), bytes)).toBe(0); expect(await readdir(root)).toEqual(["fixture.dll"]); }, ); diff --git a/tests/boundary/cli/nativePlistCli.test.ts b/tests/boundary/cli/nativePlistCli.test.ts index 192f2337ab..3b10587291 100644 --- a/tests/boundary/cli/nativePlistCli.test.ts +++ b/tests/boundary/cli/nativePlistCli.test.ts @@ -113,7 +113,7 @@ describe.skipIf(process.platform !== "darwin")( ]), }, }); - expect(await readFile(path)).toEqual(source); + expect(Buffer.compare(await readFile(path), source)).toBe(0); }, ); diff --git a/tests/boundary/cli/referenceSourceUtf8.test.ts b/tests/boundary/cli/referenceSourceUtf8.test.ts index a2451a31ac..f33bd285c2 100644 --- a/tests/boundary/cli/referenceSourceUtf8.test.ts +++ b/tests/boundary/cli/referenceSourceUtf8.test.ts @@ -185,8 +185,11 @@ cliTest.skipIf(process.platform === "win32")( }), ); expect( - await readlink(join(root, "alias.ts"), { encoding: "buffer" }), - ).toEqual(rawTarget); + Buffer.compare( + await readlink(join(root, "alias.ts"), { encoding: "buffer" }), + rawTarget, + ), + ).toBe(0); }, ); diff --git a/tests/boundary/evm/evmoleInterfaceProvider.test.ts b/tests/boundary/evm/evmoleInterfaceProvider.test.ts index 70867e67ef..6191079291 100644 --- a/tests/boundary/evm/evmoleInterfaceProvider.test.ts +++ b/tests/boundary/evm/evmoleInterfaceProvider.test.ts @@ -894,7 +894,7 @@ it expect(evidence.subject?.digest.sha256).toBe( createHash("sha256").update(source).digest("hex"), ); - expect(await readFile(path)).toEqual(source); + expect(Buffer.compare(await readFile(path), source)).toBe(0); expect(evidence).toMatchObject({ operation: "inspect_evm_interface", parameters: { path, encoding: "hex" }, diff --git a/tests/boundary/filesystem/appleDispatchMetadataStrings.test.ts b/tests/boundary/filesystem/appleDispatchMetadataStrings.test.ts index da230322c0..e22b794033 100644 --- a/tests/boundary/filesystem/appleDispatchMetadataStrings.test.ts +++ b/tests/boundary/filesystem/appleDispatchMetadataStrings.test.ts @@ -71,7 +71,7 @@ it.each([ ({ facet }) => facet === "objc_properties_categories", ), ).toMatchObject({ status: "complete", reason: null }); - expect(await readFile(path)).toEqual(bytes); + expect(Buffer.compare(await readFile(path), bytes)).toBe(0); }, ); @@ -105,6 +105,6 @@ it.each([ status: "partial", reason: expect.stringContaining("property_decode_failed"), }); - expect(await readFile(path)).toEqual(bytes); + expect(Buffer.compare(await readFile(path), bytes)).toBe(0); }, ); diff --git a/tests/boundary/filesystem/artifactLiteralBackslash.test.ts b/tests/boundary/filesystem/artifactLiteralBackslash.test.ts index b56bdb6327..931e0a9c34 100644 --- a/tests/boundary/filesystem/artifactLiteralBackslash.test.ts +++ b/tests/boundary/filesystem/artifactLiteralBackslash.test.ts @@ -117,7 +117,9 @@ it.skipIf(process.platform === "win32").each(["directory", "asar"] as const)( [...new Set(NAMES.map((name) => name.split("/")[0]))].sort(), ); for (const name of NAMES) - expect(await readFile(join(output, name))).toEqual(CONTENT); + expect(Buffer.compare(await readFile(join(output, name)), CONTENT)).toBe( + 0, + ); }, ); diff --git a/tests/boundary/filesystem/artifactZipIntegrity.test.ts b/tests/boundary/filesystem/artifactZipIntegrity.test.ts index 8ad141f4d0..232ca521d7 100644 --- a/tests/boundary/filesystem/artifactZipIntegrity.test.ts +++ b/tests/boundary/filesystem/artifactZipIntegrity.test.ts @@ -83,7 +83,7 @@ for (const zip64 of [false, true]) { expect(extraction.artifacts).toEqual([]); expect(extraction.containment_verified).toBe(true); expect(await readdir(extraction.output_root)).toEqual([]); - expect(await readFile(archive)).toEqual(bytes); + expect(Buffer.compare(await readFile(archive), bytes)).toBe(0); }, ); } diff --git a/tests/boundary/filesystem/asarHeaderAdmission.test.ts b/tests/boundary/filesystem/asarHeaderAdmission.test.ts index f7fb0d30a0..20ead66f6d 100644 --- a/tests/boundary/filesystem/asarHeaderAdmission.test.ts +++ b/tests/boundary/filesystem/asarHeaderAdmission.test.ts @@ -72,7 +72,7 @@ it("admits a header produced by the upstream ASAR serializer", async () => { headerBytes: original.readUInt32LE(4), jsonBytes: original.readUInt32LE(12), }); - expect(await readFile(archive)).toEqual(original); + expect(Buffer.compare(await readFile(archive), original)).toBe(0); }); it("rejects every truncated prefix of a real serialized header", async () => { diff --git a/tests/boundary/filesystem/asarListingAdmission.test.ts b/tests/boundary/filesystem/asarListingAdmission.test.ts index 9762adff9f..349e3b0343 100644 --- a/tests/boundary/filesystem/asarListingAdmission.test.ts +++ b/tests/boundary/filesystem/asarListingAdmission.test.ts @@ -47,7 +47,7 @@ it("rejects path expansion before publishing any inventory, preserving the input reason: "limit", message: "ASAR expanded listing exceeds its 64 MiB representation budget", }); - expect(await readFile(path)).toEqual(bytes); + expect(Buffer.compare(await readFile(path), bytes)).toBe(0); }); it("reads deeply nested packed metadata without recursive listing or member lookup", async () => { @@ -59,7 +59,12 @@ it("reads deeply nested packed metadata without recursive listing or member look count++; if (entry.kind === "file") { expect(entry.path).toBe(`${"d/".repeat(3500)}f0`); - expect(await buffer(await reader.open(entry))).toEqual(Buffer.alloc(0)); + expect( + Buffer.compare( + await buffer(await reader.open(entry)), + Buffer.alloc(0), + ), + ).toBe(0); } } expect(count).toBe(3501); @@ -123,9 +128,12 @@ it("preserves POSIX literal backslashes and rejects Windows component separators const entries = await list(); expect(entries).toHaveLength(1); expect(entries[0]?.path).toBe("a\\b"); - expect(await buffer(await reader.open(entries[0]!))).toEqual( - Buffer.alloc(0), - ); + expect( + Buffer.compare( + await buffer(await reader.open(entries[0]!)), + Buffer.alloc(0), + ), + ).toBe(0); } } finally { await reader.close(); diff --git a/tests/boundary/filesystem/boundaryLintConfiguration.test.ts b/tests/boundary/filesystem/boundaryLintConfiguration.test.ts new file mode 100644 index 0000000000..133b13fe31 --- /dev/null +++ b/tests/boundary/filesystem/boundaryLintConfiguration.test.ts @@ -0,0 +1,82 @@ +import { spawnSync } from "node:child_process"; +import { writeFile } from "node:fs/promises"; +import { join } from "node:path"; +import { fileURLToPath } from "node:url"; + +import { expect, it } from "vitest"; +import { z } from "zod"; + +import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js"; + +const repository = fileURLToPath(new URL("../../../", import.meta.url)); +const oxlint = join(repository, "node_modules/oxlint/bin/oxlint"); +const config = join(repository, ".oxlintrc.json"); +const reportSchema = z.object({ + diagnostics: z.array( + z.object({ code: z.string().optional(), severity: z.string() }), + ), +}); + +const lint = (path: string) => { + const result = spawnSync( + process.execPath, + [oxlint, "--threads=1", "--config", config, "--format", "json", path], + { cwd: repository, encoding: "utf8", timeout: 15_000 }, + ); + expect(result.error).toBeUndefined(); + expect(result.signal).toBeNull(); + const report = reportSchema.parse(JSON.parse(result.stdout)); + return { status: result.status, diagnostics: report.diagnostics }; +}; + +it("enforces boundary and stock correctness rules through the repository config", async () => { + const root = await createTestTempDirectory("rea-boundary-lint-"); + const path = join(root, "boundary-probe.ts"); + const imports = [ + 'import { readFile } from "node:fs/promises";', + 'import { expect } from "vitest";', + 'import { AnalysisCancelledError } from "./analysisErrorCore.js";', + ].join("\n"); + await writeFile( + path, + `${imports}\nasync function inspect(path: string, signal: AbortSignal) { + const before = await readFile(path); + expect(await readFile(path)).toEqual(before); + if (signal.aborted) throw new AnalysisCancelledError("inspect"); + new Promise(async resolve => resolve(1)); + const optional = undefined as { value: number } | undefined; + (optional?.value).toString(); + return; + console.log("unreachable"); + }`, + ); + const failing = lint(path); + expect(failing.status).toBe(1); + expect( + failing.diagnostics + .filter(({ severity }) => severity === "error") + .map(({ code, severity }) => ({ code: code ?? "", severity })) + .sort((left, right) => left.code.localeCompare(right.code)), + ).toEqual([ + { code: "eslint(no-async-promise-executor)", severity: "error" }, + { code: "eslint(no-unreachable)", severity: "error" }, + { code: "eslint(no-unsafe-optional-chaining)", severity: "error" }, + { code: "rea(cancellation-cause)", severity: "error" }, + { code: "rea(no-buffer-deep-equality)", severity: "error" }, + ]); + + await writeFile( + path, + `${imports}\nasync function inspect(path: string, signal: AbortSignal) { + const before = await readFile(path); + expect((await readFile(path)).equals(before)).toBe(true); + if (signal.aborted) + throw new AnalysisCancelledError("inspect", { cause: signal.reason }); + }`, + ); + const corrected = lint(path); + expect(corrected.status).toBe(0); + expect( + corrected.diagnostics.filter(({ code }) => code?.startsWith("rea(")), + ).toEqual([]); +}); diff --git a/tests/boundary/filesystem/boundedFileBytes.test.ts b/tests/boundary/filesystem/boundedFileBytes.test.ts index faa832de19..8a753eb2b2 100644 --- a/tests/boundary/filesystem/boundedFileBytes.test.ts +++ b/tests/boundary/filesystem/boundedFileBytes.test.ts @@ -16,7 +16,10 @@ describe("bounded file byte reads", () => { await writeFile(path, content); const handle = await open(path, "r"); try { - expect(await readBoundedFileBytes(handle, limit)).toEqual(content); + const actual = await readBoundedFileBytes(handle, limit); + if (actual === undefined) + throw new Error("Expected complete file bytes"); + expect(actual.equals(content)).toBe(true); } finally { await handle.close(); } diff --git a/tests/boundary/filesystem/bufferDeepEqualityLint.test.ts b/tests/boundary/filesystem/bufferDeepEqualityLint.test.ts new file mode 100644 index 0000000000..38c433b070 --- /dev/null +++ b/tests/boundary/filesystem/bufferDeepEqualityLint.test.ts @@ -0,0 +1,137 @@ +import { RuleTester } from "oxlint/plugins-dev"; +import { describe, it } from "vitest"; + +import noBufferDeepEquality from "../../../scripts/lint/no-buffer-deep-equality.mjs"; + +RuleTester.describe = describe; +RuleTester.it = it; + +new RuleTester({ + languageOptions: { sourceType: "module", env: { node: true } }, +}).run("no-buffer-deep-equality", noBufferDeepEquality, { + valid: [ + `import { readFile } from "node:fs/promises"; + import { expect } from "vitest"; + expect(await readFile(path, "utf8")).toEqual(text);`, + `import { readFile } from "node:fs/promises"; + import { expect } from "vitest"; + expect(await readFile(path, { encoding: "utf8" })).toEqual(text);`, + `import { readFile } from "node:fs/promises"; + import { expect } from "vitest"; + expect(await readFile(path, options)).toEqual(value);`, + `import { readFile } from "node:fs/promises"; + import { expect } from "vitest"; + const pending = readFile(path); + expect(pending).toEqual(promise);`, + `import { readFile } from "node:fs/promises"; + import { expect } from "vitest"; + expect(readFile(path, "utf8")).resolves.toEqual(text);`, + `import { readFile } from "node:fs/promises"; + import { expect } from "vitest"; + expect((await readFile(path)).equals(before)).toBe(true);`, + `import { expect } from "vitest"; + const Buffer = { from: () => ({}) }; + expect(Buffer.from("x")).toEqual(value);`, + `import { expect } from "vitest"; + function check(Buffer) { expect(Buffer.alloc(4)).toEqual(value); }`, + `import { expect } from "vitest"; + function check(Buffer) { + const Bytes = Buffer; + expect(Bytes.from("x")).toEqual(value); + }`, + `import { readFile } from "node:fs/promises"; + function check(expect) { expect(readFile(path)).toEqual(value); }`, + `import { expect } from "vitest"; + function check(readFile) { expect(readFile(path)).toEqual(value); }`, + `import { expect } from "other-assertions"; + expect(Buffer.from("x")).toEqual(value);`, + `expect(Buffer.from("x")).toEqual(value);`, + `import { readFile } from "other-fs"; + import { expect } from "vitest"; + expect(readFile(path)).toEqual(value);`, + `import { readFile } from "node:fs/promises"; + import { expect } from "vitest"; + expect(await readFile(...args)).toEqual(value);`, + `import { readFile } from "node:fs/promises"; + import { expect } from "vitest"; + const data = await readFile(path, "utf8"); + expect(data).toEqual(text);`, + `import { expect } from "vitest"; + expect([1, 2]).toEqual([1, 2]);`, + `import { expect } from "vitest"; + let bytes = Buffer.from("x"); + expect(bytes).toEqual(value);`, + ], + invalid: [ + { + // Exact pre-fix ELF test shape: readFile without an encoding returns Buffer. + code: `import { readFile } from "node:fs/promises"; + import { expect } from "vitest"; + const before = await readFile(path); + expect(await readFile(path)).toEqual(before);`, + errors: [{ messageId: "nativeBytes" }], + }, + { + code: `import { readFileSync as read } from "node:fs"; + import { expect } from "vitest"; + const before = read(path); + expect(value).toStrictEqual(before);`, + errors: [{ messageId: "nativeBytes" }], + }, + { + code: `import { readFile as sourceRead } from "node:fs/promises"; + import { expect } from "vitest"; + const read = sourceRead; + const bytes = await read(path); + expect(bytes).not.toEqual(value);`, + errors: [{ messageId: "nativeBytes" }], + }, + { + code: `import * as fs from "node:fs/promises"; + import { expect } from "vitest"; + expect(await fs.readFile(path)).toEqual(value);`, + errors: [{ messageId: "nativeBytes" }], + }, + { + code: `import { Buffer as NodeBuffer } from "node:buffer"; + import { expect } from "vitest"; + expect(value).toEqual(NodeBuffer.from("x"));`, + errors: [{ messageId: "nativeBytes" }], + }, + { + code: `import { expect } from "vitest"; + const Bytes = Buffer; + const bytes = Bytes.allocUnsafe(16); + expect(bytes).toEqual(value);`, + errors: [{ messageId: "nativeBytes" }], + }, + { + code: `import { readFile } from "node:fs/promises"; + import { expect } from "vitest"; + expect(await readFile(path), "bytes unchanged").toEqual(before);`, + errors: [{ messageId: "nativeBytes" }], + }, + { + code: `import { readFile } from "node:fs/promises"; + import { expect } from "vitest"; + expect(readFile(path)).resolves.toEqual(Buffer.from("x"));`, + errors: [{ messageId: "nativeBytes" }], + }, + { + code: `import { readFile } from "node:fs/promises"; + import { expect } from "vitest"; + expect(readFile(path)).resolves.not.toEqual(value);`, + errors: [{ messageId: "nativeBytes" }], + }, + { + code: `import { expect } from "vitest"; + expect(Promise.reject(Buffer.from("x"))).rejects.toStrictEqual(Buffer.from("x"));`, + errors: [{ messageId: "nativeBytes" }], + }, + { + code: `import { expect as check } from "vitest"; + check(Buffer.alloc(4)).toEqual(value);`, + errors: [{ messageId: "nativeBytes" }], + }, + ], +}); diff --git a/tests/boundary/filesystem/cancellationCauseLint.test.ts b/tests/boundary/filesystem/cancellationCauseLint.test.ts new file mode 100644 index 0000000000..b88562a149 --- /dev/null +++ b/tests/boundary/filesystem/cancellationCauseLint.test.ts @@ -0,0 +1,135 @@ +import { RuleTester } from "oxlint/plugins-dev"; +import { describe, it } from "vitest"; + +import cancellationCauseRule from "../../../scripts/lint/cancellation-cause.mjs"; + +RuleTester.describe = describe; +RuleTester.it = it; + +const core = + 'import { AnalysisCancelledError } from "./domain/analysisErrorCore.js";'; +const options = + 'import type { ExecutionOptions } from "./application/AnalysisProvider.js";'; + +new RuleTester({ + languageOptions: { parserOptions: { lang: "ts" }, sourceType: "module" }, +}).run("cancellation-cause", cancellationCauseRule, { + valid: [ + // The corrected WABT pre-abort branch retains even null or object reasons. + `${core}\n${options}\nfunction run(options?: ExecutionOptions) { + if (options?.signal?.aborted) + throw new AnalysisCancelledError("inspect", { + cause: options.signal.reason, + }); + }`, + `${core}\nfunction run(signal?: AbortSignal) { + if (signal?.aborted === true) + return err(new AnalysisCancelledError("inspect", { cause: signal.reason })); + }`, + `${core}\nfunction run(signal?: AbortSignal) { + if (signal?.aborted) + throw new AnalysisCancelledError("inspect", { 'cause': signal.reason }); + }`, + `${core}\nfunction close() { + if (this.closed) throw new AnalysisCancelledError("inspect"); + }`, + `${core}\nfunction run(signal?: AbortSignal, other?: boolean) { + if (signal?.aborted || other) + throw new AnalysisCancelledError("inspect"); + }`, + `${core}\nfunction run(signal?: AbortSignal) { + if (signal?.aborted) + return () => new AnalysisCancelledError("inspect"); + }`, + `${core}\nfunction run(signal?: AbortSignal) { + if (signal?.aborted) + throw new AnalysisCancelledError("inspect", { ...forwardedContext }); + }`, + `${core}\nfunction run(signal?: AbortSignal) { + if (signal?.aborted) + throw new AnalysisCancelledError("inspect", forwardedContext); + }`, + `${core}\nfunction run(signal?: AbortSignal) { + if (signal?.aborted) + throw new AnalysisCancelledError("inspect", { [forwardedKey]: value }); + }`, + `${core}\nfunction run(signal?: AbortSignal) { + if (signal?.aborted) { + const AnalysisCancelledError = Error; + throw new AnalysisCancelledError("inspect"); + } + }`, + `${core}\nfunction run(signal?: AbortSignal) { + if (signal?.aborted) throw new Error("cancelled"); + }`, + // Names alone are insufficient: these are unrelated local structures. + `${core}\ntype AbortSignal = { aborted: boolean }; + function run(signal: AbortSignal) { + if (signal.aborted) throw new AnalysisCancelledError("inspect"); + }`, + `${core}\ntype ExecutionOptions = { signal: { aborted: boolean } }; + function run(options: ExecutionOptions) { + if (options.signal.aborted) + throw new AnalysisCancelledError("inspect"); + }`, + `${core}\nfunction run(signal?: unknown) { + if (signal?.aborted) throw new AnalysisCancelledError("inspect"); + }`, + ], + invalid: [ + { + code: `${core}\n${options}\nfunction run(options?: ExecutionOptions) { + if (options?.signal?.aborted) + throw new AnalysisCancelledError("inspect"); + }`, + errors: [{ messageId: "missingCause" }], + }, + { + code: `${core}\nfunction run(signal?: AbortSignal) { + if (signal?.aborted === true) + return err(new AnalysisCancelledError("inspect")); + }`, + errors: [{ messageId: "missingCause" }], + }, + { + code: `${core}\nfunction run(signal?: AbortSignal) { + if (signal?.aborted && ready) + throw new AnalysisCancelledError("inspect", { diagnostics }); + }`, + errors: [{ messageId: "missingCause" }], + }, + { + code: `import { AnalysisCancelledError as Cancelled } from + "./domain/analysisErrorCore.js"; + function run(signal?: AbortSignal) { + if (signal?.aborted) throw new Cancelled("inspect"); + }`, + errors: [{ messageId: "missingCause" }], + }, + { + code: `${core}\n${options}\nfunction run(options?: ExecutionOptions) { + const signal = options?.signal; + if (signal?.aborted) throw new AnalysisCancelledError("inspect"); + }`, + errors: [{ messageId: "missingCause" }], + }, + { + code: `${core}\nimport type { ExecutionOptions as Options } from + "./application/AnalysisProvider.js"; + function run(options?: Options) { + if (options?.signal?.aborted) + return err(new AnalysisCancelledError("inspect")); + }`, + errors: [{ messageId: "missingCause" }], + }, + { + code: `${core}\nfunction run(signal?: AbortSignal) { + if (signal?.aborted) { + const failure = new AnalysisCancelledError("inspect"); + return err(failure); + } + }`, + errors: [{ messageId: "missingCause" }], + }, + ], +}); diff --git a/tests/boundary/filesystem/ciPortableBuildCache.test.ts b/tests/boundary/filesystem/ciPortableBuildCache.test.ts index c059818b61..ab5933e879 100644 --- a/tests/boundary/filesystem/ciPortableBuildCache.test.ts +++ b/tests/boundary/filesystem/ciPortableBuildCache.test.ts @@ -488,7 +488,9 @@ it.skipIf(process.platform === "win32").each([ await rm(join(root, "out"), { recursive: true }); const restored = execute("npm run --silent test:prepare"); expect(restored).toContain(`Cached: ${hits} cached, 2 total`); - expect(await readFile(join(root, "out/build.bin"))).toEqual(artifact); + expect( + Buffer.compare(await readFile(join(root, "out/build.bin")), artifact), + ).toBe(0); await expect(readFile(join(root, "out/seed.bin"))).rejects.toMatchObject({ code: "ENOENT", }); diff --git a/tests/boundary/filesystem/claudeDesktopClientConfiguration.test.ts b/tests/boundary/filesystem/claudeDesktopClientConfiguration.test.ts index 1c64d76348..494565b587 100644 --- a/tests/boundary/filesystem/claudeDesktopClientConfiguration.test.ts +++ b/tests/boundary/filesystem/claudeDesktopClientConfiguration.test.ts @@ -484,7 +484,7 @@ describe("MSIX Claude Desktop registration lifecycle", () => { throw new Error("Expected a blocked update"); expect(failedConfigurationMessage(result)).toContain(privateConfig); expect(failedConfigurationMessage(result)).toContain("Repair access"); - expect(await readFile(appDataConfig)).toEqual(original); + expect(Buffer.compare(await readFile(appDataConfig), original)).toBe(0); expect(await readdir(dirname(appDataConfig))).toEqual([CONFIG]); } finally { await chmod(privateDirectory, mode); diff --git a/tests/boundary/filesystem/clientConfigurationBom.test.ts b/tests/boundary/filesystem/clientConfigurationBom.test.ts index 9eea4cb41e..c10c76741a 100644 --- a/tests/boundary/filesystem/clientConfigurationBom.test.ts +++ b/tests/boundary/filesystem/clientConfigurationBom.test.ts @@ -96,10 +96,18 @@ describe.each(["vscode", "copilot_cli", "opencode", "commandcode"])( status: "unchanged", }, ); - expect(await readFile(client.configPath)).toEqual(Buffer.from(updated)); - expect(await readFile(`${client.configPath}.rea.backup`)).toEqual( - Buffer.from(original), - ); + expect( + Buffer.compare( + await readFile(client.configPath), + Buffer.from(updated), + ), + ).toBe(0); + expect( + Buffer.compare( + await readFile(`${client.configPath}.rea.backup`), + Buffer.from(original), + ), + ).toBe(0); }, ); }, @@ -125,7 +133,9 @@ it.each(['\uFEFF{"servers": {"rea": @}}', '\uFEFF{"setting":\uFEFFtrue}'])( expect( await configureClientConfiguration(client, {}, ["rea", "mcp"]), ).toEqual({ status: "failed", reason: "readback" }); - expect(await readFile(configPath)).toEqual(Buffer.from(original)); + expect( + Buffer.compare(await readFile(configPath), Buffer.from(original)), + ).toBe(0); expect(await readdir(home)).toEqual(["mcp.json"]); }, ); @@ -171,9 +181,12 @@ it("preserves BOM-prefixed JSONC values and comments through update and removal" expect(parseClientConfiguration(removed, client.format).document).toEqual( parseClientConfiguration(original, client.format).document, ); - expect(await readFile(`${client.configPath}.rea.backup`)).toEqual( - Buffer.from(original), - ); + expect( + Buffer.compare( + await readFile(`${client.configPath}.rea.backup`), + Buffer.from(original), + ), + ).toBe(0); }); it.each(["", "\n \n", "\uFEFF"])( diff --git a/tests/boundary/filesystem/clientConfigurationTransactions.test.ts b/tests/boundary/filesystem/clientConfigurationTransactions.test.ts index a22ce751fc..f00eaf6fb2 100644 --- a/tests/boundary/filesystem/clientConfigurationTransactions.test.ts +++ b/tests/boundary/filesystem/clientConfigurationTransactions.test.ts @@ -205,7 +205,9 @@ it("accepts a literal UTF-8 replacement character without changing its value", a ["rea", "mcp"], ); expect(result.status).toBe("configured"); - expect(await readFile(`${configPath}.rea.backup`)).toEqual(original); + expect( + Buffer.compare(await readFile(`${configPath}.rea.backup`), original), + ).toBe(0); expect(JSON.parse(await readFile(configPath, "utf8")).label).toBe("\uFFFD"); }); diff --git a/tests/boundary/filesystem/dosComTarget.test.ts b/tests/boundary/filesystem/dosComTarget.test.ts index 5968c769c6..24bc64fded 100644 --- a/tests/boundary/filesystem/dosComTarget.test.ts +++ b/tests/boundary/filesystem/dosComTarget.test.ts @@ -27,7 +27,7 @@ describe("DOS COM admission", () => { sha256: createHash("sha256").update(bytes).digest("hex"), }, }); - expect(await readFile(path)).toEqual(bytes); + expect(Buffer.compare(await readFile(path), bytes)).toBe(0); const detected = await parseBinaryTarget(path); expect(detected.ok && detected.value.format).not.toBe("dos-com"); }); @@ -59,7 +59,7 @@ describe("DOS COM admission", () => { architecture: "x86", sha256: createHash("sha256").update(bytes).digest("hex"), }); - expect(await readFile(path)).toEqual(bytes); + expect(Buffer.compare(await readFile(path), bytes)).toBe(0); }); it("rejects contradictory target kind", async () => { const directory = await createTestTempDirectory("rea-com-target-"); diff --git a/tests/boundary/filesystem/fileHandleChunks.test.ts b/tests/boundary/filesystem/fileHandleChunks.test.ts index 67c26e290d..4c823d3d02 100644 --- a/tests/boundary/filesystem/fileHandleChunks.test.ts +++ b/tests/boundary/filesystem/fileHandleChunks.test.ts @@ -84,7 +84,7 @@ it("removes cancellation observation after normal stream completion", async () = buffer: Buffer.alloc(0), })) as unknown as FileHandle["read"]; const source = readFileHandleChunks({ read }, { signal: controller.signal }); - expect(await buffer(source)).toEqual(Buffer.alloc(0)); + expect(Buffer.compare(await buffer(source), Buffer.alloc(0))).toBe(0); expect(remove).toHaveBeenCalledWith("abort", expect.any(Function)); controller.abort(null); expect(source.errored).toBeNull(); @@ -111,9 +111,12 @@ it.each(["range", "eof"])( }) as FileHandle["read"]; const source = readFileHandleChunks({ read }, { start: 5, end: 7 }); expect(source.readCoverage()).toEqual({ kind: "incomplete", bytes: 0n }); - expect(await buffer(source)).toEqual( - Buffer.alloc(boundary === "range" ? 3 : 1, 0x61), - ); + expect( + Buffer.compare( + await buffer(source), + Buffer.alloc(boundary === "range" ? 3 : 1, 0x61), + ), + ).toBe(0); const coverage = source.readCoverage(); expect(coverage).toEqual({ kind: "complete", diff --git a/tests/boundary/filesystem/ghidraProfileArtifactOwnership.test.ts b/tests/boundary/filesystem/ghidraProfileArtifactOwnership.test.ts index 7d847495d2..98874971af 100644 --- a/tests/boundary/filesystem/ghidraProfileArtifactOwnership.test.ts +++ b/tests/boundary/filesystem/ghidraProfileArtifactOwnership.test.ts @@ -353,7 +353,7 @@ it.each(["seed", "extension"] as const)( cleanupIncomplete: true, cleanupResources: [output], }); - expect(await readFile(output)).toEqual(bytes); + expect(Buffer.compare(await readFile(output), bytes)).toBe(0); allowClose = true; await resources.retryCleanup(); expect(error).toMatchObject({ cleanupIncomplete: false }); diff --git a/tests/boundary/filesystem/keyedArchiveNumberMetadata.test.ts b/tests/boundary/filesystem/keyedArchiveNumberMetadata.test.ts index 9531fb743f..14efe4a46f 100644 --- a/tests/boundary/filesystem/keyedArchiveNumberMetadata.test.ts +++ b/tests/boundary/filesystem/keyedArchiveNumberMetadata.test.ts @@ -110,7 +110,7 @@ it("keeps exact integers when two object-table entries share one record", async expect(result.limitations).not.toContainEqual( expect.stringMatching(/metadata could not be completely observed/u), ); - expect(await readFile(fixture.path)).toEqual(fixture.bytes); + expect(Buffer.compare(await readFile(fixture.path), fixture.bytes)).toBe(0); }); cliTest( diff --git a/tests/boundary/filesystem/keyedArchiveScalarIdentity.test.ts b/tests/boundary/filesystem/keyedArchiveScalarIdentity.test.ts index 8160b5b841..eaee5fbc35 100644 --- a/tests/boundary/filesystem/keyedArchiveScalarIdentity.test.ts +++ b/tests/boundary/filesystem/keyedArchiveScalarIdentity.test.ts @@ -186,7 +186,7 @@ cliTest( message: expect.stringContaining("9007199254740992"), }); const after = await readFile(path); - expect(after).toEqual(bytes); + expect(Buffer.compare(after, bytes)).toBe(0); expect(createHash("sha256").update(after).digest("hex")).toBe(originalHash); }, ); diff --git a/tests/boundary/filesystem/rawBinaryTarget.test.ts b/tests/boundary/filesystem/rawBinaryTarget.test.ts index 2757f05073..72e2acbc88 100644 --- a/tests/boundary/filesystem/rawBinaryTarget.test.ts +++ b/tests/boundary/filesystem/rawBinaryTarget.test.ts @@ -69,7 +69,7 @@ describe("raw-binary admission", () => { sha256: createHash("sha256").update(bytes).digest("hex"), }, }); - expect(await readFile(path)).toEqual(bytes); + expect(Buffer.compare(await readFile(path), bytes)).toBe(0); const detected = await parseBinaryTarget(path); expect(detected.ok && detected.value.format).not.toBe("raw-binary"); }); diff --git a/tests/boundary/filesystem/regularFileCancellation.test.ts b/tests/boundary/filesystem/regularFileCancellation.test.ts index 4fb159d10b..82a639c63d 100644 --- a/tests/boundary/filesystem/regularFileCancellation.test.ts +++ b/tests/boundary/filesystem/regularFileCancellation.test.ts @@ -434,7 +434,7 @@ it.each( expect(admitted?.fd).toBe(-1); expect(resources?.hasPending ?? false).toBe(false); await resources?.retryCleanup(); - expect(await readFile(path)).toEqual(bytes); + expect(Buffer.compare(await readFile(path), bytes)).toBe(0); } finally { vi.restoreAllMocks(); await resources?.close(); diff --git a/tests/boundary/filesystem/safeOutputTreePublication.test.ts b/tests/boundary/filesystem/safeOutputTreePublication.test.ts index 127619ecef..dbe9c22500 100644 --- a/tests/boundary/filesystem/safeOutputTreePublication.test.ts +++ b/tests/boundary/filesystem/safeOutputTreePublication.test.ts @@ -68,9 +68,12 @@ it.skipIf(process.platform === "win32")( output, dirname(output), ]); - expect(await readFile(join(output, "a", "b", "payload.txt"))).toEqual( - bytes, - ); + expect( + Buffer.compare( + await readFile(join(output, "a", "b", "payload.txt")), + bytes, + ), + ).toBe(0); } } finally { observation.refuseSync = undefined; diff --git a/tests/boundary/filesystem/setupClientConfigCleanupOwnership.test.ts b/tests/boundary/filesystem/setupClientConfigCleanupOwnership.test.ts index 9f819d53e8..839aa427da 100644 --- a/tests/boundary/filesystem/setupClientConfigCleanupOwnership.test.ts +++ b/tests/boundary/filesystem/setupClientConfigCleanupOwnership.test.ts @@ -130,7 +130,7 @@ it("retains invalid UTF-8 diagnostics and the uninstall read owner when close fa status: "failed", detail: expect.stringContaining("not valid UTF-8"), }); - expect(await readFile(path)).toEqual(original); + expect(Buffer.compare(await readFile(path), original)).toBe(0); expect(closeAttempts).toHaveLength(3); expect(closeAttempts.every((handle) => handle === closeAttempts[0])).toBe( true, @@ -280,7 +280,9 @@ describe("setup client configuration cleanup ownership", () => { expect(configure).not.toHaveBeenCalled(); expect(closeAttempts).toHaveLength(2); expect(closeAttempts[0]).toBe(closeAttempts[1]); - expect(await readFile(client.configPath)).toEqual(original); + expect( + Buffer.compare(await readFile(client.configPath), original), + ).toBe(0); await expectCloseReclaimsHandle(host, closeAttempts, release); expect(failure.cleanupIncomplete).toBe(false); diff --git a/tests/boundary/filesystem/setupSkill.test.ts b/tests/boundary/filesystem/setupSkill.test.ts index dbeb443837..47e7953599 100644 --- a/tests/boundary/filesystem/setupSkill.test.ts +++ b/tests/boundary/filesystem/setupSkill.test.ts @@ -152,8 +152,8 @@ describe("skill recovery byte preservation", () => { }); if (result.ok) throw new Error("Expected the injected write failure"); expect(result.error.recoveryBackupPaths).toContain(recovery); - expect(await readFile(recovery)).toEqual(original); - expect(await readFile(manifest)).toEqual(original); + expect(Buffer.compare(await readFile(recovery), original)).toBe(0); + expect(Buffer.compare(await readFile(manifest), original)).toBe(0); expect(result.error.rollbackFailures).toEqual([]); expect(await readFile(firstBackup, "utf8")).toBe( "historical instructions V0\n", diff --git a/tests/boundary/filesystem/setupSkillPublication.test.ts b/tests/boundary/filesystem/setupSkillPublication.test.ts index 6923d3cda9..56d2d3bec3 100644 --- a/tests/boundary/filesystem/setupSkillPublication.test.ts +++ b/tests/boundary/filesystem/setupSkillPublication.test.ts @@ -87,14 +87,16 @@ describe("skill publication ownership", () => { expect.objectContaining({ path: manifest }), ]); const expected = change === "mode" ? published : userBytes; - expect(await readFile(manifest)).toEqual(expected); + if (expected === undefined) + throw new Error("Expected published skill bytes"); + expect(Buffer.compare(await readFile(manifest), expected)).toBe(0); if (existing) expect(await readFile(`${manifest}.rea.backup`, "utf8")).toBe( "original instructions\n", ); else expect(result.error.recoveryBackupPaths).toEqual([]); expect(await result.error.retryCleanup()).toEqual([]); - expect(await readFile(manifest)).toEqual(expected); + expect(Buffer.compare(await readFile(manifest), expected)).toBe(0); }, ); @@ -156,8 +158,10 @@ describe("skill publication recovery", () => { if (result.ok) throw new Error("Expected readback failure"); expect(result.error.stage).toBe("readback"); expect(result.error.reason).toBe("readback unavailable"); - expect(await readFile(manifest)).toEqual(bytes); - expect(await readFile(`${manifest}.rea.backup`)).toEqual(bytes); + expect(Buffer.compare(await readFile(manifest), bytes)).toBe(0); + expect( + Buffer.compare(await readFile(`${manifest}.rea.backup`), bytes), + ).toBe(0); if (process.platform !== "win32") expect((await stat(manifest)).mode & 0o777).toBe(0o640); expect( diff --git a/tests/boundary/filesystem/stableArtifact.test.ts b/tests/boundary/filesystem/stableArtifact.test.ts index d3616d111d..2b03a7a75a 100644 --- a/tests/boundary/filesystem/stableArtifact.test.ts +++ b/tests/boundary/filesystem/stableArtifact.test.ts @@ -24,9 +24,12 @@ it("reads exact binary and empty bytes and rejects symlink/size/cancellation bou const root = await createTestTempDirectory("rea-stable-artifact-"); const path = join(root, "source.bin"); await writeFile(path, Buffer.from([0, 255, 254])); - expect((await readStableArtifact(path, 3)).bytes).toEqual( - Buffer.from([0, 255, 254]), - ); + expect( + Buffer.compare( + (await readStableArtifact(path, 3)).bytes, + Buffer.from([0, 255, 254]), + ), + ).toBe(0); await expect(readStableArtifact(path, 2)).rejects.toMatchObject({ reason: "limit", }); diff --git a/tests/boundary/filesystem/staticCheckReports.test.ts b/tests/boundary/filesystem/staticCheckReports.test.ts index 10ef066744..c133e21cfd 100644 --- a/tests/boundary/filesystem/staticCheckReports.test.ts +++ b/tests/boundary/filesystem/staticCheckReports.test.ts @@ -1,5 +1,5 @@ import { spawnSync } from "node:child_process"; -import { access, mkdir, readFile, writeFile } from "node:fs/promises"; +import { access, cp, mkdir, readFile, writeFile } from "node:fs/promises"; import { dirname, join, sep } from "node:path"; import { fileURLToPath } from "node:url"; import { expect, it } from "vitest"; @@ -151,6 +151,9 @@ it("excludes translation staging while retaining authored website errors", async const root = await fixture(); for (const name of [".gitignore", ".oxlintrc.json"]) await writeFile(join(root, name), await readFile(join(repository, name))); + await cp(join(repository, "scripts/lint"), join(root, "scripts/lint"), { + recursive: true, + }); const authored = "website/public/assets/authored.js"; const staged = "website/.i18n-regression/site/examples/notes-electron/main.js"; @@ -163,7 +166,7 @@ it("excludes translation staging while retaining authored website errors", async ); } - const result = invoke(root, "lint"); + const result = invoke(root, "lint", "website"); expect(result.error).toBeUndefined(); expect(result.status).toBe(1); expect(result.stdout).toContain("1 errors, 0 warnings"); diff --git a/tests/boundary/filesystem/targetDiagnostics.test.ts b/tests/boundary/filesystem/targetDiagnostics.test.ts index 5cf02b2452..4dd1f014aa 100644 --- a/tests/boundary/filesystem/targetDiagnostics.test.ts +++ b/tests/boundary/filesystem/targetDiagnostics.test.ts @@ -365,7 +365,7 @@ describe("caller-selected and inferred target interpretation", () => { ok: true, value: { kind: "database", format: "analysis-database" }, }); - expect(await readFile(databasePath)).toEqual(bytes); + expect(Buffer.compare(await readFile(databasePath), bytes)).toBe(0); }, ); diff --git a/tests/boundary/filesystem/verifierCommandOutput.test.ts b/tests/boundary/filesystem/verifierCommandOutput.test.ts index bc85ffcb7c..968085892b 100644 --- a/tests/boundary/filesystem/verifierCommandOutput.test.ts +++ b/tests/boundary/filesystem/verifierCommandOutput.test.ts @@ -37,8 +37,10 @@ it("writes exact Unicode and empty output bytes and returns the original output" expect(await saveVerifierCommandOutput(async () => output, paths)).toBe( output, ); - expect(await readFile(paths.stdout)).toEqual(Buffer.from(output.stdout)); - expect(await readFile(paths.stderr)).toEqual(Buffer.alloc(0)); + expect( + Buffer.compare(await readFile(paths.stdout), Buffer.from(output.stdout)), + ).toBe(0); + expect(Buffer.compare(await readFile(paths.stderr), Buffer.alloc(0))).toBe(0); }); it("retains both failed-command streams while preserving its exact cause", async () => { @@ -59,10 +61,13 @@ it("retains both failed-command streams while preserving its exact cause", async throw cause; }, paths), ).rejects.toBe(cause); - expect(await readFile(paths.stdout)).toEqual(Buffer.alloc(0)); - expect(await readFile(paths.stderr)).toEqual( - Buffer.from(cause.snapshot.stderr.text), - ); + expect(Buffer.compare(await readFile(paths.stdout), Buffer.alloc(0))).toBe(0); + expect( + Buffer.compare( + await readFile(paths.stderr), + Buffer.from(cause.snapshot.stderr.text), + ), + ).toBe(0); }); it("writes available partial failure output without replacing unavailable evidence", async () => { @@ -84,7 +89,7 @@ it("writes available partial failure output without replacing unavailable eviden throw cause; }, paths), ).rejects.toBe(cause); - expect(await readFile(paths.stdout)).toEqual(original); + expect(Buffer.compare(await readFile(paths.stdout), original)).toBe(0); expect(await readFile(paths.stderr, "utf8")).toBe(cause.snapshot.stderr.text); }); diff --git a/tests/boundary/filesystem/verifierFileSnapshots.test.ts b/tests/boundary/filesystem/verifierFileSnapshots.test.ts index 8b4a0736a2..65b8ed6ebb 100644 --- a/tests/boundary/filesystem/verifierFileSnapshots.test.ts +++ b/tests/boundary/filesystem/verifierFileSnapshots.test.ts @@ -44,7 +44,9 @@ it("preserves selected path spelling, UTF-8 bytes and missing-file facts", async { path: missing, exists: false, content: null }, { path, exists: true, content: Buffer.from(content, "utf8") }, ]); - expect(await readFile(path)).toEqual(Buffer.from(content, "utf8")); + expect( + Buffer.compare(await readFile(path), Buffer.from(content, "utf8")), + ).toBe(0); }); it("detects distinct invalid UTF-8 bytes in verifier file readback", async () => { diff --git a/tests/boundary/filesystem/webScriptExport.test.ts b/tests/boundary/filesystem/webScriptExport.test.ts index 74e08c6fce..24fd34ad11 100644 --- a/tests/boundary/filesystem/webScriptExport.test.ts +++ b/tests/boundary/filesystem/webScriptExport.test.ts @@ -105,13 +105,21 @@ describe("empty URL component publication", () => { for (const [index, script] of result.scripts.entries()) { if (script.content.state !== "exported") throw new Error("Missing source bytes"); - expect(script.url).toBe(scripts[index]?.url); + const expected = scripts[index]; + if (expected === undefined) throw new Error("Missing expected source"); + expect(script.url).toBe(expected.url); expect(script.content.layout).toBe(index === 0 ? "url-path" : "isolated"); expect( - await readFile( - join(result.analysis_input.input_path, script.content.relative_path), + Buffer.compare( + await readFile( + join( + result.analysis_input.input_path, + script.content.relative_path, + ), + ), + expected.bytes, ), - ).toEqual(scripts[index]?.bytes); + ).toBe(0); } const manifest = JSON.parse(await readFile(result.manifest.path, "utf8")); expect(manifest.scripts).toEqual(result.scripts); @@ -158,15 +166,16 @@ describe("captured script publication boundary", () => { const bytes = await readFile( join(result.analysis_input.input_path, script.content.relative_path), ); - expect(bytes).toEqual( - capture.events.items - .filter(({ kind }) => kind === "network-content") - .map((event) => - event.kind === "network-content" && event.body.state === "captured" - ? Buffer.from(event.body.content, "base64") - : Buffer.alloc(0), - )[index], - ); + const expected = capture.events.items + .filter(({ kind }) => kind === "network-content") + .map((event) => + event.kind === "network-content" && event.body.state === "captured" + ? Buffer.from(event.body.content, "base64") + : Buffer.alloc(0), + )[index]; + if (expected === undefined) + throw new Error("Missing expected script bytes"); + expect(bytes.equals(expected)).toBe(true); } const analyzed = await analyzeJavaScriptApplication(result.analysis_input); if (!analyzed.ok) throw analyzed.error; @@ -239,11 +248,10 @@ describe("captured script publication boundary", () => { expect(persisted.limitations).toContain( PLAYWRIGHT_RESPONSE_BODY_PROVENANCE_LIMITATION, ); - expect( - await readFile( - join(parsed.analysis_input.input_path, first.content.relative_path), - ), - ).toEqual(binary); + const exportedBytes = await readFile( + join(parsed.analysis_input.input_path, first.content.relative_path), + ); + expect(exportedBytes.equals(binary)).toBe(true); expect( await readFile( join(parsed.analysis_input.input_path, second.content.relative_path), @@ -1372,12 +1380,20 @@ it("isolates an observed exclusive-create alias using typed facts when diagnosti for (const [index, script] of result.scripts.entries()) { if (script.content.state !== "exported") throw new Error("Missing script bytes"); - expect(script.url).toBe(scripts[index]?.url); + const expected = scripts[index]; + if (expected === undefined) throw new Error("Missing expected script"); + expect(script.url).toBe(expected.url); expect( - await readFile( - join(result.analysis_input.input_path, script.content.relative_path), + Buffer.compare( + await readFile( + join( + result.analysis_input.input_path, + script.content.relative_path, + ), + ), + expected.bytes, ), - ).toEqual(scripts[index]?.bytes); + ).toBe(0); } } finally { write.mockRestore(); diff --git a/tests/boundary/firmware/firmwarePublicationPaths.test.ts b/tests/boundary/firmware/firmwarePublicationPaths.test.ts index 6ec6c7a700..6d21371da1 100644 --- a/tests/boundary/firmware/firmwarePublicationPaths.test.ts +++ b/tests/boundary/firmware/firmwarePublicationPaths.test.ts @@ -143,7 +143,7 @@ it("publishes the writer's normalized path while retaining source spelling and u ]); const published = result.files[0]; if (published === undefined) throw new Error("Expected published child"); - expect(await readFile(published.path)).toEqual(bytes); + expect(Buffer.compare(await readFile(published.path), bytes)).toBe(0); expect(result).toMatchObject({ coverage: "partial", extraction_gaps: [ diff --git a/tests/boundary/firmware/firmwareReportFiles.test.ts b/tests/boundary/firmware/firmwareReportFiles.test.ts index b1be22304d..d5c2e13357 100644 --- a/tests/boundary/firmware/firmwareReportFiles.test.ts +++ b/tests/boundary/firmware/firmwareReportFiles.test.ts @@ -277,7 +277,7 @@ it("does not activate an abort-bound source during awaited destination setup", a await expect(access(join(root, "published"))).rejects.toMatchObject({ code: "ENOENT", }); - expect(await readFile(source)).toEqual(bytes); + expect(Buffer.compare(await readFile(source), bytes)).toBe(0); }); it.skipIf(process.platform === "win32" || process.getuid?.() === 0)( diff --git a/tests/boundary/firmware/firmwareWorkspaceRecovery.test.ts b/tests/boundary/firmware/firmwareWorkspaceRecovery.test.ts index 8193bf87be..dad2285010 100644 --- a/tests/boundary/firmware/firmwareWorkspaceRecovery.test.ts +++ b/tests/boundary/firmware/firmwareWorkspaceRecovery.test.ts @@ -113,7 +113,7 @@ it.skipIf(process.platform !== "linux" || process.getuid?.() === 0)( await fixture.close(); } await assertFirmwareCleanup(fixture.launches); - expect(await readFile(fixture.path)).toEqual(sourceBytes); + expect(Buffer.compare(await readFile(fixture.path), sourceBytes)).toBe(0); for (const output of [fixture.output, join(fixture.root, "second-output")]) expect(await readFile(join(output, "rootfs/config"), "utf8")).toBe( "firmware=true\n", diff --git a/tests/boundary/gpu/codeObjects.test.ts b/tests/boundary/gpu/codeObjects.test.ts index cf1f8c9071..a508d009ff 100644 --- a/tests/boundary/gpu/codeObjects.test.ts +++ b/tests/boundary/gpu/codeObjects.test.ts @@ -1021,7 +1021,9 @@ it("preserves standalone PTX BOM and trailing NUL bytes in the retained text art const bytes = Buffer.from("\uFEFF" + ptxFixture + "\0"); const result = await inspect(bytes); if (!result.ok) throw result.error; - expect(Buffer.from(result.value.ptx_artifacts[0]!.text)).toEqual(bytes); + expect( + Buffer.compare(Buffer.from(result.value.ptx_artifacts[0]!.text), bytes), + ).toBe(0); expect(result.value.ptx_artifacts[0]!.byte_length).toBe(bytes.length); }); it("rejects a changed full-file identity before applying positional object selection", async () => { diff --git a/tests/boundary/il2cpp/il2cppBinaryFile.test.ts b/tests/boundary/il2cpp/il2cppBinaryFile.test.ts index 224121527d..cf918e9812 100644 --- a/tests/boundary/il2cpp/il2cppBinaryFile.test.ts +++ b/tests/boundary/il2cpp/il2cppBinaryFile.test.ts @@ -232,7 +232,7 @@ it.each(["thin", "universal"] as const)( expect(linked.return_types).toEqual( link(createIl2cppBinaryFixture()).return_types, ); - expect(bytes).toEqual(original); + expect(Buffer.compare(bytes, original)).toBe(0); }, ); @@ -387,7 +387,7 @@ it("reads a PE pointer array across file bytes and the declared zero-fill tail", const bytes = createSplitPointerPeFixture(); const original = Buffer.from(bytes); expect(link(bytes).method_addresses).toEqual(expectedAddresses); - expect(bytes).toEqual(original); + expect(Buffer.compare(bytes, original)).toBe(0); expect(failure(createSplitPointerPeFixture(false)).reason).toBe( "code-gen-modules-not-found", ); diff --git a/tests/boundary/il2cpp/il2cppMappingOwnership.test.ts b/tests/boundary/il2cpp/il2cppMappingOwnership.test.ts index 3688136b39..908b835a53 100644 --- a/tests/boundary/il2cpp/il2cppMappingOwnership.test.ts +++ b/tests/boundary/il2cpp/il2cppMappingOwnership.test.ts @@ -96,7 +96,7 @@ it("links one code-loaded type table among short runs across many mappings", () expect(result.types_field).toBeNull(); expect(result.types_count).toBe(4); expect(result.return_types.every((row) => row.name === "int")).toBe(true); - expect(bytes).toEqual(snapshot); + expect(Buffer.compare(bytes, snapshot)).toBe(0); }); it.each([183, 62])( @@ -110,7 +110,7 @@ it.each([183, 62])( ]); const snapshot = Buffer.from(split); expect(link(split)).toEqual(link(original)); - expect(split).toEqual(snapshot); + expect(Buffer.compare(split, snapshot)).toBe(0); }, ); @@ -136,7 +136,7 @@ it.each([false, true])( ), }), ); - expect(bytes).toEqual(snapshot); + expect(Buffer.compare(bytes, snapshot)).toBe(0); }, ); @@ -169,7 +169,7 @@ it("refuses an unresolved count relocation with conflicting file/zero-fill owner message: expect.stringContaining("ambiguous file and zero-fill mappings"), }), ); - expect(bytes).toEqual(snapshot); + expect(Buffer.compare(bytes, snapshot)).toBe(0); }); it("clears a file-backed unresolved count before scanning registration candidates", () => { @@ -184,7 +184,7 @@ it("clears a file-backed unresolved count before scanning registration candidate expect(() => link(bytes)).toThrow( expect.objectContaining({ reason: "code-registration-not-found" }), ); - expect(bytes).toEqual(snapshot); + expect(Buffer.compare(bytes, snapshot)).toBe(0); }); it.each([false, true])( diff --git a/tests/boundary/mcp/asarListingAdmissionMcp.test.ts b/tests/boundary/mcp/asarListingAdmissionMcp.test.ts index c8db05e921..19a5b07aab 100644 --- a/tests/boundary/mcp/asarListingAdmissionMcp.test.ts +++ b/tests/boundary/mcp/asarListingAdmissionMcp.test.ts @@ -39,7 +39,7 @@ it("reports expanded ASAR admission failure through MCP and permits a valid retr }, }); expect(result.structuredContent).toBeUndefined(); - expect(await readFile(path)).toEqual(bytes); + expect(Buffer.compare(await readFile(path), bytes)).toBe(0); const valid = join(root, "valid.asar"); await writeFile(valid, nestedAsarHeader(1, "lib", 1)); expect( diff --git a/tests/boundary/mcp/asarUnpackedExtractionMcp.test.ts b/tests/boundary/mcp/asarUnpackedExtractionMcp.test.ts index fdb8436cee..a671bc783f 100644 --- a/tests/boundary/mcp/asarUnpackedExtractionMcp.test.ts +++ b/tests/boundary/mcp/asarUnpackedExtractionMcp.test.ts @@ -68,7 +68,9 @@ it("extracts a rewritten unpacked companion only when the caller records the mis onTestFinished(() => rm(result.output_root, { recursive: true, force: true }), ); - expect(await readFile(join(result.output_root, addon))).toEqual(stripped); + expect( + Buffer.compare(await readFile(join(result.output_root, addon)), stripped), + ).toBe(0); expect(result.integrity_contradictions).toEqual([ { logical_path: addon, trust: "observed-untrusted" }, ]); diff --git a/tests/boundary/mcp/elfNotesMcp.test.ts b/tests/boundary/mcp/elfNotesMcp.test.ts index 6590dfdc00..63686bc91d 100644 --- a/tests/boundary/mcp/elfNotesMcp.test.ts +++ b/tests/boundary/mcp/elfNotesMcp.test.ts @@ -103,7 +103,7 @@ mcpTest( examined_tables: 1, examined_entries: 1, }); - expect(await readFile(path)).toEqual(bytes); + expect(Buffer.compare(await readFile(path), bytes)).toBe(0); } finally { await session.close(); } @@ -130,7 +130,7 @@ mcpTest( "Compressed ELF note sections are unsupported", ), }); - expect(await readFile(path)).toEqual(bytes); + expect(Buffer.compare(await readFile(path), bytes)).toBe(0); } finally { await session.close(); } diff --git a/tests/boundary/mcp/environmentBundleMcp.test.ts b/tests/boundary/mcp/environmentBundleMcp.test.ts index 5f8211e608..fd07fab79f 100644 --- a/tests/boundary/mcp/environmentBundleMcp.test.ts +++ b/tests/boundary/mcp/environmentBundleMcp.test.ts @@ -118,9 +118,13 @@ it("writes the bundle environment-build writes from retained capture Evidence", const files = await listFiles(input.output_directory); expect(files).toEqual(await listFiles(join(root, "reference"))); for (const path of files) - expect(await readFile(join(input.output_directory, path)), path).toEqual( - await readFile(join(root, "reference", path)), - ); + expect( + Buffer.compare( + await readFile(join(input.output_directory, path)), + await readFile(join(root, "reference", path)), + ), + path, + ).toBe(0); expect(report.coverage).toEqual(reference.value.coverage); }); @@ -154,8 +158,11 @@ it("copies a capture Evidence file byte for byte and plans without writing on a }); expect(built.isError).not.toBe(true); expect( - await readFile(join(root, "built/envs/grafana-example/evidence.json")), - ).toEqual(await readFile(path)); + Buffer.compare( + await readFile(join(root, "built/envs/grafana-example/evidence.json")), + await readFile(path), + ), + ).toBe(0); }); it("refuses an existing or parentless output directory and leaves it as it was", async () => { diff --git a/tests/boundary/mcp/ghidraReopen.test.ts b/tests/boundary/mcp/ghidraReopen.test.ts index f7c584ae0e..ac2dbcce7c 100644 --- a/tests/boundary/mcp/ghidraReopen.test.ts +++ b/tests/boundary/mcp/ghidraReopen.test.ts @@ -186,7 +186,7 @@ const connectGhidra = async () => { } } for (const launched of launcher.launches) await expectReleased(launched); - expect(await readFile(target)).toEqual(source); + expect(Buffer.compare(await readFile(target), source)).toBe(0); }; try { await server.connect(serverTransport); diff --git a/tests/boundary/mcp/goBinaryViews.test.ts b/tests/boundary/mcp/goBinaryViews.test.ts index a319b737bc..0788c62dd6 100644 --- a/tests/boundary/mcp/goBinaryViews.test.ts +++ b/tests/boundary/mcp/goBinaryViews.test.ts @@ -183,7 +183,7 @@ goViewTest( }, }, }); - expect(await readFile(path)).toEqual(bytes); + expect(Buffer.compare(await readFile(path), bytes)).toBe(0); await unlink(path); const source = { kind: "retained-evidence", diff --git a/tests/boundary/mcp/peLoadConfigMcp.test.ts b/tests/boundary/mcp/peLoadConfigMcp.test.ts index 8a347e9bd5..f749fbeebe 100644 --- a/tests/boundary/mcp/peLoadConfigMcp.test.ts +++ b/tests/boundary/mcp/peLoadConfigMcp.test.ts @@ -57,7 +57,7 @@ mcpTest( dynamic_value_reloc_table_section: PE_LOAD_CONFIG_FIXTURE.dynamicRelocSection, }); - expect(await readFile(path)).toEqual(bytes); + expect(Buffer.compare(await readFile(path), bytes)).toBe(0); const bad = await client.callTool( { name: "inspect_pe_load_config", diff --git a/tests/boundary/mcp/selectedSnapshotCleanupMcp.test.ts b/tests/boundary/mcp/selectedSnapshotCleanupMcp.test.ts index 7b654f7f76..54bdf34cbb 100644 --- a/tests/boundary/mcp/selectedSnapshotCleanupMcp.test.ts +++ b/tests/boundary/mcp/selectedSnapshotCleanupMcp.test.ts @@ -175,8 +175,8 @@ it("retains a selected snapshot descriptor through MCP cleanup discovery and sam expect(replayEvidence.evidence_id).not.toBe(originalEvidence.evidence_id); expect(starts).toBe(priorStarts); expect(calls).toEqual(priorCalls); - expect(await readFile(snapshotPath)).toEqual(snapshotBytes); - expect(await readFile(first)).toEqual(targetBytes); + expect(Buffer.compare(await readFile(snapshotPath), snapshotBytes)).toBe(0); + expect(Buffer.compare(await readFile(first), targetBytes)).toBe(0); } finally { restoreClose(); try { diff --git a/tests/boundary/mcp/sessionMcp.test.ts b/tests/boundary/mcp/sessionMcp.test.ts index 0cfb8bebed..2371e9186f 100644 --- a/tests/boundary/mcp/sessionMcp.test.ts +++ b/tests/boundary/mcp/sessionMcp.test.ts @@ -134,7 +134,7 @@ describe("MCP snapshot lifecycle ordering", () => { }) ).isError, ).toBe(true); - expect(await readFile(snapshotPath)).toEqual(bytes); + expect(Buffer.compare(await readFile(snapshotPath), bytes)).toBe(0); expect( ( await mcp.callTool({ diff --git a/tests/boundary/mcp/wasmArtifactMcp.test.ts b/tests/boundary/mcp/wasmArtifactMcp.test.ts index 238dbddf1a..9413177b72 100644 --- a/tests/boundary/mcp/wasmArtifactMcp.test.ts +++ b/tests/boundary/mcp/wasmArtifactMcp.test.ts @@ -161,9 +161,12 @@ it.each(["bin", "hop"])( }); await client.ping(); expect(createClient).not.toHaveBeenCalled(); - expect(await readFile(path)).toEqual( - Buffer.from([0, 0x61, 0x73, 0x6d, 1, 0, 0, 0]), - ); + expect( + Buffer.compare( + await readFile(path), + Buffer.from([0, 0x61, 0x73, 0x6d, 1, 0, 0, 0]), + ), + ).toBe(0); }, ); diff --git a/tests/boundary/mcp/webScriptSelectionMcp.test.ts b/tests/boundary/mcp/webScriptSelectionMcp.test.ts index f2ac5aa468..6bfbe233b0 100644 --- a/tests/boundary/mcp/webScriptSelectionMcp.test.ts +++ b/tests/boundary/mcp/webScriptSelectionMcp.test.ts @@ -189,10 +189,13 @@ it("publishes script bytes and retains the returned Evidence without starting an if (exported.analysis_input === null) throw new Error("published script bytes have no analysis input root"); expect( - await readFile( - join(exported.analysis_input.input_path, content.relative_path), + Buffer.compare( + await readFile( + join(exported.analysis_input.input_path, content.relative_path), + ), + bytes, ), - ).toEqual(bytes); + ).toBe(0); expect( JSON.parse(await readFile(exported.manifest.path, "utf8")), ).toMatchObject({ diff --git a/tests/boundary/pcap/pcapProvider.test.ts b/tests/boundary/pcap/pcapProvider.test.ts index e0cac88901..ca4e27afcb 100644 --- a/tests/boundary/pcap/pcapProvider.test.ts +++ b/tests/boundary/pcap/pcapProvider.test.ts @@ -310,7 +310,9 @@ it.each([ }, }); expect(producerLaunches).toBe(1); - expect(await readFile(path)).toEqual(Buffer.from("pcap-fixture-bytes")); + expect( + Buffer.compare(await readFile(path), Buffer.from("pcap-fixture-bytes")), + ).toBe(0); const snapshotPath = result.error.cleanupResources.find((resource) => resource.endsWith("/capture.pcap"), ); @@ -340,9 +342,12 @@ it.each([ }); expect(result.error.cleanupIncomplete).toBe(true); expect(cleanupAttempts).toBe(attemptsBeforeClose + 1); - await expect(readFile(snapshotPath)).resolves.toEqual( - Buffer.from("pcap-fixture-bytes"), - ); + expect( + Buffer.compare( + await readFile(snapshotPath), + Buffer.from("pcap-fixture-bytes"), + ), + ).toBe(0); } finally { denied.mockRestore(); } @@ -514,7 +519,7 @@ console.log(${JSON.stringify(TSHARK_JSON)});`, expect(projectAnalysisError(result.error)).toMatchObject({ code: "artifact_changed", }); - expect(await readFile(path)).toEqual(before); + expect(Buffer.compare(await readFile(path), before)).toBe(0); }); it("binds both tools to the snapshot when the original capture changes", async () => { diff --git a/tests/boundary/process/modelFixtureShutdown.test.ts b/tests/boundary/process/modelFixtureShutdown.test.ts index 28ab529e6d..2673022ed3 100644 --- a/tests/boundary/process/modelFixtureShutdown.test.ts +++ b/tests/boundary/process/modelFixtureShutdown.test.ts @@ -244,7 +244,7 @@ for (const protocol of protocols) expect(closed.cause).toBe(lateFailure); } else expect(closed).toEqual({ kind: "fixture-closed" }); expect(callbackFinished).toBe(true); - expect(await readFile(artifactPath)).toEqual(artifact); + expect(Buffer.compare(await readFile(artifactPath), artifact)).toBe(0); expect(fixture.failure).toBe( outcome === "late-error" ? String(lateFailure) : undefined, ); @@ -394,7 +394,7 @@ for (const protocol of protocols) await bounded(Promise.all(closing), "both admitted fixture closes"), ).toEqual([{ kind: "fixture-closed" }, { kind: "fixture-closed" }]); expect(callbackFinished).toBe(true); - expect(await readFile(artifactPath)).toEqual(artifact); + expect(Buffer.compare(await readFile(artifactPath), artifact)).toBe(0); expect(fixture.failure).toBe(ordinaryFailure); expect(server.listening).toBe(false); expect(server.address()).toBeNull(); diff --git a/tests/boundary/process/nativeVerifierCommandOwnership.test.ts b/tests/boundary/process/nativeVerifierCommandOwnership.test.ts index a11bce0f0a..fd1ffdc60b 100644 --- a/tests/boundary/process/nativeVerifierCommandOwnership.test.ts +++ b/tests/boundary/process/nativeVerifierCommandOwnership.test.ts @@ -211,7 +211,7 @@ it.skipIf(process.platform === "win32")( expect(report.failure.stderr).toBe("native verifier diagnostics\n"); expect(report.labExists).toBe(true); expect(report.artifactAtCompletion).toBe(artifact.toString("base64")); - expect(await readFile(artifactPath)).toEqual(artifact); + expect(Buffer.compare(await readFile(artifactPath), artifact)).toBe(0); expect(report.liveAtCompletion).toEqual([]); } finally { await writeFile( diff --git a/tests/boundary/process/nativeVerifierPreflightOwnership.test.ts b/tests/boundary/process/nativeVerifierPreflightOwnership.test.ts index 2aaee785df..22785b59ce 100644 --- a/tests/boundary/process/nativeVerifierPreflightOwnership.test.ts +++ b/tests/boundary/process/nativeVerifierPreflightOwnership.test.ts @@ -242,7 +242,9 @@ it.skipIf(process.platform === "win32").each(clients)( expect(receipt.artifacts).toBe(ready.lab); if (receipt.requests !== undefined) expect(receipt.requests).toEqual([]); // Read only after the verifier's own cleanup, before removing test scratch. - expect(await readFile(ready.artifact)).toEqual(retainedBytes); + expect( + Buffer.compare(await readFile(ready.artifact), retainedBytes), + ).toBe(0); } finally { // Self-release the authored orphan even on RED. PID observations never // authorize signals; outer cleanup retains its canonical acquisition proof. diff --git a/tests/boundary/process/packagePackIsolation.test.ts b/tests/boundary/process/packagePackIsolation.test.ts index 8d6875ae8a..9c3d95aa3c 100644 --- a/tests/boundary/process/packagePackIsolation.test.ts +++ b/tests/boundary/process/packagePackIsolation.test.ts @@ -83,6 +83,6 @@ process.stdout.write(JSON.stringify({ failure })); expect(JSON.parse(stdout)).toEqual({ failure: expectedFailure }); expect((await readdir(packageRoot)).sort()).toEqual(packageContentsBefore); - expect(await readFile(archivePath)).toEqual(sentinel); + expect(Buffer.compare(await readFile(archivePath), sentinel)).toBe(0); await expect(access(workspace)).rejects.toMatchObject({ code: "ENOENT" }); }); diff --git a/tests/boundary/process/packedBridgeVerification.test.ts b/tests/boundary/process/packedBridgeVerification.test.ts index 3dba30d08f..dc5aceb5bb 100644 --- a/tests/boundary/process/packedBridgeVerification.test.ts +++ b/tests/boundary/process/packedBridgeVerification.test.ts @@ -221,7 +221,9 @@ it.each(["valid", "invalid"] as const)( expectedPythonCommandFailure("SyntaxError"), ); expect(await readdir(root.path)).toEqual(["pack"]); - expect(await readFile(tarball)).toEqual(await readFile(input.tarball)); + expect( + Buffer.compare(await readFile(tarball), await readFile(input.tarball)), + ).toBe(0); }, [() => (commands === undefined ? root.close() : commands.close())]); await expect(access(root.path)).rejects.toMatchObject({ code: "ENOENT" }); }, diff --git a/tests/boundary/process/verifierCommandSemantics.test.ts b/tests/boundary/process/verifierCommandSemantics.test.ts index 683eb805e4..3624b93eb3 100644 --- a/tests/boundary/process/verifierCommandSemantics.test.ts +++ b/tests/boundary/process/verifierCommandSemantics.test.ts @@ -133,7 +133,7 @@ process.stdin.resume(); expect(output?.stderr).toBe("closed input diagnostics\n"); expect(commands.cleanupResources).toEqual([]); expect(existsSync(directory)).toBe(true); - expect(await readFile(artifact)).toEqual(bytes); + expect(Buffer.compare(await readFile(artifact), bytes)).toBe(0); }, ); @@ -183,7 +183,7 @@ it("reports a numeric nonzero exit with its complete output snapshot after clean }); expect(commands.cleanupResources).toEqual([]); expect(existsSync(directory)).toBe(true); - expect(await readFile(artifact)).toEqual(bytes); + expect(Buffer.compare(await readFile(artifact), bytes)).toBe(0); }); it("retries only the failed exact file capability after a peer releases, retaining both evidence files", async () => { @@ -259,8 +259,8 @@ it("retries only the failed exact file capability after a peer releases, retaini expect(peerHandle.fd).toBe(-1); expect(commands.cleanupResources).toEqual([]); expect(existsSync(directory)).toBe(true); - expect(await readFile(artifact)).toEqual(bytes); - expect(await readFile(peerArtifact)).toEqual(bytes); + expect(Buffer.compare(await readFile(artifact), bytes)).toBe(0); + expect(Buffer.compare(await readFile(peerArtifact), bytes)).toBe(0); }); it("refuses new owners and commands after successful close while leaving new resources with their caller", async () => { @@ -502,7 +502,7 @@ it.skipIf(process.platform === "win32")( ), ).toBe(false); expect(existsSync(directory)).toBe(true); - expect(await readFile(artifact)).toEqual(bytes); + expect(Buffer.compare(await readFile(artifact), bytes)).toBe(0); } finally { deny = false; release.resolve(); diff --git a/tests/boundary/providers/adb/adbHashCleanupConsumers.test.ts b/tests/boundary/providers/adb/adbHashCleanupConsumers.test.ts index c1547b38b0..b697d70f00 100644 --- a/tests/boundary/providers/adb/adbHashCleanupConsumers.test.ts +++ b/tests/boundary/providers/adb/adbHashCleanupConsumers.test.ts @@ -149,9 +149,12 @@ test.each(digestCases)( await f.provider.close(); expect(returnedError.cleanupIncomplete).toBe(false); expect(returnedError.cause).toBe(originalCause); - await expect(readFile(retainedPath(f.root))).resolves.toEqual( - Buffer.from("pulled bytes"), - ); + expect( + Buffer.compare( + await readFile(retainedPath(f.root)), + Buffer.from("pulled bytes"), + ), + ).toBe(0); }, ); diff --git a/tests/boundary/providers/adb/adbInputSnapshotOwnership.test.ts b/tests/boundary/providers/adb/adbInputSnapshotOwnership.test.ts index a084f5a187..e99702ff39 100644 --- a/tests/boundary/providers/adb/adbInputSnapshotOwnership.test.ts +++ b/tests/boundary/providers/adb/adbInputSnapshotOwnership.test.ts @@ -144,9 +144,9 @@ test("retains failed snapshot cleanup ownership and retries it on close", async sha256: f.digest, }, }); - await expect(readFile(f.snapshotPath())).resolves.toEqual( - await readFile(f.source), - ); + expect( + Buffer.compare(await readFile(f.snapshotPath()), await readFile(f.source)), + ).toBe(0); await f.provider.close(); await expect(lstat(dirname(f.snapshotPath()))).rejects.toMatchObject({ @@ -372,9 +372,10 @@ test.each([ const secondary = result.error.secondaryFailures[0]; expect(secondary).toBeInstanceOf(AnalysisOutputError); expect(secondary?.cause).toBeInstanceOf(AdbInputSnapshotFailure); - expect( - (secondary?.cause as AdbInputSnapshotFailure).cleanupCause, - ).toBeDefined(); + const snapshotFailure = secondary?.cause; + if (!(snapshotFailure instanceof AdbInputSnapshotFailure)) + throw new Error("Expected snapshot cleanup failure"); + expect(snapshotFailure.cleanupCause).toBeDefined(); }, ); @@ -464,7 +465,7 @@ test.each([ }), }, }); - await expect(readFile(localPath)).resolves.toEqual(pulledBytes); + expect(Buffer.compare(await readFile(localPath), pulledBytes)).toBe(0); const originatingError = result.error; await f.provider.close(); expect(closeAttempts).toBe(failClose ? 2 : 1); @@ -681,7 +682,12 @@ test.each([false, true])( dirname(f.snapshotPath()), ); else expect(result.error.cleanupIncomplete).toBe(false); - expect(await readFile(f.source)).toEqual(Buffer.from("immutable payload")); + expect( + Buffer.compare( + await readFile(f.source), + Buffer.from("immutable payload"), + ), + ).toBe(0); await f.provider.close(); }, ); diff --git a/tests/boundary/providers/adb/adbScreenPublicationCancellation.test.ts b/tests/boundary/providers/adb/adbScreenPublicationCancellation.test.ts index 492dde7be2..f8e018cb6e 100644 --- a/tests/boundary/providers/adb/adbScreenPublicationCancellation.test.ts +++ b/tests/boundary/providers/adb/adbScreenPublicationCancellation.test.ts @@ -124,7 +124,9 @@ test("retains a committed screen capture receipt when cancellation arrives after height: 1, }, }); - expect(await readFile(join(output, "screen.png"))).toEqual(f.png); + expect( + Buffer.compare(await readFile(join(output, "screen.png")), f.png), + ).toBe(0); await f.provider.close(); }); @@ -279,7 +281,7 @@ test("retains the committed receipt and cleanup observation when cancellation ra confidence: "observed", normalized_result: { local_path: destination, bytes: f.png.byteLength }, }); - expect(await readFile(destination)).toEqual(f.png); + expect(Buffer.compare(await readFile(destination), f.png)).toBe(0); await provider.close(); expect(closeAttempts).toBe(2); expect(result.error.cleanupIncomplete).toBe(false); @@ -300,7 +302,9 @@ test("publishes screen captures when the output filesystem does not support hard input: { serial: "device", output_directory: output }, }); expect(result.ok).toBe(true); - expect(await readFile(join(output, "screen.png"))).toEqual(f.png); + expect( + Buffer.compare(await readFile(join(output, "screen.png")), f.png), + ).toBe(0); await f.provider.close(); }); @@ -401,7 +405,7 @@ test("retains a receipt when cancellation arrives after exclusive output creatio confidence: "observed", normalized_result: { local_path: destination, bytes: f.png.byteLength }, }); - expect(await readFile(destination)).toEqual(f.png); + expect(Buffer.compare(await readFile(destination), f.png)).toBe(0); await f.provider.close(); }); @@ -458,7 +462,7 @@ test("closes and retains ownership when exclusive output stat fails", async () = expect(result.error.cleanupResources).toContain(destination); expect(closeAttempts).toBe(0); expect(handleOpen).toBe(true); - expect(await readFile(destination)).toEqual(Buffer.alloc(0)); + expect(Buffer.compare(await readFile(destination), Buffer.alloc(0))).toBe(0); vi.mocked(rm).mockImplementation(async (path, options) => { if (String(path) === destination && ++removeAttempts === 1) throw new Error("controlled rollback removal fault"); @@ -598,7 +602,7 @@ test.each([false, true])( }); expect(result.error.cleanupIncomplete).toBe(rootFails); if (rootFails) expect(result.error.cleanupResources).toEqual([runtimePath]); - expect(await readFile(f.destination)).toEqual(f.png); + expect(Buffer.compare(await readFile(f.destination), f.png)).toBe(0); expect(f.closeAttempts()).toBe(2); await f.provider.close(); expect(result.error.cleanupIncomplete).toBe(false); @@ -627,7 +631,7 @@ test("keeps a sticky publication close failure until provider close retries its confidence: "observed", normalized_result: { local_path: f.destination, bytes: f.png.byteLength }, }); - expect(await readFile(f.destination)).toEqual(f.png); + expect(Buffer.compare(await readFile(f.destination), f.png)).toBe(0); expect(f.closeAttempts()).toBe(2); const linkedFailure = (result.error.cause as AggregateError).errors[0] as { readonly cleanupIncomplete: boolean; @@ -638,7 +642,7 @@ test("keeps a sticky publication close failure until provider close retries its expect(f.closeAttempts()).toBe(3); expect(linkedFailure.cleanupIncomplete).toBe(false); expect(result.error.cleanupIncomplete).toBe(false); - expect(await readFile(f.destination)).toEqual(f.png); + expect(Buffer.compare(await readFile(f.destination), f.png)).toBe(0); }); test("resolves an admission cleanup error when the retained runtime-root lease closes", async () => { @@ -668,7 +672,7 @@ test("resolves an admission cleanup error when the retained runtime-root lease c expect(f.closeAttempts()).toBe(4); expect(admissionError.cleanupIncomplete).toBe(false); expect(admissionError.cause).toBeInstanceOf(AggregateError); - expect(await readFile(f.destination)).toEqual(f.png); + expect(Buffer.compare(await readFile(f.destination), f.png)).toBe(0); }); test("resolves the provider close aggregate after its failed root owner recovers", async () => { @@ -691,7 +695,7 @@ test("resolves the provider close aggregate after its failed root owner recovers expect(f.closeAttempts()).toBe(4); expect(closeError).toMatchObject({ cleanupIncomplete: false }); expect(result.error.cleanupIncomplete).toBe(false); - expect(await readFile(f.destination)).toEqual(f.png); + expect(Buffer.compare(await readFile(f.destination), f.png)).toBe(0); }); test("retains incomplete cleanup on a cancelled capture while keeping cancellation typed", async () => { diff --git a/tests/boundary/providers/ghidra/ghidraTargetSnapshot.test.ts b/tests/boundary/providers/ghidra/ghidraTargetSnapshot.test.ts index db7d3ec8f9..19e2574bae 100644 --- a/tests/boundary/providers/ghidra/ghidraTargetSnapshot.test.ts +++ b/tests/boundary/providers/ghidra/ghidraTargetSnapshot.test.ts @@ -19,11 +19,11 @@ describe("Ghidra target snapshot", () => { platform: "linux", }); expect(snapshot.admission).toBeUndefined(); - await expect(readFile(snapshot.path)).resolves.toEqual(bytes); + expect(Buffer.compare(await readFile(snapshot.path), bytes)).toBe(0); await expect( createGhidraTargetSnapshot(source, root, sha256, { platform: "linux" }), ).rejects.toMatchObject({ code: "EEXIST" }); - await expect(readFile(snapshot.path)).resolves.toEqual(bytes); + expect(Buffer.compare(await readFile(snapshot.path), bytes)).toBe(0); await expect( createGhidraTargetSnapshot(source, root, sha256, { platform: "win32" }), ).rejects.toThrow(/No native private runtime owns/u); diff --git a/tests/boundary/providers/hopper/bridgeLauncher.test.ts b/tests/boundary/providers/hopper/bridgeLauncher.test.ts index e72c7348dc..ac72a5b3a2 100644 --- a/tests/boundary/providers/hopper/bridgeLauncher.test.ts +++ b/tests/boundary/providers/hopper/bridgeLauncher.test.ts @@ -878,7 +878,7 @@ it("ordinary executable launches pass a verified private image with configured l expect(supervisor.snapshot().stdout.text).toBe(preparedPath); expect(launched.value.preparedImagePath).toBe(preparedPath); await writeFile(targetPath, "replacement bytes"); - expect(await readFile(preparedPath)).toEqual(selected); + expect(Buffer.compare(await readFile(preparedPath), selected)).toBe(0); } finally { await supervisor.stop(); await launched.value.releaseLease?.(); diff --git a/tests/boundary/providers/native/artifactProvider.extractionCompleteness.test.ts b/tests/boundary/providers/native/artifactProvider.extractionCompleteness.test.ts index 89696d48e0..aa0371d389 100644 --- a/tests/boundary/providers/native/artifactProvider.extractionCompleteness.test.ts +++ b/tests/boundary/providers/native/artifactProvider.extractionCompleteness.test.ts @@ -328,9 +328,12 @@ it("preserves every equal-content occurrence and copies a nested ASAR as its con extracted.artifacts[2]?.artifact_id, ); for (const path of ["a.txt", "b.txt", "app.asar"]) - expect(await readFile(join(output, path))).toEqual( - await readFile(join(source, path)), - ); + expect( + Buffer.compare( + await readFile(join(output, path)), + await readFile(join(source, path)), + ), + ).toBe(0); }); it("ignores unavailable members of a nested ASAR but refuses them as active entries", async () => { @@ -358,9 +361,12 @@ it("ignores unavailable members of a nested ASAR but refuses them as active entr expect( nestedExtraction.artifacts.map(({ relative_path }) => relative_path), ).toEqual(["app.asar"]); - expect(await readFile(join(nestedOutput, "app.asar"))).toEqual( - await readFile(archive), - ); + expect( + Buffer.compare( + await readFile(join(nestedOutput, "app.asar")), + await readFile(archive), + ), + ).toBe(0); const activeOutput = join(root, "active-output"); const activeResult = await extract(archive, activeOutput); diff --git a/tests/boundary/providers/native/artifactProvider.machOSlices.test.ts b/tests/boundary/providers/native/artifactProvider.machOSlices.test.ts index eedf1eb80f..4e210a3040 100644 --- a/tests/boundary/providers/native/artifactProvider.machOSlices.test.ts +++ b/tests/boundary/providers/native/artifactProvider.machOSlices.test.ts @@ -117,7 +117,7 @@ describe("artifact Mach-O slices", () => { const chunks: Buffer[] = []; const stream = await reader.open({ ...secondEntry }); for await (const chunk of stream) chunks.push(Buffer.from(chunk)); - expect(Buffer.concat(chunks)).toEqual(Buffer.from(arm)); + expect(Buffer.compare(Buffer.concat(chunks), Buffer.from(arm))).toBe(0); expect(reader.provenance()).toEqual([ expect.objectContaining({ tool: "lipo", effects: ["read"] }), ]); diff --git a/tests/boundary/providers/native/artifactProvider.zipCancellation.test.ts b/tests/boundary/providers/native/artifactProvider.zipCancellation.test.ts index 8e8a4d781a..8af0ae0ccb 100644 --- a/tests/boundary/providers/native/artifactProvider.zipCancellation.test.ts +++ b/tests/boundary/providers/native/artifactProvider.zipCancellation.test.ts @@ -102,9 +102,12 @@ it("preserves a real ZIP stream's caller cancellation through extraction rollbac containment_verified: true, artifacts: [expect.objectContaining({ relative_path: "member.bin" })], }); - expect(await readFile(join(controlOutput, "member.bin"))).toEqual( - Buffer.from(bytes), - ); + expect( + Buffer.compare( + await readFile(join(controlOutput, "member.bin")), + Buffer.from(bytes), + ), + ).toBe(0); expect(await client.close()).toMatchObject({ ok: true }); } finally { writeSpy.mockRestore(); diff --git a/tests/boundary/providers/native/nativePlistTextSource.test.ts b/tests/boundary/providers/native/nativePlistTextSource.test.ts index b16898cd5c..b077cc8f2a 100644 --- a/tests/boundary/providers/native/nativePlistTextSource.test.ts +++ b/tests/boundary/providers/native/nativePlistTextSource.test.ts @@ -212,5 +212,5 @@ it.for([ (item) => item.format === "json" && item.exitCode === 1, ), ); - expect(await readFile(path)).toEqual(fixture.bytes); + expect(Buffer.compare(await readFile(path), fixture.bytes)).toBe(0); }); diff --git a/tests/boundary/providers/native/nativeSignatureTargetBinding.test.ts b/tests/boundary/providers/native/nativeSignatureTargetBinding.test.ts index 8c05be76a5..83368d9479 100644 --- a/tests/boundary/providers/native/nativeSignatureTargetBinding.test.ts +++ b/tests/boundary/providers/native/nativeSignatureTargetBinding.test.ts @@ -414,7 +414,9 @@ it("does not classify an unrelated path-echo diagnostic as an unsigned observati snapshotPath = args.at(-1); if (snapshotPath === undefined) throw new Error("Expected the bound snapshot as the codesign operand"); - expect(await readFile(snapshotPath)).toEqual(Buffer.from(CONTENT)); + expect( + Buffer.compare(await readFile(snapshotPath), Buffer.from(CONTENT)), + ).toBe(0); return new NativeFixtureRunner( { codesign: `${snapshotPath}: No such file or directory\n` }, 1, @@ -475,7 +477,7 @@ it.skipIf(process.platform !== "darwin")( ok: false, error: { _tag: "AnalysisArtifactChangedError" }, }); - expect(await readFile(path)).toEqual(originalBytes); + expect(Buffer.compare(await readFile(path), originalBytes)).toBe(0); if (snapshotPath === undefined) throw new Error("Expected codesign capture"); await expect(lstat(dirname(snapshotPath))).rejects.toMatchObject({ @@ -569,7 +571,9 @@ it("copies and hashes the same bytes across multiple snapshot chunks", async () target, new ArtifactResourceScope(), async (binding) => { - expect(await readFile(binding.snapshotPath)).toEqual(bytes); + expect(Buffer.compare(await readFile(binding.snapshotPath), bytes)).toBe( + 0, + ); const verified = await verifySignatureTarget(target, binding); return verified.ok ? ok(signatureInspectionFixture()) : verified; }, @@ -658,9 +662,12 @@ it.skipIf(process.platform !== "darwin")( await rename(selected, saved); await rename(replacement, selected); try { - expect(await readFile(path)).not.toEqual( - await readFile(join(saved, "program")), - ); + expect( + Buffer.compare( + await readFile(path), + await readFile(join(saved, "program")), + ), + ).not.toBe(0); return await real.run(tool, args, options); } finally { await rename(selected, replacement); diff --git a/tests/boundary/setup/linuxHopperPublisherVerification.test.ts b/tests/boundary/setup/linuxHopperPublisherVerification.test.ts index d29cdfd69e..1f95aad8b0 100644 --- a/tests/boundary/setup/linuxHopperPublisherVerification.test.ts +++ b/tests/boundary/setup/linuxHopperPublisherVerification.test.ts @@ -525,7 +525,9 @@ linux( ok: false, error: { reason: "verification_unavailable" }, }); - expect(await readFile(join(f.cache, "release.json"))).toEqual(metadata); + expect( + Buffer.compare(await readFile(join(f.cache, "release.json")), metadata), + ).toBe(0); expect( (await readdir(f.publisher.cacheRoot)).some((name) => name.includes(".backup-"), diff --git a/tests/boundary/signals/recordingProvider.test.ts b/tests/boundary/signals/recordingProvider.test.ts index 4e6f26fed2..8757aefb8b 100644 --- a/tests/boundary/signals/recordingProvider.test.ts +++ b/tests/boundary/signals/recordingProvider.test.ts @@ -351,7 +351,7 @@ it.each([ expect(report.annotations_text).toBe(`${source.toString("hex")}\n`); expect(report.tool.arguments).toContain(`--input-file=./${recordingName}`); expect(report.tool.arguments).not.toContain("--input-file=-"); - expect(await readFile(f.path)).toEqual(source); + expect(Buffer.compare(await readFile(f.path), source)).toBe(0); expect((await readdir(f.directory)).sort()).toEqual( [recordingName, "producer.mjs"].sort(), ); diff --git a/tests/boundary/sqlite/sqliteDatabaseInspection.test.ts b/tests/boundary/sqlite/sqliteDatabaseInspection.test.ts index 8044a9ada4..44ce4ca8fa 100644 --- a/tests/boundary/sqlite/sqliteDatabaseInspection.test.ts +++ b/tests/boundary/sqlite/sqliteDatabaseInspection.test.ts @@ -769,6 +769,6 @@ it.each(["inventory", "selected_table", "database"] as const)( ), }), ); - expect(await readFile(path)).toEqual(before); + expect(Buffer.compare(await readFile(path), before)).toBe(0); }, ); diff --git a/tests/boundary/sqlite/sqliteDatabaseProvider.test.ts b/tests/boundary/sqlite/sqliteDatabaseProvider.test.ts index b39a3d2462..cd561c3c02 100644 --- a/tests/boundary/sqlite/sqliteDatabaseProvider.test.ts +++ b/tests/boundary/sqlite/sqliteDatabaseProvider.test.ts @@ -102,7 +102,7 @@ it("enforces a selected worker budget and completes the corrected same-database schema: { completeness: "complete" }, }); await expectPrivateRootRemoved(root); - expect(await readFile(database.path)).toEqual(original); + expect(Buffer.compare(await readFile(database.path), original)).toBe(0); }); nativeTest( @@ -179,7 +179,7 @@ nativeTest( ).toHaveLength(2); expect(result.error.cleanupResources).toEqual([]); await expectPrivateRootRemoved(root); - expect(await readFile(database.path)).toEqual(original); + expect(Buffer.compare(await readFile(database.path), original)).toBe(0); }, ); @@ -504,7 +504,7 @@ it.each(["late-cancellation", "cleanup-failure", "cancelled cleanup-failure"])( await provider.close(); await expectPrivateRootRemoved(root.path); } - expect(await readFile(database.path)).toEqual(original); + expect(Buffer.compare(await readFile(database.path), original)).toBe(0); }, ); @@ -586,7 +586,9 @@ it("inspects committed WAL schema and rows while preserving every source artifac }, }); for (const artifact of original) { - expect(await readFile(artifact.path)).toEqual(artifact.bytes); + expect(Buffer.compare(await readFile(artifact.path), artifact.bytes)).toBe( + 0, + ); const after = await lstat(artifact.path); expect({ ino: after.ino, @@ -901,7 +903,7 @@ it("attributes inventory selections beyond metadata bind capacity to result-colu }, }); expect(JSON.stringify(projected)).not.toContain("invalid_format"); - expect(await readFile(database.path)).toEqual(original); + expect(Buffer.compare(await readFile(database.path), original)).toBe(0); }); it("reports application-collation row failures as unavailable while retaining schema-only recovery", async () => { @@ -945,7 +947,7 @@ it("reports application-collation row failures as unavailable while retaining sc "Select columns whose values were read", ); } - expect(await readFile(path)).toEqual(original); + expect(Buffer.compare(await readFile(path), original)).toBe(0); }); it("rejects malformed databases without altering the selected file", async () => { @@ -961,7 +963,7 @@ it("rejects malformed databases without altering the selected file", async () => ok: false, error: { _tag: "AnalysisInputError" }, }); - expect(await readFile(path)).toEqual(bytes); + expect(Buffer.compare(await readFile(path), bytes)).toBe(0); }); nativeTest.runIf(process.platform !== "win32")( @@ -997,8 +999,8 @@ nativeTest( ok: false, error: { _tag: "AnalysisInputError" }, }); - expect(await readFile(database.path)).toEqual(main); - expect(await readFile(journalPath)).toEqual(journal); + expect(Buffer.compare(await readFile(database.path), main)).toBe(0); + expect(Buffer.compare(await readFile(journalPath), journal)).toBe(0); } finally { writer.exec("ROLLBACK"); writer.close(); @@ -1217,7 +1219,7 @@ it("retains an unavailable virtual module definition without executing it", asyn expect( await provider.inspect({ path, table: "optional_search" }), ).toMatchObject({ ok: false, error: { _tag: "AnalysisInputError" } }); - expect(await readFile(path)).toEqual(bytes); + expect(Buffer.compare(await readFile(path), bytes)).toBe(0); }); nativeTest( diff --git a/tests/boundary/sqlite/sqliteDatabaseSnapshot.test.ts b/tests/boundary/sqlite/sqliteDatabaseSnapshot.test.ts index 3687ffc72a..cbefb2b2c5 100644 --- a/tests/boundary/sqlite/sqliteDatabaseSnapshot.test.ts +++ b/tests/boundary/sqlite/sqliteDatabaseSnapshot.test.ts @@ -67,7 +67,7 @@ it.runIf(SQLITE_NATIVE_LIMITS_AVAILABLE).each(["PERSIST", "TRUNCATE"])( table: "records", }); expect(result.rows).toMatchObject({ returned_rows: 4, truncated: false }); - expect(await readFile(journalPath)).toEqual(before); + expect(Buffer.compare(await readFile(journalPath), before)).toBe(0); const afterStat = await lstat(journalPath, { bigint: true }); expect([ afterStat.ino, @@ -181,8 +181,8 @@ it("refuses an active journal even before SQLite writes its magic header", async reason: "format", message: expect.stringContaining("recovery"), }); - expect(await readFile(database.path)).toEqual(original); - expect(await readFile(journalPath)).toEqual(journal); + expect(Buffer.compare(await readFile(database.path), original)).toBe(0); + expect(Buffer.compare(await readFile(journalPath), journal)).toBe(0); } finally { writer.exec("ROLLBACK"); writer.close(); @@ -279,7 +279,9 @@ it("rejects a database changed after its copy but before WAL acquisition", async reason: "integrity", }); expect(changed).toBe(true); - expect(await readFile(`${root}/database.snapshot`)).toEqual(initial); + expect( + Buffer.compare(await readFile(`${root}/database.snapshot`), initial), + ).toBe(0); }); it.each(["-wal", "-journal"] as const)( diff --git a/tests/fixtures/machoArmSlices.ts b/tests/fixtures/machoArmSlices.ts index d22631faf6..a90d06c935 100644 --- a/tests/fixtures/machoArmSlices.ts +++ b/tests/fixtures/machoArmSlices.ts @@ -97,7 +97,7 @@ export const expectArmSliceExtraction = async ( for (const slice of fixture.slices) { const relativePath = `slices/${slice.name}`; const bytes = await readFile(join(extracted.output_root, relativePath)); - expect(bytes).toEqual(Buffer.from(slice.bytes)); + expect(Buffer.compare(bytes, Buffer.from(slice.bytes))).toBe(0); expect( extracted.artifacts.find( ({ relative_path }) => relative_path === relativePath, @@ -107,6 +107,6 @@ export const expectArmSliceExtraction = async ( bytes_written: slice.bytes.length, }); } - expect(await readFile(fixture.path)).toEqual(fixture.bytes); + expect(Buffer.compare(await readFile(fixture.path), fixture.bytes)).toBe(0); return extracted.manifest; }; diff --git a/tests/process-global/artifactFileSourceLifecycle.test.ts b/tests/process-global/artifactFileSourceLifecycle.test.ts index e06e680368..4f23fd690e 100644 --- a/tests/process-global/artifactFileSourceLifecycle.test.ts +++ b/tests/process-global/artifactFileSourceLifecycle.test.ts @@ -264,7 +264,7 @@ it.each(carriers)( await reader.close(); expect(close).not.toHaveBeenCalled(); expect(handle.fd).toBeGreaterThanOrEqual(0); - expect(await readFile(path)).toEqual(replacementBytes); + expect(Buffer.compare(await readFile(path), replacementBytes)).toBe(0); expect(control.handles.size).toBe(1); await handle.close(); expect(close).toHaveBeenCalledOnce(); diff --git a/tests/process-global/asarSnapshotOperation.test.ts b/tests/process-global/asarSnapshotOperation.test.ts index eaaee96865..02fb11b0ab 100644 --- a/tests/process-global/asarSnapshotOperation.test.ts +++ b/tests/process-global/asarSnapshotOperation.test.ts @@ -40,7 +40,9 @@ it("serializes concurrent ASAR snapshot preparation and cleans its single owned preparingSnapshot, ]); if (firstEntry.done) throw new Error("Expected an ASAR member"); - expect(await buffer(snapshot)).toEqual(await readFile(archive)); + expect( + Buffer.compare(await buffer(snapshot), await readFile(archive)), + ).toBe(0); const mismatch = reader.prepareContainer("0".repeat(64)); const matching = reader.prepareContainer(expectedDigest); diff --git a/tests/process-global/cutterDescriptorLifetime.test.ts b/tests/process-global/cutterDescriptorLifetime.test.ts index 86e4091e95..3e4e87d0f0 100644 --- a/tests/process-global/cutterDescriptorLifetime.test.ts +++ b/tests/process-global/cutterDescriptorLifetime.test.ts @@ -255,9 +255,10 @@ it("does not assign a peer's later cancellation to a descriptor that already fai error.cause.errors[0] instanceof AnalysisCancelledError, ); expect(cancelled).toHaveLength(1); - expect((cancelled[0]?.cause as AggregateError).errors[0].cause).toBe( - reason, - ); + const cancelledCause = cancelled[0]?.cause; + if (!(cancelledCause instanceof AggregateError)) + throw new Error("Expected descriptor cancellation failure"); + expect(cancelledCause.errors[0].cause).toBe(reason); expect(failure.cleanupIncomplete).toBe(true); fault.blocked.clear(); await client.close(); diff --git a/tests/process-global/ghidraImportSnapshotLifetime.test.ts b/tests/process-global/ghidraImportSnapshotLifetime.test.ts index 66160bab60..effa887bcd 100644 --- a/tests/process-global/ghidraImportSnapshotLifetime.test.ts +++ b/tests/process-global/ghidraImportSnapshotLifetime.test.ts @@ -381,7 +381,7 @@ it("copies every byte across chunk boundaries through the destination owner", as const snapshot = await createGhidraTargetSnapshot(path, runtime, sha256, { platform: "linux", }); - expect(await readFile(snapshot.path)).toEqual(bytes); + expect(Buffer.compare(await readFile(snapshot.path), bytes)).toBe(0); expect(snapshot.sha256).toBe(sha256); expect( [...fault.handles, ...fault.destinations].every( diff --git a/tests/process-global/mainStartupCancellation.test.ts b/tests/process-global/mainStartupCancellation.test.ts index d37d326626..e352250677 100644 --- a/tests/process-global/mainStartupCancellation.test.ts +++ b/tests/process-global/mainStartupCancellation.test.ts @@ -222,7 +222,9 @@ it("hands exact startup bytes to the installed SDK framing owner", async () => { owner.resume(); input.write(ping.subarray(11)); await expect.poll(() => messages.length).toBe(2); - expect(Buffer.concat(raw)).toEqual(Buffer.concat([request, ping])); + expect( + Buffer.compare(Buffer.concat(raw), Buffer.concat([request, ping])), + ).toBe(0); expect(messages[0]).toMatchObject({ params: { clientInfo: { name: "é🚀" } }, }); diff --git a/tests/process-global/regularFileNoFollowFallback.test.ts b/tests/process-global/regularFileNoFollowFallback.test.ts index 0e6e549bbd..c32d7ef4f1 100644 --- a/tests/process-global/regularFileNoFollowFallback.test.ts +++ b/tests/process-global/regularFileNoFollowFallback.test.ts @@ -50,7 +50,9 @@ it("rejects a symlink when O_NOFOLLOW is unavailable and still follows when requ symlinks: "follow", }); try { - await expect(readFile(followed)).resolves.toEqual(await readFile(target)); + expect( + Buffer.compare(await readFile(followed), await readFile(target)), + ).toBe(0); } finally { await followed.close(); } diff --git a/tests/process-global/setupConfigurationConflicts.test.ts b/tests/process-global/setupConfigurationConflicts.test.ts index a2e35e6b31..f480181362 100644 --- a/tests/process-global/setupConfigurationConflicts.test.ts +++ b/tests/process-global/setupConfigurationConflicts.test.ts @@ -111,7 +111,7 @@ it.each(["before", "after"] as const)( format: "json", }), ).toMatchObject({ status: "failed", reason: "conflict" }); - expect(await readFile(path)).toEqual(changed); + expect(Buffer.compare(await readFile(path), changed)).toBe(0); expect(await readFile(`${path}.rea.backup`, "utf8")).toBe(original); expect(await readdir(root)).toEqual([ "config.json",