import type { IncomingMessage, ServerResponse } from "node:http"; import { describe, expect, it, vi } from "vitest"; import { createGatewayPluginRequestHandler } from "./plugins-http.js"; import { createTestRegistry } from "./__tests__/test-utils.js"; const makeResponse = (): { res: ServerResponse; setHeader: ReturnType; end: ReturnType; } => { const setHeader = vi.fn(); const end = vi.fn(); const res = { headersSent: false, statusCode: 200, setHeader, end, } as unknown as ServerResponse; return { res, setHeader, end }; }; describe("createGatewayPluginRequestHandler", () => { it("returns false when no handlers are registered", async () => { const log = { warn: vi.fn() } as unknown as Parameters< typeof createGatewayPluginRequestHandler >[0]["log"]; const handler = createGatewayPluginRequestHandler({ registry: createTestRegistry(), log, }); const { res } = makeResponse(); const handled = await handler({} as IncomingMessage, res); expect(handled).toBe(false); }); it("continues until a handler reports it handled the request", async () => { const first = vi.fn(async () => false); const second = vi.fn(async () => true); const handler = createGatewayPluginRequestHandler({ registry: createTestRegistry({ httpHandlers: [ { pluginId: "first", handler: first, source: "first" }, { pluginId: "second", handler: second, source: "second" }, ], }), log: { warn: vi.fn() } as unknown as Parameters< typeof createGatewayPluginRequestHandler >[0]["log"], }); const { res } = makeResponse(); const handled = await handler({} as IncomingMessage, res); expect(handled).toBe(true); expect(first).toHaveBeenCalledTimes(1); expect(second).toHaveBeenCalledTimes(1); }); it("handles registered http routes before generic handlers", async () => { const routeHandler = vi.fn(async (_req, res: ServerResponse) => { res.statusCode = 200; }); const fallback = vi.fn(async () => true); const handler = createGatewayPluginRequestHandler({ registry: createTestRegistry({ httpRoutes: [ { pluginId: "route", path: "/demo", handler: routeHandler, source: "route", }, ], httpHandlers: [{ pluginId: "fallback", handler: fallback, source: "fallback" }], }), log: { warn: vi.fn() } as unknown as Parameters< typeof createGatewayPluginRequestHandler >[0]["log"], }); const { res } = makeResponse(); const handled = await handler({ url: "/demo" } as IncomingMessage, res); expect(handled).toBe(true); expect(routeHandler).toHaveBeenCalledTimes(1); expect(fallback).not.toHaveBeenCalled(); }); it("logs and responds with 500 when a handler throws", async () => { const log = { warn: vi.fn() } as unknown as Parameters< typeof createGatewayPluginRequestHandler >[0]["log"]; const handler = createGatewayPluginRequestHandler({ registry: createTestRegistry({ httpHandlers: [ { pluginId: "boom", handler: async () => { throw new Error("boom"); }, source: "boom", }, ], }), log, }); const { res, setHeader, end } = makeResponse(); const handled = await handler({} as IncomingMessage, res); expect(handled).toBe(true); expect(log.warn).toHaveBeenCalledWith(expect.stringContaining("boom")); expect(res.statusCode).toBe(500); expect(setHeader).toHaveBeenCalledWith("Content-Type", "text/plain; charset=utf-8"); expect(end).toHaveBeenCalledWith("Internal Server Error"); }); });