trails/apps/planner/app/lib/http.server.test.ts
Ullrich Schäfer d70d6ee8a8
fix(planner/brouter): add 30s timeout to outbound fetch
Mirrors journal PR #5. `fetchSegment()` and `computeSegmentGpx()` in
lib/brouter.ts called `fetch()` with no AbortSignal — a hung or slow
BRouter would stall the request handler indefinitely.

New `lib/http.server.ts::fetchWithTimeout()` (same as the journal's
helper) wraps fetch with `AbortSignal.timeout(30_000)`, composable
with a caller-supplied signal via `AbortSignal.any()`.

Tests: lib/http.server.test.ts (2 cases — timeout abort, happy path).
2026-05-25 23:53:19 +02:00

28 lines
1 KiB
TypeScript

import { describe, it, expect, vi, afterEach } from "vitest";
import { fetchWithTimeout } from "./http.server.ts";
const originalFetch = globalThis.fetch;
afterEach(() => {
globalThis.fetch = originalFetch;
vi.restoreAllMocks();
});
describe("fetchWithTimeout (planner)", () => {
it("aborts when the upstream exceeds the timeout", async () => {
globalThis.fetch = vi.fn((_input: RequestInfo | URL, init?: RequestInit) => {
return new Promise((_resolve, reject) => {
init?.signal?.addEventListener("abort", () => {
reject(new DOMException("aborted", "AbortError"));
});
});
}) as typeof fetch;
await expect(fetchWithTimeout("https://example.test", {}, 25)).rejects.toThrow();
});
it("returns the response when the call resolves in time", async () => {
globalThis.fetch = vi.fn(async () => new Response("ok", { status: 200 })) as typeof fetch;
const resp = await fetchWithTimeout("https://example.test", {}, 1000);
expect(resp.status).toBe(200);
});
});