// src/lib/seranking/rate-limit.test.ts — horloge virtuelle injectée : aucun
// vrai timer, le test est instantané et déterministe.
import { describe, expect, test } from "bun:test";
import { createRateLimiter } from "./rate-limit";

function virtualClock() {
  let t = 0;
  return {
    now: () => t,
    sleep: (ms: number) => {
      t += ms;
      return Promise.resolve();
    },
    time: () => t,
  };
}

describe("createRateLimiter", () => {
  test("spaces calls by minIntervalMs", async () => {
    const clock = virtualClock();
    const limiter = createRateLimiter({
      minIntervalMs: 200,
      now: clock.now,
      sleep: clock.sleep,
    });
    const starts: number[] = [];
    await Promise.all([
      limiter.schedule(async () => starts.push(clock.time())),
      limiter.schedule(async () => starts.push(clock.time())),
      limiter.schedule(async () => starts.push(clock.time())),
    ]);
    expect(starts).toEqual([0, 200, 400]);
  });

  test("serializes: a call never starts before the previous one finished", async () => {
    const clock = virtualClock();
    const limiter = createRateLimiter({
      minIntervalMs: 200,
      now: clock.now,
      sleep: clock.sleep,
    });
    const order: string[] = [];
    await Promise.all([
      limiter.schedule(async () => {
        order.push("a-start");
        await clock.sleep(500); // travail plus long que l'intervalle
        order.push("a-end");
      }),
      limiter.schedule(async () => {
        order.push("b-start");
      }),
    ]);
    expect(order).toEqual(["a-start", "a-end", "b-start"]);
  });

  test("a rejected task does not poison the queue", async () => {
    const clock = virtualClock();
    const limiter = createRateLimiter({
      minIntervalMs: 200,
      now: clock.now,
      sleep: clock.sleep,
    });
    await expect(
      limiter.schedule(async () => {
        throw new Error("boom");
      }),
    ).rejects.toThrow("boom");
    await expect(limiter.schedule(async () => 42)).resolves.toBe(42);
  });

  test("returns the task result", async () => {
    const limiter = createRateLimiter({ minIntervalMs: 0 });
    expect(await limiter.schedule(async () => "ok")).toBe("ok");
  });
});
