From 268e03617246245fdaa12a06cf71d92519586639 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Thu, 12 Mar 2026 21:44:58 +0000 Subject: [PATCH] refactor(test): share hook request handler fixtures --- .../server-http.hooks-request-timeout.test.ts | 90 ++++--------------- src/gateway/server-http.test-harness.ts | 38 +++++++- 2 files changed, 49 insertions(+), 79 deletions(-) diff --git a/src/gateway/server-http.hooks-request-timeout.test.ts b/src/gateway/server-http.hooks-request-timeout.test.ts index e56432a95..4a8c1ec34 100644 --- a/src/gateway/server-http.hooks-request-timeout.test.ts +++ b/src/gateway/server-http.hooks-request-timeout.test.ts @@ -1,7 +1,9 @@ -import type { IncomingMessage, ServerResponse } from "node:http"; import { beforeEach, describe, expect, test, vi } from "vitest"; -import type { createSubsystemLogger } from "../logging/subsystem.js"; -import { createGatewayRequest, createHooksConfig } from "./hooks-test-helpers.js"; +import { + createHookRequest, + createHooksHandler, + createResponse, +} from "./server-http.test-harness.js"; const { readJsonBodyMock } = vi.hoisted(() => ({ readJsonBodyMock: vi.fn(), @@ -15,68 +17,6 @@ vi.mock("./hooks.js", async (importOriginal) => { }; }); -import { createHooksRequestHandler } from "./server-http.js"; - -type HooksHandlerDeps = Parameters[0]; - -function createRequest(params?: { - authorization?: string; - remoteAddress?: string; - url?: string; - headers?: Record; -}): IncomingMessage { - return createGatewayRequest({ - method: "POST", - path: params?.url ?? "/hooks/wake", - host: "127.0.0.1:18789", - authorization: params?.authorization ?? "Bearer hook-secret", - remoteAddress: params?.remoteAddress, - headers: params?.headers, - }); -} - -function createResponse(): { - res: ServerResponse; - end: ReturnType; - setHeader: ReturnType; -} { - const setHeader = vi.fn(); - const end = vi.fn(); - const res = { - statusCode: 200, - setHeader, - end, - } as unknown as ServerResponse; - return { res, end, setHeader }; -} - -function createHandler(params?: { - dispatchWakeHook?: HooksHandlerDeps["dispatchWakeHook"]; - dispatchAgentHook?: HooksHandlerDeps["dispatchAgentHook"]; - bindHost?: string; - getClientIpConfig?: HooksHandlerDeps["getClientIpConfig"]; -}) { - return createHooksRequestHandler({ - getHooksConfig: () => createHooksConfig(), - bindHost: params?.bindHost ?? "127.0.0.1", - port: 18789, - logHooks: { - warn: vi.fn(), - debug: vi.fn(), - info: vi.fn(), - error: vi.fn(), - } as unknown as ReturnType, - getClientIpConfig: params?.getClientIpConfig, - dispatchWakeHook: - params?.dispatchWakeHook ?? - ((() => { - return; - }) as HooksHandlerDeps["dispatchWakeHook"]), - dispatchAgentHook: - params?.dispatchAgentHook ?? ((() => "run-1") as HooksHandlerDeps["dispatchAgentHook"]), - }); -} - describe("createHooksRequestHandler timeout status mapping", () => { beforeEach(() => { readJsonBodyMock.mockClear(); @@ -86,8 +26,8 @@ describe("createHooksRequestHandler timeout status mapping", () => { readJsonBodyMock.mockResolvedValue({ ok: false, error: "request body timeout" }); const dispatchWakeHook = vi.fn(); const dispatchAgentHook = vi.fn(() => "run-1"); - const handler = createHandler({ dispatchWakeHook, dispatchAgentHook }); - const req = createRequest(); + const handler = createHooksHandler({ dispatchWakeHook, dispatchAgentHook }); + const req = createHookRequest(); const { res, end } = createResponse(); const handled = await handler(req, res); @@ -100,10 +40,10 @@ describe("createHooksRequestHandler timeout status mapping", () => { }); test("shares hook auth rate-limit bucket across ipv4 and ipv4-mapped ipv6 forms", async () => { - const handler = createHandler(); + const handler = createHooksHandler({ bindHost: "127.0.0.1" }); for (let i = 0; i < 20; i++) { - const req = createRequest({ + const req = createHookRequest({ authorization: "Bearer wrong", remoteAddress: "1.2.3.4", }); @@ -113,7 +53,7 @@ describe("createHooksRequestHandler timeout status mapping", () => { expect(res.statusCode).toBe(401); } - const mappedReq = createRequest({ + const mappedReq = createHookRequest({ authorization: "Bearer wrong", remoteAddress: "::ffff:1.2.3.4", }); @@ -126,12 +66,12 @@ describe("createHooksRequestHandler timeout status mapping", () => { }); test("uses trusted proxy forwarded client ip for hook auth throttling", async () => { - const handler = createHandler({ + const handler = createHooksHandler({ getClientIpConfig: () => ({ trustedProxies: ["10.0.0.1"] }), }); for (let i = 0; i < 20; i++) { - const req = createRequest({ + const req = createHookRequest({ authorization: "Bearer wrong", remoteAddress: "10.0.0.1", headers: { "x-forwarded-for": "1.2.3.4" }, @@ -142,7 +82,7 @@ describe("createHooksRequestHandler timeout status mapping", () => { expect(res.statusCode).toBe(401); } - const forwardedReq = createRequest({ + const forwardedReq = createHookRequest({ authorization: "Bearer wrong", remoteAddress: "10.0.0.1", headers: { "x-forwarded-for": "1.2.3.4, 10.0.0.1" }, @@ -158,8 +98,8 @@ describe("createHooksRequestHandler timeout status mapping", () => { test.each(["0.0.0.0", "::"])( "does not throw when bindHost=%s while parsing non-hook request URL", async (bindHost) => { - const handler = createHandler({ bindHost }); - const req = createRequest({ url: "/" }); + const handler = createHooksHandler({ bindHost }); + const req = createHookRequest({ url: "/" }); const { res, end } = createResponse(); const handled = await handler(req, res); diff --git a/src/gateway/server-http.test-harness.ts b/src/gateway/server-http.test-harness.ts index 24612d60b..1adf863e4 100644 --- a/src/gateway/server-http.test-harness.ts +++ b/src/gateway/server-http.test-harness.ts @@ -9,6 +9,7 @@ import { withTempConfig } from "./test-temp-config.js"; export type GatewayHttpServer = ReturnType; export type GatewayServerOptions = Partial[0]>; +type HooksHandlerDeps = Parameters[0]; export const AUTH_NONE: ResolvedGatewayAuth = { mode: "none", @@ -30,6 +31,7 @@ export function createRequest(params: { method?: string; remoteAddress?: string; host?: string; + headers?: Record; }): IncomingMessage { return createGatewayRequest({ path: params.path, @@ -37,6 +39,23 @@ export function createRequest(params: { method: params.method, remoteAddress: params.remoteAddress, host: params.host, + headers: params.headers, + }); +} + +export function createHookRequest(params?: { + authorization?: string; + remoteAddress?: string; + url?: string; + headers?: Record; +}): IncomingMessage { + return createRequest({ + method: "POST", + path: params?.url ?? "/hooks/wake", + host: "127.0.0.1:18789", + authorization: params?.authorization ?? "Bearer hook-secret", + remoteAddress: params?.remoteAddress, + headers: params?.headers, }); } @@ -162,10 +181,20 @@ export function createCanonicalizedChannelPluginHandler() { }); } -export function createHooksHandler(bindHost: string) { +export function createHooksHandler( + params: + | string + | { + dispatchWakeHook?: HooksHandlerDeps["dispatchWakeHook"]; + dispatchAgentHook?: HooksHandlerDeps["dispatchAgentHook"]; + bindHost?: string; + getClientIpConfig?: HooksHandlerDeps["getClientIpConfig"]; + }, +) { + const options = typeof params === "string" ? { bindHost: params } : params; return createHooksRequestHandler({ getHooksConfig: () => createHooksConfig(), - bindHost, + bindHost: options.bindHost ?? "127.0.0.1", port: 18789, logHooks: { warn: vi.fn(), @@ -173,8 +202,9 @@ export function createHooksHandler(bindHost: string) { info: vi.fn(), error: vi.fn(), } as unknown as ReturnType, - dispatchWakeHook: () => {}, - dispatchAgentHook: () => "run-1", + getClientIpConfig: options.getClientIpConfig, + dispatchWakeHook: options.dispatchWakeHook ?? (() => {}), + dispatchAgentHook: options.dispatchAgentHook ?? (() => "run-1"), }); }