fix(evidence): retain authenticated parents on cancellation

This commit is contained in:
morluto
2026-10-27 12:31:33 +08:00
parent dcfb61826d
commit 7cbf375d01
2 changed files with 60 additions and 2 deletions
@@ -1478,6 +1478,53 @@ it.each(["file", "bundle"] as const)(
},
);
it.each(["file", "bundle"] as const)(
"preserves authenticated %s parent Evidence when cancellation arrives before projection",
async (kind) => {
const parent = analysisViewCaptureEvidence();
const path = join(
await createTestTempDirectory("rea-view-admission-"),
"source.json",
);
await writeFile(
path,
JSON.stringify(kind === "file" ? parent : createEvidenceBundle([parent])),
);
const controller = new AbortController();
const dispatch = queuedInterrupts.dispatchQueuedInterrupts;
const interrupt = vi
.spyOn(queuedInterrupts, "dispatchQueuedInterrupts")
.mockImplementation(async () => {
await dispatch();
controller.abort("after-source-admission");
});
try {
const result = await inspectAnalysisView(
{
source:
kind === "file"
? { kind: "evidence-file", path }
: { kind: "retained-evidence", evidence_id: parent.evidence_id },
view: manifestView,
},
undefined,
{
signal: controller.signal,
...(kind === "bundle" ? { evidenceBundlePath: path } : {}),
},
);
if (result.ok) throw new Error("Expected cancellation before projection");
expect(result.error).toMatchObject({
_tag: "AnalysisCancelledError",
cause: "after-source-admission",
partialObservation: parent,
});
} finally {
interrupt.mockRestore();
}
},
);
it.each(["file", "bundle"] as const)(
"preserves completed %s view Evidence when cancellation arrives after projection",
async (kind) => {
@@ -101,7 +101,11 @@ const inspectFileView = async (
// the signal notifications they enqueue before accepting either result.
await dispatchQueuedInterrupts();
// A request cancelled during authentication reports cancellation, even when authentication failed.
const admitted = cancelledFileViewResult(loaded, options.signal);
const admitted = cancelledFileViewResult(
loaded,
options.signal,
loaded.ok ? loaded.value : undefined,
);
if (!admitted.ok) return admitted;
const id = admitted.value.evidence_id;
const projected = inspectAnalysisViewValidated(
@@ -140,7 +144,14 @@ const inspectBundleView = async (
await dispatchQueuedInterrupts();
// Preserve an already-produced bundle failure before considering a later abort.
if (!loaded.ok) return loaded;
const admitted = cancelledFileViewResult(loaded, signal);
const parentId = input.source.evidence_id;
const admitted = cancelledFileViewResult(
loaded,
signal,
signal?.aborted
? loaded.value.records.find((record) => record.evidence_id === parentId)
: undefined,
);
if (!admitted.ok) return admitted;
const projected = inspectAnalysisViewValidated(
{ source: input.source, view: input.view },