mirror of
https://github.com/morluto/rea.git
synced 2026-11-07 05:56:19 -10:00
fix(evidence): share queued cancellation handling
This commit is contained in:
1 parent
0793b6c3ca
commit
3e8923c6eb
2 files changed
+39
-9
No files matched your search
@@ -11,7 +11,7 @@ import {
|
||||
analysisViewJavaScriptEvidence,
|
||||
analysisViewLayoutEvidence,
|
||||
} from "../../../tests/fixtures/analysisView.js";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { JAVASCRIPT_APPLICATION_EVIDENCE_EXAMPLE } from "../../contracts/javascript/javascriptRuntimeReconciliationExample.js";
|
||||
import {
|
||||
@@ -41,6 +41,7 @@ import {
|
||||
scenarioCaptureFixture,
|
||||
} from "../../../tests/fixtures/browserScenarioCapture.js";
|
||||
import { projectAnalysisError } from "../../domain/analysisErrorProjection.js";
|
||||
import * as evidenceFileSource from "./evidenceFileSource.js";
|
||||
|
||||
it.each(["inline", "retained-evidence", "invalid"] as const)(
|
||||
"refuses an already cancelled %s view before source admission",
|
||||
@@ -1222,6 +1223,37 @@ describe("inspectAnalysisView", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("prioritizes queued cancellation over a file authentication failure", async () => {
|
||||
const controller = new AbortController();
|
||||
const reason = Object.freeze({ requested: "cancel-after-authentication" });
|
||||
// Model an event callback which queues the caller's cancellation notification
|
||||
// as authentication finishes. The service must dispatch that notification
|
||||
// before accepting the authentication failure.
|
||||
const read = vi
|
||||
.spyOn(evidenceFileSource, "readEvidenceFileSource")
|
||||
.mockImplementationOnce(async () => {
|
||||
setImmediate(() => setImmediate(() => controller.abort(reason)));
|
||||
return err(new EvidenceIntegrityError("Invalid selected Evidence"));
|
||||
});
|
||||
try {
|
||||
const result = await inspectAnalysisView(
|
||||
{
|
||||
source: { kind: "evidence-file", path: "/selected-evidence.json" },
|
||||
view: manifestView,
|
||||
},
|
||||
undefined,
|
||||
{ signal: controller.signal },
|
||||
);
|
||||
expect(result).toMatchObject({
|
||||
ok: false,
|
||||
error: { _tag: "AnalysisCancelledError" },
|
||||
});
|
||||
expect(result.ok ? undefined : result.error.cause).toBe(reason);
|
||||
} finally {
|
||||
read.mockRestore();
|
||||
}
|
||||
});
|
||||
|
||||
describe("inspectAnalysisView scenario parents", () => {
|
||||
const view = manifestView;
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { parseAnalysisInput } from "../AnalysisInput.js";
|
||||
import { isNativeObservationOperation } from "../../domain/native/nativeObservation.js";
|
||||
import { setImmediate } from "node:timers/promises";
|
||||
import { dispatchQueuedInterrupts } from "../../process/queuedInterrupts.js";
|
||||
import { readImmutableEvidenceBundle } from "../EvidenceBundleFiles.js";
|
||||
import { INSPECT_ANALYSIS_VIEW_OPERATION } from "../../domain/analysisView/analysisViewErrors.js";
|
||||
@@ -96,13 +95,11 @@ const inspectFileView = async (
|
||||
options,
|
||||
input.source.evidence_id,
|
||||
);
|
||||
// Authentication and projection are synchronous; yield so a pending abort is dispatched before each check.
|
||||
const cancelled = async (): Promise<boolean> => {
|
||||
await setImmediate();
|
||||
return options.signal?.aborted === true;
|
||||
};
|
||||
// Authentication and projection are synchronous; dispatch queued callbacks and
|
||||
// the signal notifications they enqueue before accepting either result.
|
||||
await dispatchQueuedInterrupts();
|
||||
// A request cancelled during authentication reports cancellation, even when authentication failed.
|
||||
if (await cancelled())
|
||||
if (options.signal?.aborted)
|
||||
return err(
|
||||
new AnalysisCancelledError(INSPECT_ANALYSIS_VIEW_OPERATION, {
|
||||
cause: options.signal?.reason,
|
||||
@@ -114,8 +111,9 @@ const inspectFileView = async (
|
||||
{ ...input, source: { kind: "retained-evidence", evidence_id: id } },
|
||||
(evidenceId) => (evidenceId === id ? loaded.value : lookup?.(evidenceId)),
|
||||
);
|
||||
await dispatchQueuedInterrupts();
|
||||
// A request cancelled during projection delivers and records nothing.
|
||||
if (await cancelled())
|
||||
if (options.signal?.aborted)
|
||||
return err(
|
||||
new AnalysisCancelledError(INSPECT_ANALYSIS_VIEW_OPERATION, {
|
||||
cause: options.signal?.reason,
|
||||
|
||||
Reference in new issue
Block a user