163 lines
5.1 KiB
JavaScript
163 lines
5.1 KiB
JavaScript
import assert from "node:assert/strict";
|
||||
|
|
import { createHash } from "node:crypto";
|
|||
|
|
import { test } from "node:test";
|
|||
|
|
import { verifyNormalRecords } from "./http3-capture-assertions.mjs";
|
|||
|
|
|
|||
|
|
function connectionFactsFixture() {
|
|||
|
|
const body = Buffer.from([0, 255, 128, 1]);
|
|||
|
|
const view = (bytes) => ({
|
|||
|
|
base64: bytes.toString("base64"),
|
|||
|
|
bytes: bytes.length,
|
|||
|
|
sha256: createHash("sha256").update(bytes).digest("hex"),
|
|||
|
|
});
|
|||
|
|
const producer = {
|
|||
|
|
id: "flow-id",
|
|||
|
|
error: null,
|
|||
|
|
request: { http_version: "HTTP/3", content: view(body) },
|
|||
|
|
response: { http_version: "HTTP/3", content: view(body) },
|
|||
|
|
client_conn: {
|
|||
|
|
id: "client-id",
|
|||
|
|
transport_protocol: "udp",
|
|||
|
|
tls_version: "QUICv1",
|
|||
|
|
timestamp_tls_setup: 1_700_000_000.125678,
|
|||
|
|
error: null,
|
|||
|
|
},
|
|||
|
|
server_conn: {
|
|||
|
|
id: "server-id",
|
|||
|
|
transport_protocol: "udp",
|
|||
|
|
tls_version: "QUICv1",
|
|||
|
|
timestamp_tls_setup: 1_700_000_000.987654,
|
|||
|
|
error: null,
|
|||
|
|
},
|
|||
|
|
};
|
|||
|
|
const reported = structuredClone(producer);
|
|||
|
|
const requestFields = {
|
|||
|
|
method: "POST",
|
|||
|
|
scheme: "https",
|
|||
|
|
authority: "lab.test:18443",
|
|||
|
|
path: "/echo?case=readable",
|
|||
|
|
};
|
|||
|
|
Object.assign(reported.request, requestFields);
|
|||
|
|
const headers = [
|
|||
|
|
[Buffer.from("x-repeat"), Buffer.from("first-α")],
|
|||
|
|
[Buffer.from("x-repeat"), Buffer.from("second-β")],
|
|||
|
|
[Buffer.from("x-binary"), Buffer.from([255])],
|
|||
|
|
];
|
|||
|
|
for (const side of ["request", "response"])
|
|||
|
|
reported[side].headers = [
|
|||
|
|
["x-repeat", "first-α"],
|
|||
|
|
["x-repeat", "second-β"],
|
|||
|
|
["x-binary", null],
|
|||
|
|
];
|
|||
|
|
reported.client_conn.alpn = "h3";
|
|||
|
|
reported.server_conn.alpn = "h3";
|
|||
|
|
reported.response.status_code = 200;
|
|||
|
|
const binary_fields = [
|
|||
|
|
["/request/content", body],
|
|||
|
|
["/response/content", body],
|
|||
|
|
["/client_conn/alpn", Buffer.from("h3")],
|
|||
|
|
["/server_conn/alpn", Buffer.from("h3")],
|
|||
|
|
...Object.entries(requestFields).map(([name, value]) => [
|
|||
|
|
`/request/${name}`,
|
|||
|
|
Buffer.from(value),
|
|||
|
|
]),
|
|||
|
|
...["request", "response"].flatMap((side) =>
|
|||
|
|
headers.flatMap((pair, index) =>
|
|||
|
|
pair.map((value, part) => [`/${side}/headers/${index}/${part}`, value]),
|
|||
|
|
),
|
|||
|
|
),
|
|||
|
|
].map(([pointer, bytes]) => {
|
|||
|
|
const { base64, ...facts } = view(bytes);
|
|||
|
|
return {
|
|||
|
|
pointer,
|
|||
|
|
representation: "producer-bytes",
|
|||
|
|
state: "retained",
|
|||
|
|
content_base64: base64,
|
|||
|
|
...facts,
|
|||
|
|
};
|
|||
|
|
});
|
|||
|
|
return {
|
|||
|
|
normal: { records: [{ reported, binary_fields }] },
|
|||
|
|
manifest: {
|
|||
|
|
requests: {
|
|||
|
|
curl: {
|
|||
|
|
headers: [
|
|||
|
|
...Object.entries(requestFields).map(([name, value]) => [
|
|||
|
|
view(Buffer.from(`:${name}`)),
|
|||
|
|
view(Buffer.from(value)),
|
|||
|
|
]),
|
|||
|
|
...headers.map((pair) => pair.map(view)),
|
|||
|
|
],
|
|||
|
|
body: view(body),
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
responses: {
|
|||
|
|
curl: {
|
|||
|
|
headers: headers.map((pair) => pair.map(view)),
|
|||
|
|
body: view(body),
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
producer: [producer],
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
test("positive assertions retain exact producer connection facts on both legs", () => {
|
|||
|
|
const { normal, manifest, producer } = connectionFactsFixture();
|
|||
|
|
verifyNormalRecords(normal, manifest, producer);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
for (const [field, value] of [
|
|||
|
|
["method", "GET"],
|
|||
|
|
["scheme", "http"],
|
|||
|
|
["authority", "other.test:18443"],
|
|||
|
|
["path", "/different"],
|
|||
|
|
])
|
|||
|
|
test(`positive assertions reject changed readable request ${field}`, () => {
|
|||
|
|
const { normal, manifest, producer } = connectionFactsFixture();
|
|||
|
|
normal.records[0].reported.request[field] = value;
|
|||
|
|
assert.throws(() => verifyNormalRecords(normal, manifest, producer), {
|
|||
|
|
code: "ERR_ASSERTION",
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
for (const side of ["client_conn", "server_conn"])
|
|||
|
|
test(`positive assertions reject null readable ${side} ALPN`, () => {
|
|||
|
|
const { normal, manifest, producer } = connectionFactsFixture();
|
|||
|
|
normal.records[0].reported[side].alpn = null;
|
|||
|
|
assert.throws(() => verifyNormalRecords(normal, manifest, producer), {
|
|||
|
|
code: "ERR_ASSERTION",
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
for (const side of ["request", "response"])
|
|||
|
|
for (const [index, part, value] of [
|
|||
|
|
[0, 0, "x-changed"],
|
|||
|
|
[0, 1, "changed-first"],
|
|||
|
|
[1, 1, "changed-second"],
|
|||
|
|
[2, 1, "�"],
|
|||
|
|
])
|
|||
|
|
test(`positive assertions reject changed readable ${side} header ${index}/${part}`, () => {
|
|||
|
|
const { normal, manifest, producer } = connectionFactsFixture();
|
|||
|
|
normal.records[0].reported[side].headers[index][part] = value;
|
|||
|
|
assert.throws(() => verifyNormalRecords(normal, manifest, producer), {
|
|||
|
|
code: "ERR_ASSERTION",
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
for (const side of ["client_conn", "server_conn"])
|
|||
|
|
for (const [field, value] of [
|
|||
|
|
["tls_version", null],
|
|||
|
|
["tls_version", "changed-producer-label"],
|
|||
|
|
["error", "changed-connection-error"],
|
|||
|
|
["error", undefined],
|
|||
|
|
["timestamp_tls_setup", 0],
|
|||
|
|
["timestamp_tls_setup", 1_700_000_000],
|
|||
|
|
])
|
|||
|
|
test(`positive assertions reject ${side}.${field} changed to ${String(value)}`, () => {
|
|||
|
|
const { normal, manifest, producer } = connectionFactsFixture();
|
|||
|
|
normal.records[0].reported[side][field] = value;
|
|||
|
|
assert.throws(() => verifyNormalRecords(normal, manifest, producer), {
|
|||
|
|
code: "ERR_ASSERTION",
|
|||
|
|
});
|
|||
|
|
});
|