mirror of
https://github.com/morluto/rea.git
synced 2026-10-26 17:35:11 +00:00
refactor(evidence): own bundle validation failures
This commit is contained in:
1 parent
e32a0494d4
commit
8b1e3143a2
6 files changed
+277
-76
No files matched your search
@@ -3,10 +3,10 @@ import { absoluteCallerPath } from "../filesystem/callerPath.js";
|
||||
|
||||
import { describeValidationFailure } from "../domain/validationFailure.js";
|
||||
import {
|
||||
describeEvidenceBundleFailure,
|
||||
parseImmutableEvidenceBundle,
|
||||
parseEvidenceBundle,
|
||||
safeParseImmutableEvidenceBundle,
|
||||
safeParseEvidenceBundle,
|
||||
type EvidenceBundle,
|
||||
type EvidenceBundleFailure,
|
||||
} from "../domain/evidenceBundle.js";
|
||||
import {
|
||||
EvidenceFileError,
|
||||
@@ -42,7 +42,7 @@ export const readEvidenceBundle = (
|
||||
resources?: ArtifactResourceScope,
|
||||
operation = "import_evidence_bundle",
|
||||
): Promise<Result<EvidenceBundle, EvidenceReadFailure>> =>
|
||||
readBundle(path, parseEvidenceBundle, signal, resources, operation);
|
||||
readBundle(path, safeParseEvidenceBundle, signal, resources, operation);
|
||||
|
||||
/** Read an authenticated immutable snapshot for a subsequent session import. */
|
||||
export const readImmutableEvidenceBundle = (
|
||||
@@ -51,11 +51,19 @@ export const readImmutableEvidenceBundle = (
|
||||
resources?: ArtifactResourceScope,
|
||||
operation = "import_evidence_bundle",
|
||||
): Promise<Result<EvidenceBundle, EvidenceReadFailure>> =>
|
||||
readBundle(path, parseImmutableEvidenceBundle, signal, resources, operation);
|
||||
readBundle(
|
||||
path,
|
||||
safeParseImmutableEvidenceBundle,
|
||||
signal,
|
||||
resources,
|
||||
operation,
|
||||
);
|
||||
|
||||
const readBundle = async (
|
||||
path: string,
|
||||
parseBundle: (input: unknown) => EvidenceBundle,
|
||||
parseBundle: (
|
||||
input: unknown,
|
||||
) => Result<EvidenceBundle, EvidenceBundleFailure>,
|
||||
signal?: AbortSignal,
|
||||
resources?: ArtifactResourceScope,
|
||||
operation = "import_evidence_bundle",
|
||||
@@ -81,26 +89,25 @@ const readBundle = async (
|
||||
"Insufficient heap headroom to authenticate the Evidence bundle",
|
||||
});
|
||||
if (!headroom.ok) return headroom;
|
||||
let bundle: EvidenceBundle;
|
||||
try {
|
||||
bundle = parseBundle(loaded.value);
|
||||
} catch (cause: unknown) {
|
||||
const failure = describeEvidenceBundleFailure(loaded.value, cause);
|
||||
const parsed = parseBundle(loaded.value);
|
||||
if (!parsed.ok) {
|
||||
const failure = parsed.error;
|
||||
if (failure.kind === "single-record")
|
||||
return err(
|
||||
new EvidenceFileError("read", "wrong-kind", {
|
||||
path: absoluteCallerPath(path),
|
||||
cause,
|
||||
cause: failure.cause,
|
||||
userMessage: failure.message,
|
||||
}),
|
||||
);
|
||||
return err(
|
||||
new EvidenceIntegrityError("Evidence bundle validation failed", {
|
||||
cause,
|
||||
cause: failure.cause,
|
||||
userMessage: failure.message,
|
||||
}),
|
||||
);
|
||||
}
|
||||
const bundle = parsed.value;
|
||||
for (const record of bundle.records) {
|
||||
if (record.predicate_type !== "rea.process-capture") continue;
|
||||
const parsedCapture = safeParseProcessCapture(record.normalized_result);
|
||||
@@ -136,18 +143,16 @@ export const writeEvidenceBundle = async (
|
||||
cause: signal.reason,
|
||||
}),
|
||||
);
|
||||
let checked: EvidenceBundle;
|
||||
try {
|
||||
checked = parseEvidenceBundle(bundle);
|
||||
} catch (cause: unknown) {
|
||||
const checked = safeParseEvidenceBundle(bundle);
|
||||
if (!checked.ok) {
|
||||
return err(
|
||||
new EvidenceIntegrityError("Evidence bundle validation failed", {
|
||||
cause,
|
||||
cause: checked.error.cause,
|
||||
}),
|
||||
);
|
||||
}
|
||||
return writeTextParts(
|
||||
bufferedJsonParts(canonicalJsonParts(checked)),
|
||||
bufferedJsonParts(canonicalJsonParts(checked.value)),
|
||||
path,
|
||||
overwrite,
|
||||
resources,
|
||||
|
||||
@@ -4,8 +4,7 @@ import { z } from "zod";
|
||||
import {
|
||||
createEvidenceBundle,
|
||||
createImmutableEvidenceBundle,
|
||||
describeEvidenceBundleFailure,
|
||||
parseEvidenceBundle,
|
||||
safeParseEvidenceBundle,
|
||||
validateResidualUnknownAddition,
|
||||
type EvidenceBundle,
|
||||
} from "../../domain/evidenceBundle.js";
|
||||
@@ -75,25 +74,24 @@ export class EvidenceLedger {
|
||||
|
||||
/** Validate an entire bundle before atomically merging its records. */
|
||||
import(input: unknown): ImportResult {
|
||||
let bundle: EvidenceBundle;
|
||||
try {
|
||||
bundle = parseEvidenceBundle(input);
|
||||
} catch (cause: unknown) {
|
||||
const failure = describeEvidenceBundleFailure(input, cause);
|
||||
if (failure.kind === "single-record")
|
||||
const parsed = safeParseEvidenceBundle(input);
|
||||
if (!parsed.ok) {
|
||||
const { kind, message, cause } = parsed.error;
|
||||
if (kind === "single-record")
|
||||
return err(
|
||||
new AnalysisInputError("import_evidence_bundle", {
|
||||
cause,
|
||||
userMessage: failure.message,
|
||||
userMessage: message,
|
||||
}),
|
||||
);
|
||||
return err(
|
||||
new EvidenceIntegrityError("Evidence bundle validation failed", {
|
||||
cause,
|
||||
userMessage: failure.message,
|
||||
userMessage: message,
|
||||
}),
|
||||
);
|
||||
}
|
||||
const bundle = parsed.value;
|
||||
// A record-only bundle cannot change the already-valid unknown graph. The
|
||||
// bundle parser has validated every incoming record and its manifests, so
|
||||
// checking ID conflicts and committing only the additions preserves the
|
||||
|
||||
@@ -6,9 +6,9 @@ import {
|
||||
createEvidenceBundle,
|
||||
createImmutableEvidenceBundle,
|
||||
NonCanonicalEvidenceBundleError,
|
||||
describeEvidenceBundleFailure,
|
||||
evidenceBundleForTarget,
|
||||
parseEvidenceBundle,
|
||||
safeParseEvidenceBundle,
|
||||
} from "./evidenceBundle.js";
|
||||
import {
|
||||
createResidualUnknown,
|
||||
@@ -30,12 +30,14 @@ describe("evidence bundle failure classification", () => {
|
||||
}),
|
||||
);
|
||||
const canonical = createEvidenceBundle(records);
|
||||
expect(() =>
|
||||
parseEvidenceBundle({
|
||||
...canonical,
|
||||
records: [...canonical.records].reverse(),
|
||||
}),
|
||||
).toThrow(NonCanonicalEvidenceBundleError);
|
||||
const parsed = safeParseEvidenceBundle({
|
||||
...canonical,
|
||||
records: [...canonical.records].reverse(),
|
||||
});
|
||||
expect(parsed.ok).toBe(false);
|
||||
if (parsed.ok) return;
|
||||
expect(parsed.error.kind).toBe("invalid-bundle");
|
||||
expect(parsed.error.cause).toBeInstanceOf(NonCanonicalEvidenceBundleError);
|
||||
});
|
||||
|
||||
it("treats only authenticated standalone Evidence as the wrong input kind", () => {
|
||||
@@ -44,14 +46,18 @@ describe("evidence bundle failure classification", () => {
|
||||
parameters: {},
|
||||
result: { observed: true },
|
||||
});
|
||||
const single = describeEvidenceBundleFailure(record, new Error("bundle"));
|
||||
expect(single).toMatchObject({ kind: "single-record" });
|
||||
const single = safeParseEvidenceBundle(record);
|
||||
expect(single.ok).toBe(false);
|
||||
if (single.ok) return;
|
||||
expect(single.error.kind).toBe("single-record");
|
||||
|
||||
const tampered = describeEvidenceBundleFailure(
|
||||
{ ...record, normalized_result: { observed: false } },
|
||||
new Error("bundle"),
|
||||
);
|
||||
expect(tampered).toMatchObject({ kind: "invalid-bundle" });
|
||||
const tampered = safeParseEvidenceBundle({
|
||||
...record,
|
||||
normalized_result: { observed: false },
|
||||
});
|
||||
expect(tampered.ok).toBe(false);
|
||||
if (tampered.ok) return;
|
||||
expect(tampered.error.kind).toBe("invalid-bundle");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -238,27 +238,35 @@ const referencedEvidenceIds = (unknown: ResidualUnknown): readonly string[] => [
|
||||
...(unknown.resolution?.evidence_ids ?? []),
|
||||
];
|
||||
|
||||
/**
|
||||
* Name the bundle constraint that failed and how to recover. A single
|
||||
* Evidence record, such as one command result, is not a bundle.
|
||||
*/
|
||||
export type EvidenceBundleFailureDescription =
|
||||
| { readonly kind: "single-record"; readonly message: string }
|
||||
| { readonly kind: "invalid-bundle"; readonly message: string };
|
||||
type EvidenceBundleValidationFailure =
|
||||
| z.ZodError
|
||||
| DuplicateEvidenceRecordError
|
||||
| InvalidResidualUnknownGraphError
|
||||
| NonCanonicalEvidenceBundleError;
|
||||
|
||||
export const describeEvidenceBundleFailure = (
|
||||
/** Expected source rejection, including authenticated standalone Evidence. */
|
||||
export type EvidenceBundleFailure = {
|
||||
readonly kind: "single-record" | "invalid-bundle";
|
||||
readonly message: string;
|
||||
readonly cause: EvidenceBundleValidationFailure;
|
||||
};
|
||||
|
||||
const describeEvidenceBundleFailure = (
|
||||
input: unknown,
|
||||
cause: unknown,
|
||||
): EvidenceBundleFailureDescription => {
|
||||
cause: EvidenceBundleValidationFailure,
|
||||
): EvidenceBundleFailure => {
|
||||
try {
|
||||
const record = parseEvidence(input);
|
||||
return {
|
||||
kind: "single-record",
|
||||
cause,
|
||||
message: `Expected an Evidence bundle with records and manifest arrays, but this JSON is one Evidence record (${record.evidence_id}). Export a session bundle with the MCP export_evidence_bundle tool and supply that file.`,
|
||||
};
|
||||
} catch {
|
||||
} catch (recordFailure: unknown) {
|
||||
if (!(recordFailure instanceof z.ZodError)) throw recordFailure;
|
||||
return {
|
||||
kind: "invalid-bundle",
|
||||
cause,
|
||||
message:
|
||||
cause instanceof z.ZodError
|
||||
? `Evidence bundle does not match the bundle schema ${describeValidationFailure(cause)}. Supply an unmodified bundle written by export_evidence_bundle or rea evidence-export.`
|
||||
@@ -272,12 +280,19 @@ export const parseEvidenceBundle = (input: unknown): EvidenceBundle => {
|
||||
return parseBundle(input, false);
|
||||
};
|
||||
|
||||
/** Authenticate and seal file-owned payloads once for downstream zero-copy reuse. */
|
||||
export const parseImmutableEvidenceBundle = (
|
||||
/** Authenticate and seal snapshot-owned payloads, throwing validation failures. */
|
||||
export const parseImmutableEvidenceBundle = (input: unknown): EvidenceBundle =>
|
||||
parseBundle(input, true);
|
||||
|
||||
/** Read expected source failures as facts; implementation failures still throw. */
|
||||
export const safeParseEvidenceBundle = (
|
||||
input: unknown,
|
||||
): EvidenceBundle => {
|
||||
return parseBundle(input, true);
|
||||
};
|
||||
): Result<EvidenceBundle, EvidenceBundleFailure> => readBundle(input, false);
|
||||
|
||||
/** Authenticate and seal file-owned payloads once for downstream zero-copy reuse. */
|
||||
export const safeParseImmutableEvidenceBundle = (
|
||||
input: unknown,
|
||||
): Result<EvidenceBundle, EvidenceBundleFailure> => readBundle(input, true);
|
||||
|
||||
const parseBundle = (input: unknown, seal: boolean): EvidenceBundle => {
|
||||
if (typeof input === "object" && input !== null) {
|
||||
@@ -291,8 +306,25 @@ const parseBundle = (input: unknown, seal: boolean): EvidenceBundle => {
|
||||
export const readEvidenceBundle = (
|
||||
input: unknown,
|
||||
): Result<EvidenceBundle, EvidenceIntegrityError> => {
|
||||
const parsed = safeParseEvidenceBundle(input);
|
||||
if (parsed.ok) return parsed;
|
||||
const { cause } = parsed.error;
|
||||
return err(
|
||||
new EvidenceIntegrityError(cause.message, {
|
||||
cause,
|
||||
userMessage: cause.message,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
const readBundle = (
|
||||
input: unknown,
|
||||
seal: boolean,
|
||||
): Result<EvidenceBundle, EvidenceBundleFailure> => {
|
||||
try {
|
||||
return ok(parseEvidenceBundle(input));
|
||||
return ok(
|
||||
seal ? parseImmutableEvidenceBundle(input) : parseEvidenceBundle(input),
|
||||
);
|
||||
} catch (cause: unknown) {
|
||||
if (
|
||||
!(cause instanceof z.ZodError) &&
|
||||
@@ -301,12 +333,7 @@ export const readEvidenceBundle = (
|
||||
!(cause instanceof NonCanonicalEvidenceBundleError)
|
||||
)
|
||||
throw cause;
|
||||
return err(
|
||||
new EvidenceIntegrityError(cause.message, {
|
||||
cause,
|
||||
userMessage: cause.message,
|
||||
}),
|
||||
);
|
||||
return err(describeEvidenceBundleFailure(input, cause));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,20 +1,48 @@
|
||||
import { writeFile } from "node:fs/promises";
|
||||
import { readdir, writeFile } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
|
||||
import { Client, InMemoryTransport } from "@modelcontextprotocol/client";
|
||||
import { expect, it, onTestFinished, vi } from "vitest";
|
||||
import { z } from "zod";
|
||||
|
||||
import { createCli } from "../../src/cli.js";
|
||||
import { compareEvidenceBundlesCommand } from "../../src/application/EvidenceBundleCommands.js";
|
||||
import {
|
||||
readEvidenceBundle,
|
||||
readImmutableEvidenceBundle,
|
||||
writeEvidenceBundle,
|
||||
} from "../../src/application/EvidenceBundleFiles.js";
|
||||
import { ArtifactResourceScope } from "../../src/artifacts/ArtifactResourceScope.js";
|
||||
import { bundleComparisonResultSchema } from "../../src/domain/bundleComparison.js";
|
||||
import { processEvidence } from "../../src/application/ReconstructionObligationLedger.fixture.js";
|
||||
import { processCaptureSchema } from "../../src/domain/process/processCapture.js";
|
||||
import {
|
||||
createEvidenceBundle,
|
||||
serializeEvidenceBundle,
|
||||
} from "../../src/domain/evidenceBundle.js";
|
||||
import * as evidenceBundleDomain from "../../src/domain/evidenceBundle.js";
|
||||
import * as evidenceDomain from "../../src/domain/evidence.js";
|
||||
import { createServer } from "../../src/server/createServer.js";
|
||||
import { createTestBinarySession } from "../fixtures/binarySession.js";
|
||||
import { createTestTempDirectory } from "../fixtures/temporaryDirectory.js";
|
||||
|
||||
const unexpectedBundleFailures = [
|
||||
new TypeError("bundle authentication TypeError"),
|
||||
new Error("bundle authentication Error"),
|
||||
] as const;
|
||||
|
||||
const spyOnBundleParse = (failure: Error): void => {
|
||||
const parser = vi
|
||||
.spyOn(evidenceBundleDomain.evidenceBundleSchema, "parse")
|
||||
.mockImplementation(() => {
|
||||
throw failure;
|
||||
});
|
||||
onTestFinished(() => parser.mockRestore());
|
||||
};
|
||||
|
||||
const validBundleFile = async (directory: string, name = "bundle.json") => {
|
||||
const path = join(directory, name);
|
||||
const bundle = evidenceBundleDomain.createEvidenceBundle([processEvidence()]);
|
||||
await writeFile(path, evidenceBundleDomain.serializeEvidenceBundle(bundle));
|
||||
return { bundle, path };
|
||||
};
|
||||
|
||||
it.each([
|
||||
new TypeError("capture validation implementation failed"),
|
||||
new Error("capture validator failed"),
|
||||
@@ -30,7 +58,9 @@ it.each([
|
||||
const path = join(directory, "bundle.json");
|
||||
await writeFile(
|
||||
path,
|
||||
serializeEvidenceBundle(createEvidenceBundle([processEvidence()])),
|
||||
evidenceBundleDomain.serializeEvidenceBundle(
|
||||
evidenceBundleDomain.createEvidenceBundle([processEvidence()]),
|
||||
),
|
||||
);
|
||||
const validator = vi
|
||||
.spyOn(processCaptureSchema, "safeParse")
|
||||
@@ -45,6 +75,136 @@ it.each([
|
||||
},
|
||||
);
|
||||
|
||||
it.each(unexpectedBundleFailures)(
|
||||
"preserves unexpected bundle authentication failures in mutable and immutable readers: $name",
|
||||
async (failure) => {
|
||||
const directory = await createTestTempDirectory(
|
||||
"rea-bundle-reader-failure-",
|
||||
);
|
||||
const { path } = await validBundleFile(directory);
|
||||
spyOnBundleParse(failure);
|
||||
|
||||
await expect(readEvidenceBundle(path)).rejects.toBe(failure);
|
||||
await expect(readImmutableEvidenceBundle(path)).rejects.toBe(failure);
|
||||
await expect(
|
||||
compareEvidenceBundlesCommand({ leftPath: path, rightPath: path }),
|
||||
).rejects.toBe(failure);
|
||||
},
|
||||
);
|
||||
|
||||
it.each(unexpectedBundleFailures)(
|
||||
"does not publish output or retain resources when bundle authentication throws: $name",
|
||||
async (failure) => {
|
||||
const directory = await createTestTempDirectory(
|
||||
"rea-bundle-write-failure-",
|
||||
);
|
||||
const { bundle } = await validBundleFile(directory, "input.json");
|
||||
const outputPath = join(directory, "output.json");
|
||||
const resources = new ArtifactResourceScope();
|
||||
onTestFinished(async () => {
|
||||
await resources.close();
|
||||
});
|
||||
spyOnBundleParse(failure);
|
||||
|
||||
await expect(
|
||||
writeEvidenceBundle(bundle, outputPath, false, resources),
|
||||
).rejects.toBe(failure);
|
||||
expect(await readdir(directory)).toEqual(["input.json"]);
|
||||
expect(resources.hasPending).toBe(false);
|
||||
await resources.close();
|
||||
},
|
||||
);
|
||||
|
||||
it.each(unexpectedBundleFailures)(
|
||||
"preserves unexpected bundle authentication failures through SDK import: $name",
|
||||
async (failure) => {
|
||||
const directory = await createTestTempDirectory(
|
||||
"rea-bundle-import-failure-",
|
||||
);
|
||||
const { path } = await validBundleFile(directory);
|
||||
const session = createTestBinarySession(() => {
|
||||
throw new Error("Evidence import must not launch a provider");
|
||||
});
|
||||
const server = createServer({ kind: "session", session });
|
||||
const client = new Client({ name: "bundle-import-failure", version: "1" });
|
||||
const [clientTransport, serverTransport] =
|
||||
InMemoryTransport.createLinkedPair();
|
||||
spyOnBundleParse(failure);
|
||||
try {
|
||||
await server.connect(serverTransport);
|
||||
await client.connect(clientTransport);
|
||||
const response = await client.callTool({
|
||||
name: "import_evidence_bundle",
|
||||
arguments: { path },
|
||||
});
|
||||
expect(response).toMatchObject({
|
||||
isError: true,
|
||||
content: [{ type: "text", text: failure.message }],
|
||||
});
|
||||
expect(response.structuredContent).toBeUndefined();
|
||||
expect(session.exportEvidenceBundle().records).toEqual([]);
|
||||
expect(session.exportEvidenceBundle().unknowns).toEqual([]);
|
||||
} finally {
|
||||
await Promise.allSettled([
|
||||
client.close(),
|
||||
server.close(),
|
||||
session.close(),
|
||||
]);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
it.each(unexpectedBundleFailures)(
|
||||
"preserves the session ledger when bundle authentication throws: $name",
|
||||
async (failure) => {
|
||||
const directory = await createTestTempDirectory(
|
||||
"rea-ledger-import-failure-",
|
||||
);
|
||||
const { bundle } = await validBundleFile(directory);
|
||||
const existing = evidenceDomain.createEvidence(
|
||||
undefined,
|
||||
{ id: "fixture", name: "Fixture", version: "1" },
|
||||
{
|
||||
operation: "observe",
|
||||
parameters: {},
|
||||
result: { retained: true },
|
||||
},
|
||||
);
|
||||
const session = createTestBinarySession(() => {
|
||||
throw new Error("Evidence import must not launch a provider");
|
||||
});
|
||||
onTestFinished(async () => {
|
||||
await session.close();
|
||||
});
|
||||
expect(session.recordEvidence(existing).ok).toBe(true);
|
||||
const before = session.exportEvidenceBundle();
|
||||
spyOnBundleParse(failure);
|
||||
|
||||
expect(() => session.importEvidenceBundle(bundle)).toThrow(failure);
|
||||
expect(session.exportEvidenceBundle()).toEqual(before);
|
||||
},
|
||||
);
|
||||
|
||||
it.each(unexpectedBundleFailures)(
|
||||
"propagates unexpected standalone-record classification failures: $name",
|
||||
async (failure) => {
|
||||
const directory = await createTestTempDirectory(
|
||||
"rea-bundle-classifier-failure-",
|
||||
);
|
||||
const path = join(directory, "record.json");
|
||||
const record = processEvidence();
|
||||
await writeFile(path, JSON.stringify(record));
|
||||
const parser = vi
|
||||
.spyOn(evidenceDomain, "parseEvidence")
|
||||
.mockImplementation(() => {
|
||||
throw failure;
|
||||
});
|
||||
onTestFinished(() => parser.mockRestore());
|
||||
|
||||
await expect(readImmutableEvidenceBundle(path)).rejects.toBe(failure);
|
||||
},
|
||||
);
|
||||
|
||||
it.each([
|
||||
new TypeError("comparison output invariant failed"),
|
||||
new RangeError("comparison computation failed"),
|
||||
@@ -58,7 +218,12 @@ it.each([
|
||||
"rea-bundle-compute-failure-",
|
||||
);
|
||||
const path = join(directory, "bundle.json");
|
||||
await writeFile(path, serializeEvidenceBundle(createEvidenceBundle([])));
|
||||
await writeFile(
|
||||
path,
|
||||
evidenceBundleDomain.serializeEvidenceBundle(
|
||||
evidenceBundleDomain.createEvidenceBundle([]),
|
||||
),
|
||||
);
|
||||
// Reach output validation through the actual command, reader, authentication
|
||||
// and comparison. This fault is generated output, not the caller's bundle.
|
||||
const output = vi
|
||||
|
||||
@@ -25,7 +25,7 @@ import { join } from "node:path";
|
||||
import { afterEach, expect, it, vi } from "vitest";
|
||||
import {
|
||||
createImmutableEvidenceBundle,
|
||||
parseImmutableEvidenceBundle,
|
||||
safeParseImmutableEvidenceBundle,
|
||||
} from "../../src/domain/evidenceBundle.js";
|
||||
import { ArtifactResourceScope } from "../../src/artifacts/ArtifactResourceScope.js";
|
||||
import { deliverAnalysisEvidence } from "../../src/application/EvidenceBundleCommands.js";
|
||||
@@ -190,7 +190,7 @@ it("admits bundle authentication before cloning and hashing retained records", a
|
||||
availableBytes: 0,
|
||||
}),
|
||||
);
|
||||
vi.mocked(parseImmutableEvidenceBundle).mockClear();
|
||||
vi.mocked(safeParseImmutableEvidenceBundle).mockClear();
|
||||
const resources = new ArtifactResourceScope();
|
||||
try {
|
||||
expect(
|
||||
@@ -199,7 +199,7 @@ it("admits bundle authentication before cloning and hashing retained records", a
|
||||
ok: false,
|
||||
error: { _tag: "AnalysisResourceConstraintError", resource: "memory" },
|
||||
});
|
||||
expect(parseImmutableEvidenceBundle).not.toHaveBeenCalled();
|
||||
expect(safeParseImmutableEvidenceBundle).not.toHaveBeenCalled();
|
||||
} finally {
|
||||
await resources.close();
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user