

























@@ -34,6 +34,22 @@ beforeEach(() => {
3434resetDiagnosticEventsForTest();
3535});
363637+function headerNames(setHeader: ReturnType<typeof vi.fn>): string[] {
38+return setHeader.mock.calls
39+.map((call) => call[0])
40+.filter((name): name is string => typeof name === "string");
41+}
42+43+function expectHeaderNotSet(setHeader: ReturnType<typeof vi.fn>, name: string): void {
44+expect(headerNames(setHeader)).not.toContain(name);
45+}
46+47+function mockCallRecord(mock: ReturnType<typeof vi.fn>, index: number): unknown[] {
48+const call = mock.mock.calls[index];
49+expect(call).toBeTruthy();
50+return call ?? [];
51+}
52+3753describe("setDefaultSecurityHeaders", () => {
3854it("sets X-Content-Type-Options", () => {
3955const { res, setHeader } = makeMockHttpResponse();
@@ -70,19 +86,19 @@ describe("setDefaultSecurityHeaders", () => {
7086it("does not set Strict-Transport-Security when not provided", () => {
7187const { res, setHeader } = makeMockHttpResponse();
7288setDefaultSecurityHeaders(res);
73-expect(setHeader).not.toHaveBeenCalledWith("Strict-Transport-Security", expect.anything());
89+expectHeaderNotSet(setHeader, "Strict-Transport-Security");
7490});
75917692it("does not set Strict-Transport-Security for empty string", () => {
7793const { res, setHeader } = makeMockHttpResponse();
7894setDefaultSecurityHeaders(res, { strictTransportSecurity: "" });
79-expect(setHeader).not.toHaveBeenCalledWith("Strict-Transport-Security", expect.anything());
95+expectHeaderNotSet(setHeader, "Strict-Transport-Security");
8096});
81978298it("does not set Strict-Transport-Security when opts is omitted", () => {
8399const { res, setHeader } = makeMockHttpResponse();
84100setDefaultSecurityHeaders(res, undefined);
85-expect(setHeader).not.toHaveBeenCalledWith("Strict-Transport-Security", expect.anything());
101+expectHeaderNotSet(setHeader, "Strict-Transport-Security");
86102});
87103});
88104@@ -138,7 +154,7 @@ describe("sendRateLimited", () => {
138154const { res, setHeader, end } = makeMockHttpResponse();
139155sendRateLimited(res);
140156expect(res.statusCode).toBe(429);
141-expect(setHeader).not.toHaveBeenCalledWith("Retry-After", expect.anything());
157+expectHeaderNotSet(setHeader, "Retry-After");
142158expect(end).toHaveBeenCalledWith(
143159JSON.stringify({
144160error: {
@@ -153,14 +169,14 @@ describe("sendRateLimited", () => {
153169const { res, setHeader } = makeMockHttpResponse();
154170sendRateLimited(res, 0);
155171expect(res.statusCode).toBe(429);
156-expect(setHeader).not.toHaveBeenCalledWith("Retry-After", expect.anything());
172+expectHeaderNotSet(setHeader, "Retry-After");
157173});
158174159175it("responds with 429 and no Retry-After when retryAfterMs is negative", () => {
160176const { res, setHeader } = makeMockHttpResponse();
161177sendRateLimited(res, -500);
162178expect(res.statusCode).toBe(429);
163-expect(setHeader).not.toHaveBeenCalledWith("Retry-After", expect.anything());
179+expectHeaderNotSet(setHeader, "Retry-After");
164180});
165181166182it("sets Retry-After (seconds, ceiled) when retryAfterMs is positive", () => {
@@ -209,9 +225,10 @@ describe("readJsonBodyOrError", () => {
209225it("returns the parsed body on success", async () => {
210226readJsonBodyMock.mockResolvedValueOnce({ ok: true, value: { hello: "world" } });
211227const { res } = makeMockHttpResponse();
212-const result = await readJsonBodyOrError(makeRequest(), res, 1024);
228+const req = makeRequest();
229+const result = await readJsonBodyOrError(req, res, 1024);
213230expect(result).toEqual({ hello: "world" });
214-expect(readJsonBodyMock).toHaveBeenCalledWith(expect.anything(), 1024);
231+expect(readJsonBodyMock).toHaveBeenCalledWith(req, 1024);
215232});
216233217234it("responds with 413 when the body is too large", async () => {
@@ -229,16 +246,12 @@ describe("readJsonBodyOrError", () => {
229246error: { message: "Payload too large", type: "invalid_request_error" },
230247}),
231248);
232-expect(events).toContainEqual(
233-expect.objectContaining({
234-type: "payload.large",
235-surface: "gateway.http.json",
236-action: "rejected",
237-bytes: 2048,
238-limitBytes: 1024,
239-reason: "json_body_limit",
240-}),
241-);
249+const event = events.find((entry) => entry.type === "payload.large");
250+expect(event?.surface).toBe("gateway.http.json");
251+expect(event?.action).toBe("rejected");
252+expect(event?.bytes).toBe(2048);
253+expect(event?.limitBytes).toBe(1024);
254+expect(event?.reason).toBe("json_body_limit");
242255});
243256244257it("responds with 408 when the request body times out", async () => {
@@ -366,8 +379,12 @@ describe("watchClientDisconnect", () => {
366379const { req, res } = buildReqRes(reqSocket, resSocket);
367380const controller = new AbortController();
368381watchClientDisconnect(req, res, controller);
369-expect(reqOn).toHaveBeenCalledWith("close", expect.any(Function));
370-expect(resOn).toHaveBeenCalledWith("close", expect.any(Function));
382+const reqOnCall = mockCallRecord(reqOn, 0);
383+const resOnCall = mockCallRecord(resOn, 0);
384+expect(reqOnCall[0]).toBe("close");
385+expect(typeof reqOnCall[1]).toBe("function");
386+expect(resOnCall[0]).toBe("close");
387+expect(typeof resOnCall[1]).toBe("function");
371388});
372389373390it("cleanup detaches the close listener from each socket", () => {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。