Ctrl K
ComunidadGuías…

lib/client-request-deadline.test.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import { afterEach, beforeEach, expect, it, vi } from "vitest";
import { withClientRequestDeadline } from "./client-request-deadline";

beforeEach(() => {
  vi.useFakeTimers();
  vi.spyOn(AbortSignal, "any").mockImplementation(() => { throw new Error("Unsupported API"); });
});
afterEach(() => { vi.useRealTimers(); vi.restoreAllMocks(); });

it("completes without AbortSignal.any and removes caller listeners", async () => {
  const caller = new AbortController();
  const add = vi.spyOn(caller.signal, "addEventListener");
  const remove = vi.spyOn(caller.signal, "removeEventListener");
  await expect(withClientRequestDeadline(async () => "ready", 30_000, caller.signal)).resolves.toBe("ready");
  expect(AbortSignal.any).not.toHaveBeenCalled();
  expect(remove).toHaveBeenCalledWith("abort", add.mock.calls[0][1]);
  expect(vi.getTimerCount()).toBe(0);
});

it("forwards caller cancellation and its reason without leaving listeners", async () => {
  const caller = new AbortController();
  const remove = vi.spyOn(caller.signal, "removeEventListener");
  let transport!: AbortSignal;
  const pending = withClientRequestDeadline(signal => {
    transport = signal;
    return new Promise(() => {});
  }, 30_000, caller.signal);
  const reason = new Error("Caller cancelled");
  caller.abort(reason);
  await expect(pending).rejects.toBe(reason);
  expect(transport.reason).toBe(reason);
  expect(remove).toHaveBeenCalledTimes(1);
  expect(vi.getTimerCount()).toBe(0);
});

it("never starts work for a pre-aborted caller", async () => {
  const caller = new AbortController();
  caller.abort();
  const work = vi.fn();
  await expect(withClientRequestDeadline(work, 30_000, caller.signal)).rejects.toMatchObject({ name: "AbortError" });
  expect(work).not.toHaveBeenCalled();
  expect(vi.getTimerCount()).toBe(0);
});

it("times out the transport and cleans up even without AbortSignal.any", async () => {
  const caller = new AbortController();
  const remove = vi.spyOn(caller.signal, "removeEventListener");
  let transport!: AbortSignal;
  const pending = withClientRequestDeadline(signal => {
    transport = signal;
    return new Promise(() => {});
  }, 30_000, caller.signal);
  const rejected = expect(pending).rejects.toMatchObject({ name: "TimeoutError" });
  await vi.advanceTimersByTimeAsync(30_000);
  await rejected;
  expect(transport.aborted).toBe(true);
  expect(remove).toHaveBeenCalledTimes(1);
  expect(vi.getTimerCount()).toBe(0);
});

it("keeps cancellation available when no transfer deadline is configured", async () => {
  const caller = new AbortController();
  const pending = withClientRequestDeadline(() => new Promise(() => {}), null, caller.signal);
  await vi.advanceTimersByTimeAsync(90_000);
  expect(vi.getTimerCount()).toBe(0);
  caller.abort();
  await expect(pending).rejects.toMatchObject({ name: "AbortError" });
});

it("cleans up after work rejects", async () => {
  const caller = new AbortController();
  const remove = vi.spyOn(caller.signal, "removeEventListener");
  const error = new Error("Read failed");
  await expect(withClientRequestDeadline(async () => { throw error; }, 30_000, caller.signal)).rejects.toBe(error);
  expect(remove).toHaveBeenCalledTimes(1);
  expect(vi.getTimerCount()).toBe(0);
});