fix(browser): treat an unusable cache list as incomplete storage coverage (#2762)
Squash merge of https://github.com/morluto/rea/pull/2762. Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
2a78763e20
commit
c580ba940a
|
|
@ -0,0 +1,84 @@
|
|||
import { expect, it } from "vitest";
|
||||
|
||||
import { inspectWebPageInputSchema } from "../domain/browserObservation.js";
|
||||
import { captureStorage } from "./CdpCaptureStorage.js";
|
||||
import { CdpCommandRejection } from "./CdpCommandRejection.js";
|
||||
import type { CdpConnection } from "./CdpConnection.js";
|
||||
|
||||
const NO_CACHE_COMMAND = Symbol("unsupported");
|
||||
|
||||
const captureWithCacheResult = async (
|
||||
cacheResult: unknown,
|
||||
): Promise<Awaited<ReturnType<typeof captureStorage>>["value"]> => {
|
||||
const connection = {
|
||||
send(method: string): Promise<unknown> {
|
||||
switch (method) {
|
||||
case "Storage.getUsageAndQuota":
|
||||
return Promise.resolve({ usage: 1, quota: 10 });
|
||||
case "DOMStorage.getDOMStorageItems":
|
||||
return Promise.resolve({ entries: [] });
|
||||
case "IndexedDB.requestDatabaseNames":
|
||||
return Promise.resolve({ databaseNames: [] });
|
||||
case "Network.getCookies":
|
||||
return Promise.resolve({ cookies: [] });
|
||||
case "CacheStorage.requestCacheNames":
|
||||
return cacheResult === NO_CACHE_COMMAND
|
||||
? Promise.reject(
|
||||
new CdpCommandRejection(
|
||||
"inspect_web_page",
|
||||
method,
|
||||
-32601,
|
||||
"Method not found",
|
||||
),
|
||||
)
|
||||
: Promise.resolve(cacheResult);
|
||||
case "CacheStorage.requestEntries":
|
||||
return Promise.resolve({ cacheDataEntries: [], returnCount: 0 });
|
||||
default:
|
||||
return Promise.reject(new Error(`Unexpected command: ${method}`));
|
||||
}
|
||||
},
|
||||
} as unknown as CdpConnection;
|
||||
const { value } = await captureStorage(
|
||||
{
|
||||
connection,
|
||||
sessionId: undefined,
|
||||
input: inspectWebPageInputSchema.parse({
|
||||
cdp_endpoint: "http://127.0.0.1:9222",
|
||||
target_id: "fixture-page",
|
||||
include_storage_keys: true,
|
||||
include_storage_fingerprints: true,
|
||||
}),
|
||||
},
|
||||
"https://example.test/",
|
||||
[],
|
||||
);
|
||||
return value;
|
||||
};
|
||||
|
||||
it.each([
|
||||
["an observed empty cache list", { caches: [] }, true],
|
||||
["a response without a cache list", {}, false],
|
||||
["a non-array cache list", { caches: "nope" }, false],
|
||||
["a cache list with an unusable entry", { caches: [5] }, false],
|
||||
["an unsupported cache command", NO_CACHE_COMMAND, false],
|
||||
] as const)(
|
||||
"reports fingerprint completeness for %s",
|
||||
async (_label, cacheResult, complete) => {
|
||||
const value = await captureWithCacheResult(cacheResult);
|
||||
expect(value.cache_names).toEqual([]);
|
||||
expect(value.fingerprints_complete).toBe(complete);
|
||||
},
|
||||
);
|
||||
|
||||
it.each([false, true])(
|
||||
"preserves valid cache evidence beside an unusable entry: %s",
|
||||
async (mixed) => {
|
||||
const cache = { cacheName: "fixture-cache", cacheId: "fixture-cache-id" };
|
||||
const value = await captureWithCacheResult({
|
||||
caches: mixed ? [cache, 5] : [cache],
|
||||
});
|
||||
expect(value.cache_names).toEqual(["fixture-cache"]);
|
||||
expect(value.fingerprints_complete).toBe(!mixed);
|
||||
},
|
||||
);
|
||||
|
|
@ -60,8 +60,8 @@ export const captureStorage = async (
|
|||
limitations,
|
||||
)
|
||||
: undefined;
|
||||
const cacheValues = recordsValue(recordValue(cacheRaw)?.caches);
|
||||
const caches = capturedCaches(cacheValues);
|
||||
const cacheField = recordValue(cacheRaw)?.caches;
|
||||
const caches = capturedCaches(recordsValue(cacheField));
|
||||
const fingerprints = context.input.include_storage_fingerprints
|
||||
? await captureStorageFingerprints({
|
||||
context,
|
||||
|
|
@ -78,8 +78,10 @@ export const captureStorage = async (
|
|||
indexedRaw !== undefined &&
|
||||
Array.isArray(indexedValues) &&
|
||||
indexed.length === indexedValues.length &&
|
||||
cacheRaw !== undefined &&
|
||||
caches.length === cacheValues.length;
|
||||
// Like IndexedDB names, a missing, non-array or partly unusable cache
|
||||
// list is an incomplete enumeration rather than an empty one.
|
||||
Array.isArray(cacheField) &&
|
||||
caches.length === cacheField.length;
|
||||
return {
|
||||
value: {
|
||||
origin,
|
||||
|
|
|
|||
Loading…
Reference in New Issue