mirror of
https://github.com/morluto/rea.git
synced 2026-11-07 05:56:19 -10:00
fix(browser): retain evidence across cleanup errors
This commit is contained in:
1 parent
178ab26290
commit
8f894546c8
2 files changed
+81
-17
No files matched your search
@@ -11,6 +11,7 @@ import {
|
||||
AnalysisCancelledError,
|
||||
AnalysisOutputError,
|
||||
} from "../domain/analysisErrorCore.js";
|
||||
import { resolvedAnalysisError } from "../domain/analysisErrorBase.js";
|
||||
import { projectAnalysisError } from "../domain/analysisErrorProjection.js";
|
||||
import {
|
||||
expectDroppedPartial,
|
||||
@@ -150,6 +151,49 @@ it("converts a raw page inspection retained after browser cleanup failure", asyn
|
||||
}
|
||||
});
|
||||
|
||||
it.each([true, false])(
|
||||
"promotes cleanup primary facts once when wrapper retains them: %s",
|
||||
async (wrapperRetainsFacts) => {
|
||||
const inspection = passiveNetworkCapture();
|
||||
const primary = new AnalysisCancelledError("inspect_web_page", {
|
||||
partialObservation: inspection,
|
||||
});
|
||||
const failure = new BrowserObservationError(
|
||||
"inspect_web_page",
|
||||
"cleanup_failed",
|
||||
{
|
||||
cleanupPrimary: primary,
|
||||
cleanup: {
|
||||
reason: "target detach failed",
|
||||
resources: ["browser target"],
|
||||
},
|
||||
...(wrapperRetainsFacts ? { partialObservation: inspection } : {}),
|
||||
},
|
||||
);
|
||||
const browser = {
|
||||
...provider(async () => ok(report)),
|
||||
inspectPage: async () => err(failure),
|
||||
} as unknown as BrowserObservationPort;
|
||||
const result = await inspectWebPage(browser, pageInput());
|
||||
expect(result.ok).toBe(false);
|
||||
expect(parseEvidence(primary.partialObservation)).toMatchObject({
|
||||
operation: "inspect_web_page",
|
||||
normalized_result: inspection,
|
||||
});
|
||||
if (wrapperRetainsFacts)
|
||||
expect(failure.partialObservation).toBe(primary.partialObservation);
|
||||
failure.resolveCleanup();
|
||||
expect(resolvedAnalysisError(failure)).toBe(primary);
|
||||
expect(
|
||||
parseEvidence(
|
||||
projectAnalysisError(resolvedAnalysisError(failure)).details?.[
|
||||
"partial_observation"
|
||||
],
|
||||
),
|
||||
).toEqual(primary.partialObservation);
|
||||
},
|
||||
);
|
||||
|
||||
it("retains captured page Evidence when bundle analysis fails after capture", async () => {
|
||||
const inspection = passiveNetworkCapture();
|
||||
const failure = new BrowserObservationError(
|
||||
|
||||
@@ -288,44 +288,71 @@ const retainBrowserObservation = (
|
||||
provider: BrowserObservationPort,
|
||||
errorOperation: "inspect_web_page" | "analyze_web_bundle",
|
||||
): Result<Evidence, AnalysisError> => {
|
||||
const partial = error.partialObservation;
|
||||
const seen = new Set<AnalysisError>();
|
||||
const promoted = new Map<object, Evidence>();
|
||||
let current: AnalysisError | undefined = error;
|
||||
while (current !== undefined && !seen.has(current)) {
|
||||
seen.add(current);
|
||||
const partial = current.partialObservation;
|
||||
if (partial !== undefined) {
|
||||
const evidence =
|
||||
promoted.get(partial) ??
|
||||
promoteBrowserObservation(partial, input, provider, errorOperation);
|
||||
if (evidence !== undefined) {
|
||||
promoted.set(partial, evidence);
|
||||
current.partialObservation = evidence;
|
||||
}
|
||||
}
|
||||
current = current.cleanupPrimary;
|
||||
}
|
||||
return err(
|
||||
keepIncompleteObservation(
|
||||
error,
|
||||
errorOperation,
|
||||
NON_JSON_BROWSER_OBSERVATION,
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
/** Each cleanup owner can become the selected error after cleanup succeeds. */
|
||||
const promoteBrowserObservation = (
|
||||
partial: NonNullable<AnalysisError["partialObservation"]>,
|
||||
input: InspectWebPageInput | AnalyzeWebBundleInput,
|
||||
provider: BrowserObservationPort,
|
||||
errorOperation: "inspect_web_page" | "analyze_web_bundle",
|
||||
): Evidence | undefined => {
|
||||
if (
|
||||
errorOperation === "analyze_web_bundle" &&
|
||||
partial !== undefined &&
|
||||
"kind" in partial &&
|
||||
partial.kind === "web-bundle-acquisition"
|
||||
) {
|
||||
const acquisition =
|
||||
webBundleAcquisitionPartialObservationSchema.safeParse(partial);
|
||||
if (acquisition.success) {
|
||||
error.partialObservation = createBrowserEvidence(
|
||||
return createBrowserEvidence(
|
||||
"analyze_web_bundle",
|
||||
input,
|
||||
acquisition.data,
|
||||
provider.identity(),
|
||||
);
|
||||
return err(error);
|
||||
}
|
||||
}
|
||||
if (
|
||||
errorOperation === "analyze_web_bundle" &&
|
||||
partial !== undefined &&
|
||||
!("evidence_id" in partial) &&
|
||||
"observations" in partial &&
|
||||
"capture" in partial
|
||||
) {
|
||||
const bundle = webBundleAnalysisSchema.safeParse(partial);
|
||||
if (bundle.success) {
|
||||
error.partialObservation = createBrowserEvidence(
|
||||
return createBrowserEvidence(
|
||||
"analyze_web_bundle",
|
||||
input,
|
||||
bundle.data,
|
||||
provider.identity(),
|
||||
);
|
||||
return err(error);
|
||||
}
|
||||
} else if (
|
||||
partial !== undefined &&
|
||||
!("evidence_id" in partial) &&
|
||||
"target" in partial &&
|
||||
"network" in partial &&
|
||||
@@ -337,20 +364,13 @@ const retainBrowserObservation = (
|
||||
"fetch_source_maps" in input
|
||||
? (({ fetch_source_maps: _fetchSourceMaps, ...page }) => page)(input)
|
||||
: input;
|
||||
error.partialObservation = createBrowserEvidence(
|
||||
return createBrowserEvidence(
|
||||
"inspect_web_page",
|
||||
pageInput,
|
||||
inspection.data,
|
||||
provider.identity(),
|
||||
);
|
||||
return err(error);
|
||||
}
|
||||
}
|
||||
return err(
|
||||
keepIncompleteObservation(
|
||||
error,
|
||||
errorOperation,
|
||||
NON_JSON_BROWSER_OBSERVATION,
|
||||
),
|
||||
);
|
||||
return undefined;
|
||||
};
|
||||
Reference in new issue
Block a user