71 lines
3.1 KiB
TypeScript
71 lines
3.1 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
|
|
import {
|
|
DUPLEX_LOSS_REASONS,
|
|
HTTP2_TELEMETRY_EVENT_NAMES,
|
|
mapHttp2EventToDuplexLossReason,
|
|
normalizeDuplexLossReason,
|
|
normalizeDuplexProvider,
|
|
type DuplexFallbackReason,
|
|
type DuplexTransportValue,
|
|
} from "./duplex-observability.js";
|
|
|
|
describe("duplex observability: HTTP/2 event mapping (accepted security fix 7)", () => {
|
|
it("test_http2_events_map_to_the_loss_taxonomy", () => {
|
|
// Every closed HTTP/2 event name maps to a value from the existing,
|
|
// closed DuplexLossReason set. The map reuses one taxonomy across every
|
|
// transport, so no separate HTTP/2-only reason list exists.
|
|
for (const event of HTTP2_TELEMETRY_EVENT_NAMES) {
|
|
const reason = mapHttp2EventToDuplexLossReason(event);
|
|
expect(DUPLEX_LOSS_REASONS).toContain(reason);
|
|
}
|
|
expect(mapHttp2EventToDuplexLossReason("session_error")).toBe("rpc_failure");
|
|
expect(mapHttp2EventToDuplexLossReason("session_goaway")).toBe("transport_closed");
|
|
expect(mapHttp2EventToDuplexLossReason("session_stall")).toBe("heartbeat_timeout");
|
|
expect(mapHttp2EventToDuplexLossReason("write_error")).toBe("write_error");
|
|
expect(mapHttp2EventToDuplexLossReason("transport_closed")).toBe("transport_closed");
|
|
expect(mapHttp2EventToDuplexLossReason("channel_exit")).toBe("provider_exit");
|
|
});
|
|
|
|
it("test_an_unknown_telemetry_input_maps_to_other", () => {
|
|
// An unknown event name, a raw provider error string, and a missing value
|
|
// all map to `other`. No raw text ever reaches a sink through this map.
|
|
expect(mapHttp2EventToDuplexLossReason("ECONNRESET: read failed at socket.js:42")).toBe("other");
|
|
expect(mapHttp2EventToDuplexLossReason("some_future_event")).toBe("other");
|
|
expect(mapHttp2EventToDuplexLossReason(null)).toBe("other");
|
|
expect(mapHttp2EventToDuplexLossReason(undefined)).toBe("other");
|
|
expect(mapHttp2EventToDuplexLossReason("")).toBe("other");
|
|
});
|
|
|
|
it("keeps the closed HTTP/2 event-name set to the seven named signals", () => {
|
|
expect([...HTTP2_TELEMETRY_EVENT_NAMES].sort()).toEqual(
|
|
[
|
|
"session_error",
|
|
"session_goaway",
|
|
"session_stall",
|
|
"write_error",
|
|
"transport_closed",
|
|
"channel_exit",
|
|
].sort(),
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("duplex observability: closed transport and fallback-reason values", () => {
|
|
it("accepts the http2 transport value and the preface_missing fallback reason", () => {
|
|
// A type-level check: these string literals must widen to the exported
|
|
// union types with no cast, so a drift in either union breaks the build.
|
|
const transport: DuplexTransportValue = "http2";
|
|
const fallbackReason: DuplexFallbackReason = "preface_missing";
|
|
expect(transport).toBe("http2");
|
|
expect(fallbackReason).toBe("preface_missing");
|
|
});
|
|
});
|
|
|
|
describe("duplex observability: existing normalization stays intact", () => {
|
|
it("still maps an unknown loss cause and an unknown provider key to their closed defaults", () => {
|
|
expect(normalizeDuplexLossReason("not_a_real_reason")).toBe("other");
|
|
expect(normalizeDuplexProvider("some-unlisted-plugin")).toBe("other");
|
|
});
|
|
});
|