← back to Homesonspec

apps/workers/src/health-server.test.ts

95 lines

import { afterEach, describe, expect, it } from "vitest";
import http from "node:http";
import type { AddressInfo } from "node:net";
import { startHealthServer, closeHealthServer } from "./health-server";

/**
 * Regression coverage for the 2026-08-09 prod incident: the worker crash-looped
 * because healthServer.listen() had no 'error' handler, so an EADDRINUSE bind
 * failure on the shared host became an uncaughtException. A health probe is
 * auxiliary — its bind failure must NEVER crash the worker.
 */

const opened: http.Server[] = [];
afterEach(() => {
  for (const s of opened) {
    try {
      if (s.listening) s.close();
    } catch {
      /* ignore */
    }
  }
  opened.length = 0;
});

function listenReady(server: http.Server): Promise<number> {
  return new Promise((resolve, reject) => {
    server.once("error", reject);
    server.once("listening", () => resolve((server.address() as AddressInfo).port));
  });
}

function get(port: number): Promise<{ status: number; body: string }> {
  return new Promise((resolve, reject) => {
    http
      .get({ host: "127.0.0.1", port, path: "/health" }, (res) => {
        let body = "";
        res.on("data", (c) => (body += c));
        res.on("end", () => resolve({ status: res.statusCode ?? 0, body }));
      })
      .on("error", reject);
  });
}

describe("startHealthServer", () => {
  it("serves 200 + verdict on /health when healthy", async () => {
    const server = startHealthServer({ port: 0, host: "127.0.0.1", health: () => ({ healthy: true, reason: "ok" }) });
    opened.push(server);
    const port = await listenReady(server);
    const res = await get(port);
    expect(res.status).toBe(200);
    expect(JSON.parse(res.body)).toEqual({ healthy: true, reason: "ok" });
  });

  it("serves 503 when the queue verdict is unhealthy", async () => {
    const server = startHealthServer({
      port: 0,
      host: "127.0.0.1",
      health: () => ({ healthy: false, reason: "wedged" }),
    });
    opened.push(server);
    const port = await listenReady(server);
    const res = await get(port);
    expect(res.status).toBe(503);
    expect(JSON.parse(res.body).healthy).toBe(false);
  });

  it("a bind failure (EADDRINUSE) is NON-FATAL — logs and continues, never throws", async () => {
    // Occupy a port first.
    const blocker = http.createServer();
    opened.push(blocker);
    const takenPort = await new Promise<number>((resolve) =>
      blocker.listen(0, "127.0.0.1", () => resolve((blocker.address() as AddressInfo).port)),
    );

    const errors: string[] = [];
    // Starting the health server on the SAME port must not throw or crash.
    const server = startHealthServer({
      port: takenPort,
      host: "127.0.0.1",
      health: () => ({ healthy: true, reason: "ok" }),
      errorLog: (...a) => errors.push(a.map(String).join(" ")),
    });
    opened.push(server);

    // The 'error' event is async; give the event loop a tick to deliver it.
    await new Promise((r) => setTimeout(r, 50));

    expect(server.listening).toBe(false); // never bound (port taken)...
    expect(errors.some((e) => /failed to bind/.test(e))).toBe(true); // ...but handled, not thrown.
    // Reaching this line at all proves the process did not crash.
    // closeHealthServer must be safe on a server that never bound:
    expect(() => closeHealthServer(server)).not.toThrow();
  });
});