Compare commits

..
2 changed files with 65 additions and 13 deletions

No files matched your search

@@ -110,3 +110,47 @@ describe("web resource comparison identity and optional size", () => {
});
});
});
it.each([
[[128, 256], [128], [false, true], "unknown"],
[[128], [256], [true, true], "unknown"],
[[128], [128], [true, true], "unknown"],
[[128], [128, 256], [false, true], "changed"],
[[], [128], [false, true], "changed"],
[[128], [], [true, true], "unknown"],
[[128], [null], [false, true], "unknown"],
] as const)(
"uses directional resource coverage for %j vs %j (limited=%j): %s",
(leftSizes, rightSizes, [leftLimited, rightLimited], status) => {
const before = capture(leftSizes.map((size) => resource(size)));
const after = capture(rightSizes.map((size) => resource(size)));
if (leftLimited) before.completeness.truncated_sections.push("resources");
if (rightLimited) after.completeness.truncated_sections.push("resources");
for (const [left, right] of [
[before, after],
[after, before],
] as const)
expect(compareResources(left, right)).toMatchObject({
status,
total_changes: status === "changed" ? 1 : 0,
changes:
status === "changed"
? [
{
identity: expect.stringMatching(/^resg_/u),
change:
left.resources.length === 0
? "added"
: right.resources.length === 0
? "removed"
: "modified",
},
]
: [],
reason:
status === "changed"
? null
: expect.stringContaining("Resource inventory"),
});
},
);
+21 -13
View File
@@ -213,9 +213,8 @@ const compareWebCaptureDimensions = (
resources: compareResources(
before.resources,
after.resources,
sectionsComplete(before, ["resources"]) &&
sectionsComplete(after, ["resources"]),
"Resource inventory or content-size evidence was incomplete in at least one observation.",
sectionsComplete(before, ["resources"]),
sectionsComplete(after, ["resources"]),
),
network: networkDimension(before, after),
metadata: compareAggregate(
@@ -235,9 +234,12 @@ const compareWebCaptureDimensions = (
const compareResources = (
before: WebPageInspection["resources"],
after: WebPageInspection["resources"],
complete: boolean,
reason: string,
beforeComplete: boolean,
afterComplete: boolean,
): Dimension => {
const reason =
"Resource inventory or content-size evidence was incomplete in at least one observation.";
const complete = beforeComplete && afterComplete;
const left = resourceGroups(before);
const right = resourceGroups(after);
const changes: Change[] = [];
@@ -246,12 +248,14 @@ const compareResources = (
const leftSizes = left.get(identity);
const rightSizes = right.get(identity);
if (leftSizes === undefined || rightSizes === undefined) {
if (!complete) unresolved = true;
else
const absenceProven =
leftSizes === undefined ? beforeComplete : afterComplete;
if (absenceProven)
changes.push({
identity,
change: leftSizes === undefined ? "added" : "removed",
});
else unresolved = true;
continue;
}
// Stable inventories deduplicate identical observations. A null size also
@@ -261,7 +265,12 @@ const compareResources = (
unresolved = true;
continue;
}
if (!sameResourceSizes(leftSizes, rightSizes))
// A partial set can omit any size variant. Only a complete peer can
// contradict a retained variant, including when the other side is partial.
if (
(beforeComplete && !resourceSizesContained(rightSizes, leftSizes)) ||
(afterComplete && !resourceSizesContained(leftSizes, rightSizes))
)
changes.push({ identity, change: "modified" });
}
if (changes.length > 0)
@@ -292,11 +301,10 @@ const resourceGroups = (
return groups;
};
const sameResourceSizes = (
left: ReadonlySet<number | null>,
right: ReadonlySet<number | null>,
): boolean =>
left.size === right.size && [...left].every((size) => right.has(size));
const resourceSizesContained = (
observed: ReadonlySet<number | null>,
complete: ReadonlySet<number | null>,
): boolean => [...observed].every((size) => complete.has(size));
type BodyShapeSources = {
readonly request: boolean;