mirror of
https://github.com/morluto/rea
synced 2026-10-26 14:25:49 -04:00
fix(tracing): retain caller reasons at traversal checkpoints
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { traceCallPath } from "./CallPathTracing.js";
|
||||
import { createAnalysisExecution } from "./AnalysisProvider.js";
|
||||
import {
|
||||
AnalysisCancelledError,
|
||||
AnalysisOutputError,
|
||||
} from "../domain/analysisErrorCore.js";
|
||||
import { err, ok } from "../domain/result.js";
|
||||
|
||||
const input = { start: "entry", direction: "forward" as const };
|
||||
const provider = { id: "fixture", name: "Fixture", version: "1" };
|
||||
|
||||
describe("call-path traversal cancellation ownership", () => {
|
||||
for (const phase of ["selection", "relationship"] as const)
|
||||
for (const reason of [null, { requested: "stop after provider reply" }])
|
||||
it(`preserves the exact reason at the ${phase} continuation (${reason === null ? "null" : "object"})`, async () => {
|
||||
const controller = new AbortController();
|
||||
const result = await traceCallPath(
|
||||
async () => {
|
||||
if (phase === "selection") controller.abort(reason);
|
||||
return ok("0x1000");
|
||||
},
|
||||
async () => {
|
||||
controller.abort(reason);
|
||||
return ok(createAnalysisExecution([], provider));
|
||||
},
|
||||
input,
|
||||
controller.signal,
|
||||
);
|
||||
expect(result.ok).toBe(false);
|
||||
if (result.ok) throw new Error("Expected cancellation");
|
||||
expect(result.error).toBeInstanceOf(AnalysisCancelledError);
|
||||
expect(result.error.cause).toBe(reason);
|
||||
});
|
||||
|
||||
it("preserves a selected provider failure when cancellation arrives with its reply", async () => {
|
||||
const controller = new AbortController();
|
||||
const failure = new AnalysisOutputError(
|
||||
"procedure_callees",
|
||||
"invalid fixture reply",
|
||||
);
|
||||
const result = await traceCallPath(
|
||||
async () => ok("0x1000"),
|
||||
async () => {
|
||||
controller.abort({ requested: "stop" });
|
||||
return err(failure);
|
||||
},
|
||||
input,
|
||||
controller.signal,
|
||||
);
|
||||
expect(result).toEqual(err(failure));
|
||||
});
|
||||
});
|
||||
@@ -98,14 +98,18 @@ export const traceCallPath = async (
|
||||
direction: input.direction,
|
||||
};
|
||||
if (isAborted(signal))
|
||||
return err(new AnalysisCancelledError("trace_call_path"));
|
||||
return err(
|
||||
new AnalysisCancelledError("trace_call_path", { cause: signal?.reason }),
|
||||
);
|
||||
const state = createTraceState(resolvedInput);
|
||||
while (
|
||||
state.queueIndex < state.queue.length &&
|
||||
state.traversalPath.length === 0
|
||||
) {
|
||||
if (isAborted(signal))
|
||||
return err(new AnalysisCancelledError("trace_call_path"));
|
||||
return err(
|
||||
new AnalysisCancelledError("trace_call_path", { cause: signal?.reason }),
|
||||
);
|
||||
const address = state.queue[state.queueIndex++];
|
||||
if (address === undefined) break;
|
||||
const current = state.visited.get(address);
|
||||
@@ -121,7 +125,9 @@ export const traceCallPath = async (
|
||||
if (expanded.value) break;
|
||||
}
|
||||
if (isAborted(signal))
|
||||
return err(new AnalysisCancelledError("trace_call_path"));
|
||||
return err(
|
||||
new AnalysisCancelledError("trace_call_path", { cause: signal?.reason }),
|
||||
);
|
||||
return ok(projectTrace(resolvedInput, state));
|
||||
};
|
||||
|
||||
@@ -155,7 +161,9 @@ const expandAddress = async (
|
||||
return result;
|
||||
}
|
||||
if (isAborted(signal))
|
||||
return err(new AnalysisCancelledError("trace_call_path"));
|
||||
return err(
|
||||
new AnalysisCancelledError("trace_call_path", { cause: signal?.reason }),
|
||||
);
|
||||
if (!result.ok) {
|
||||
state.failures.push({
|
||||
address,
|
||||
|
||||
Reference in New Issue
Block a user