mirror of
https://github.com/morluto/rea.git
synced 2026-11-09 16:26:36 +00:00
* test: focus configuration diagnostics on their owners Preserve shape errors, selected paths, recovery guidance, and unchanged-file behavior across parser, setup, and uninstall boundaries. * test: focus artifact and evidence checks on behavior Retain format, identity, malformed-input, and cleanup coverage while removing fixed prose and closing the admitted test source. * refactor(debugger): use configured session admission limits * test: focus evidence and diagnostics on behavior * fix(test): restore verifier export and formatting * test(config): verify debugger limit ranges * test(rizin): keep fixture executable authoritative * test(rizin): isolate debugger fixture environment
206 lines
6.9 KiB
TypeScript
206 lines
6.9 KiB
TypeScript
import { expect, it } from "vitest";
|
|
|
|
import type { AnalysisError } from "../domain/analysisErrorBase.js";
|
|
import {
|
|
AnalysisCancelledError,
|
|
AnalysisOutputError,
|
|
} from "../domain/analysisErrorCore.js";
|
|
import { BrowserObservationError } from "../domain/browserObservationError.js";
|
|
import { parseEvidence } from "../domain/evidence.js";
|
|
import { browserScenarioInputSchema } from "../domain/browserScenario.js";
|
|
import type {
|
|
BrowserScenarioCapture,
|
|
BrowserScenarioPartialObservation,
|
|
} from "../domain/browserScenarioCapture.js";
|
|
import { err, ok } from "../domain/result.js";
|
|
import { scriptScenarioFixture } from "../../tests/fixtures/webScriptCapture.js";
|
|
import type { BrowserScenarioCapturePort } from "./BrowserScenarioCapturePort.js";
|
|
import { captureBrowserScenario } from "./BrowserScenarioCaptureService.js";
|
|
import {
|
|
expectDroppedPartial,
|
|
expectKeptPartial,
|
|
expectOwnerError,
|
|
} from "../../tests/fixtures/ownerObservationExpectation.js";
|
|
|
|
const scenario = browserScenarioInputSchema.parse({
|
|
browser: { mode: "launch", executable_path: "/fixture/chrome" },
|
|
start_url: { url: "https://fixture.test/", query: [] },
|
|
actions: [{ step_id: "settle", action: "wait_for_timeout", duration_ms: 1 }],
|
|
});
|
|
const capture = scriptScenarioFixture();
|
|
const scenarioIdentity = {
|
|
id: "scenario-fixture",
|
|
name: "Scenario fixture",
|
|
version: "1",
|
|
};
|
|
const runScenario = (
|
|
failure: AnalysisError,
|
|
id = { id: "fixture", name: "fixture", version: "1" },
|
|
) =>
|
|
captureBrowserScenario(
|
|
{
|
|
identity: () => id,
|
|
captureScenario: async () => err(failure),
|
|
},
|
|
scenario,
|
|
);
|
|
const expectRetainedCapture = (
|
|
result: Awaited<ReturnType<typeof runScenario>>,
|
|
failure: AnalysisError,
|
|
) => {
|
|
if (result.ok) throw new Error("Expected the retained capture");
|
|
expect(result.error).toBe(failure);
|
|
expect(parseEvidence(result.error.partialObservation)).toMatchObject({
|
|
operation: "capture_browser_scenario",
|
|
normalized_result: capture,
|
|
});
|
|
};
|
|
|
|
it("converts a retained raw scenario capture into partial Evidence", async () => {
|
|
const failure = new AnalysisCancelledError("capture_browser_scenario", {
|
|
partialObservation: {
|
|
kind: "browser-scenario-observation",
|
|
capture,
|
|
},
|
|
});
|
|
expectRetainedCapture(await runScenario(failure, scenarioIdentity), failure);
|
|
});
|
|
|
|
it("converts a raw scenario capture retained after cleanup failure", async () => {
|
|
const failure = new BrowserObservationError(
|
|
"capture_browser_scenario",
|
|
"cleanup_failed",
|
|
{
|
|
cleanup: {
|
|
reason: "browser close failed",
|
|
resources: ["browser process"],
|
|
},
|
|
partialObservation: {
|
|
kind: "browser-scenario-observation",
|
|
capture,
|
|
},
|
|
},
|
|
);
|
|
const result = await runScenario(failure, scenarioIdentity);
|
|
expectRetainedCapture(result, failure);
|
|
if (!result.ok) expect(result.error.cleanupIncomplete).toBe(true);
|
|
});
|
|
|
|
it.each([
|
|
null,
|
|
{ request: "opaque cancellation" },
|
|
new BrowserObservationError("capture_browser_scenario", "protocol_error"),
|
|
])(
|
|
"preserves the exact pre-admission caller reason without dispatch: %j",
|
|
async (reason) => {
|
|
const caller = new AbortController();
|
|
caller.abort(reason);
|
|
let calls = 0;
|
|
const provider: BrowserScenarioCapturePort = {
|
|
identity: () => ({ id: "fixture", name: "fixture", version: "1" }),
|
|
captureScenario: async () => {
|
|
calls++;
|
|
throw new Error("Unexpected dispatch");
|
|
},
|
|
};
|
|
const result = await captureBrowserScenario(provider, scenario, {
|
|
signal: caller.signal,
|
|
});
|
|
if (result.ok) throw new Error("Expected cancellation");
|
|
expect(result.error).toBeInstanceOf(AnalysisCancelledError);
|
|
expect(result.error.cause).toBe(reason);
|
|
expect(calls).toBe(0);
|
|
},
|
|
);
|
|
|
|
it("retains completed scenario Evidence when cancellation arrives during capture", async () => {
|
|
const caller = new AbortController();
|
|
const reason = { requested: "stop after capture" };
|
|
const provider: BrowserScenarioCapturePort = {
|
|
identity: () => scenarioIdentity,
|
|
captureScenario: async (received, options) => {
|
|
expect(received).toBe(scenario);
|
|
expect(options?.signal).toBe(caller.signal);
|
|
caller.abort(reason);
|
|
return ok(capture);
|
|
},
|
|
};
|
|
const result = await captureBrowserScenario(provider, scenario, {
|
|
signal: caller.signal,
|
|
});
|
|
if (result.ok) throw new Error("Expected late cancellation");
|
|
expect(result.error).toBeInstanceOf(AnalysisCancelledError);
|
|
expect(result.error.cause).toBe(reason);
|
|
expect(parseEvidence(result.error.partialObservation)).toMatchObject({
|
|
operation: "capture_browser_scenario",
|
|
provider: scenarioIdentity,
|
|
confidence: "observed",
|
|
authority: "controlled-replay",
|
|
parameters: {
|
|
browser: scenario.browser,
|
|
start_url: scenario.start_url,
|
|
actions: scenario.actions,
|
|
},
|
|
normalized_result: capture,
|
|
});
|
|
});
|
|
|
|
it("keeps startup cancellation and its incomplete capture", async () => {
|
|
const reason = { requested: "stop during initial navigation" };
|
|
const completed = scriptScenarioFixture();
|
|
const {
|
|
completeness: _completeness,
|
|
steps: _steps,
|
|
browser,
|
|
...retained
|
|
} = completed;
|
|
const partial: BrowserScenarioPartialObservation = {
|
|
kind: "browser-scenario-observation",
|
|
capture: {
|
|
...retained,
|
|
browser: { ...browser, cleanup: "incomplete" },
|
|
steps: [],
|
|
},
|
|
};
|
|
const failure = new AnalysisCancelledError("capture_browser_scenario", {
|
|
cause: reason,
|
|
partialObservation: partial,
|
|
});
|
|
const result = await runScenario(failure);
|
|
expectKeptPartial(expectOwnerError(result, failure, reason), partial);
|
|
});
|
|
|
|
it.each([{ steps: [undefined] }, { started_at: Number.NaN }])(
|
|
"drops a non-JSON incomplete scenario capture and keeps cancellation primary",
|
|
async (capturePartial) => {
|
|
const reason = { requested: "stop during initial navigation" };
|
|
const partial: BrowserScenarioPartialObservation = {
|
|
kind: "browser-scenario-observation",
|
|
capture: capturePartial as never,
|
|
};
|
|
const failure = new AnalysisCancelledError("capture_browser_scenario", {
|
|
cause: reason,
|
|
partialObservation: partial,
|
|
});
|
|
const result = await runScenario(failure);
|
|
expectDroppedPartial(
|
|
expectOwnerError(result, failure, reason),
|
|
"Browser scenario provider retained a non-JSON incomplete observation.",
|
|
);
|
|
},
|
|
);
|
|
|
|
it("rejects a successful capture that is not a completed scenario", async () => {
|
|
const provider: BrowserScenarioCapturePort = {
|
|
identity: () => ({ id: "fixture", name: "fixture", version: "1" }),
|
|
captureScenario: async () => ok({} as BrowserScenarioCapture),
|
|
};
|
|
const result = await captureBrowserScenario(provider, scenario);
|
|
if (result.ok) throw new Error("Expected a completed-capture refusal");
|
|
expect(result.error).toBeInstanceOf(AnalysisOutputError);
|
|
expect(result.error.message).toContain(
|
|
"returned a malformed completed capture",
|
|
);
|
|
expect(result.error.partialObservation).toBeUndefined();
|
|
});
|