import { describe, expect, test } from "bun:test"; import { sleep, withTimeout } from "../sleep"; import { sequential } from "../sequential"; // ─── sleep ───────────────────────────────────────────────────────────── describe("sleep", () => { test("resolves after timeout", async () => { const start = Date.now(); await sleep(50); expect(Date.now() - start).toBeGreaterThanOrEqual(40); }); test("resolves immediately when signal already aborted", async () => { const ac = new AbortController(); ac.abort(); const start = Date.now(); await sleep(10_000, ac.signal); expect(Date.now() - start).toBeLessThan(50); }); test("resolves early on abort (default: no throw)", async () => { const ac = new AbortController(); const start = Date.now(); const p = sleep(10_000, ac.signal); setTimeout(() => ac.abort(), 30); await p; expect(Date.now() - start).toBeLessThan(200); }); test("rejects on abort with throwOnAbort", async () => { const ac = new AbortController(); ac.abort(); await expect( sleep(10_000, ac.signal, { throwOnAbort: true }) ).rejects.toThrow("aborted"); }); test("rejects with custom abortError", async () => { const ac = new AbortController(); ac.abort(); const customErr = () => new Error("custom abort"); await expect( sleep(10_000, ac.signal, { abortError: customErr }) ).rejects.toThrow("custom abort"); }); test("throwOnAbort rejects on mid-sleep abort", async () => { const ac = new AbortController(); const p = sleep(10_000, ac.signal, { throwOnAbort: true }); setTimeout(() => ac.abort(), 20); await expect(p).rejects.toThrow("aborted"); }); test("works without signal", async () => { await sleep(10); // just verify it resolves }); }); // ─── withTimeout ─────────────────────────────────────────────────────── describe("withTimeout", () => { test("resolves when promise completes before timeout", async () => { const result = await withTimeout( Promise.resolve(42), 1000, "timed out" ); expect(result).toBe(42); }); test("rejects when promise takes too long", async () => { const slow = new Promise((resolve) => setTimeout(resolve, 5000)); await expect( withTimeout(slow, 50, "operation timed out") ).rejects.toThrow("operation timed out"); }); test("rejects propagate through", async () => { await expect( withTimeout(Promise.reject(new Error("inner")), 1000, "timeout") ).rejects.toThrow("inner"); }); }); // ─── sequential ──────────────────────────────────────────────────────── describe("sequential", () => { test("executes calls in order", async () => { const order: number[] = []; const fn = sequential(async (n: number) => { await sleep(10); order.push(n); return n; }); const results = await Promise.all([fn(1), fn(2), fn(3)]); expect(order).toEqual([1, 2, 3]); expect(results).toEqual([1, 2, 3]); }); test("returns correct result for each call", async () => { const fn = sequential(async (x: number) => x * 2); const r1 = await fn(5); const r2 = await fn(10); expect(r1).toBe(10); expect(r2).toBe(20); }); test("propagates errors without blocking queue", async () => { const fn = sequential(async (x: number) => { if (x === 2) throw new Error("fail"); return x; }); const p1 = fn(1); const p2 = fn(2); const p3 = fn(3); expect(await p1).toBe(1); await expect(p2).rejects.toThrow("fail"); expect(await p3).toBe(3); }); test("handles single call", async () => { const fn = sequential(async (s: string) => s.toUpperCase()); expect(await fn("hello")).toBe("HELLO"); }); });