Compare commits

...
2 changed files with 73 additions and 12 deletions

No files matched your search

@@ -1,4 +1,8 @@
import { createHash } from "node:crypto";
import { Ajv2020 } from "ajv/dist/2020.js";
import { z } from "zod";
import { ANALYSIS_VIEW_TOOL_CONTRACTS } from "../../contracts/analysisViewToolContracts.js";
import { describe, expect, it } from "vitest";
@@ -79,6 +83,57 @@ const view = async (
return projected.value.normalized_result as Record<string, unknown>;
};
it("summarizes every three-digit status bucket within the advertised output contract", async () => {
const contract = ANALYSIS_VIEW_TOOL_CONTRACTS.find(
({ name }) => name === "inspect_analysis_view",
);
if (contract === undefined)
throw new Error("Missing inspect_analysis_view contract");
const validateOutput = new Ajv2020({
strict: false,
validateFormats: false,
inlineRefs: false,
code: { optimize: false },
}).compile(z.toJSONSchema(contract.outputSchema));
for (const [status, statusClass] of [
[100, "1xx"],
[200, "2xx"],
[300, "3xx"],
[400, "4xx"],
[599, "5xx"],
[600, "6xx"],
[700, "7xx"],
[800, "8xx"],
[999, "9xx"],
] as const) {
const capture = scenarioCaptureFixture([
{ method: "GET", url: "/status", status },
]);
const original = JSON.stringify(capture);
const result = await inspectAnalysisView({
source: { kind: "inline", evidence: scenarioCaptureEvidence(capture) },
view: { kind: "summary" },
});
if (!result.ok) throw result.error;
expect(result.value.normalized_result).toMatchObject({
summary: {
network: {
by_status_class: [{ status_class: statusClass, transactions: 1 }],
},
},
});
expect(contract.outputSchema.safeParse(result.value).success).toBe(true);
expect(
validateOutput(result.value),
JSON.stringify(validateOutput.errors),
).toBe(true);
expect(JSON.stringify(capture)).toBe(original);
expect(capture.events.items).toContainEqual(
expect.objectContaining({ kind: "response", status }),
);
}
});
describe("browser scenario analysis views", () => {
it("counts inherited Evidence limitations alongside capture-local caveats", async () => {
const capture = scenarioCaptureFixture();
@@ -4,15 +4,23 @@ import { isImmutableJsonSnapshot } from "../immutableJson.js";
import { compareAsciiStrings } from "../canonicalOrdering.js";
const count = z.int().nonnegative();
const statusClass = z.enum([
"1xx",
"2xx",
"3xx",
"4xx",
"5xx",
"failed",
"unknown",
]);
const statusClass = z
.enum([
"1xx",
"2xx",
"3xx",
"4xx",
"5xx",
"6xx",
"7xx",
"8xx",
"9xx",
"failed",
"unknown",
])
.describe(
"Numeric hundreds bucket of the recorded three-digit status. The 6xx through 9xx buckets retain nonstandard source values without assigning standard HTTP semantics.",
);
/** Counts concern retained transaction identities, including those without a request event. */
export const browserScenarioNetworkSummarySchema = z.strictObject({
@@ -107,9 +115,7 @@ export const browserScenarioTransactionGroups = (
: null,
status_class:
group.statuses.size === 1 && status !== undefined
? (`${String(Math.floor(status / 100))}xx` as z.infer<
typeof statusClass
>)
? statusClass.parse(`${String(Math.floor(status / 100))}xx`)
: group.statuses.size === 0 && group.failed
? "failed"
: "unknown",