import { beforeEach, describe, expect, test, vi } from "vitest"; import { createHookRequest, createHooksHandler, createResponse, } from "./server-http.test-harness.js"; const { readJsonBodyMock } = vi.hoisted(() => ({ readJsonBodyMock: vi.fn(), })); vi.mock("./hooks.js", async (importOriginal) => { const actual = await importOriginal(); return { ...actual, readJsonBody: readJsonBodyMock, }; }); describe("createHooksRequestHandler timeout status mapping", () => { beforeEach(() => { readJsonBodyMock.mockClear(); }); test("returns 408 for request body timeout", async () => { readJsonBodyMock.mockResolvedValue({ ok: false, error: "request body timeout" }); const dispatchWakeHook = vi.fn(); const dispatchAgentHook = vi.fn(() => "run-1"); const handler = createHooksHandler({ dispatchWakeHook, dispatchAgentHook }); const req = createHookRequest(); const { res, end } = createResponse(); const handled = await handler(req, res); expect(handled).toBe(true); expect(res.statusCode).toBe(408); expect(end).toHaveBeenCalledWith(JSON.stringify({ ok: false, error: "request body timeout" })); expect(dispatchWakeHook).not.toHaveBeenCalled(); expect(dispatchAgentHook).not.toHaveBeenCalled(); }); test("shares hook auth rate-limit bucket across ipv4 and ipv4-mapped ipv6 forms", async () => { const handler = createHooksHandler({ bindHost: "127.0.0.1" }); for (let i = 0; i < 20; i++) { const req = createHookRequest({ authorization: "Bearer wrong", remoteAddress: "1.2.3.4", }); const { res } = createResponse(); const handled = await handler(req, res); expect(handled).toBe(true); expect(res.statusCode).toBe(401); } const mappedReq = createHookRequest({ authorization: "Bearer wrong", remoteAddress: "::ffff:1.2.3.4", }); const { res: mappedRes, setHeader } = createResponse(); const handled = await handler(mappedReq, mappedRes); expect(handled).toBe(true); expect(mappedRes.statusCode).toBe(429); expect(setHeader).toHaveBeenCalledWith("Retry-After", expect.any(String)); }); test("uses trusted proxy forwarded client ip for hook auth throttling", async () => { const handler = createHooksHandler({ getClientIpConfig: () => ({ trustedProxies: ["10.0.0.1"] }), }); for (let i = 0; i < 20; i++) { const req = createHookRequest({ authorization: "Bearer wrong", remoteAddress: "10.0.0.1", headers: { "x-forwarded-for": "1.2.3.4" }, }); const { res } = createResponse(); const handled = await handler(req, res); expect(handled).toBe(true); expect(res.statusCode).toBe(401); } const forwardedReq = createHookRequest({ authorization: "Bearer wrong", remoteAddress: "10.0.0.1", headers: { "x-forwarded-for": "1.2.3.4, 10.0.0.1" }, }); const { res: forwardedRes, setHeader } = createResponse(); const handled = await handler(forwardedReq, forwardedRes); expect(handled).toBe(true); expect(forwardedRes.statusCode).toBe(429); expect(setHeader).toHaveBeenCalledWith("Retry-After", expect.any(String)); }); test.each(["0.0.0.0", "::"])( "does not throw when bindHost=%s while parsing non-hook request URL", async (bindHost) => { const handler = createHooksHandler({ bindHost }); const req = createHookRequest({ url: "/" }); const { res, end } = createResponse(); const handled = await handler(req, res); expect(handled).toBe(false); expect(end).not.toHaveBeenCalled(); }, ); });