import { describe, expect, it } from "vitest"; import { createSiteRateLimiter } from "./rate-limit.ts"; import { parseSizeMp as parseSize } from "./spider/spider.ts"; import { parseCount, parseSiteTime } from "./util.ts"; describe("站点令牌桶", () => { it("burst 内不等待,之后按间隔补充;penalize 顺延;并发按顺序排队", async () => { let clock = 0; const sleeps: number[] = []; const l = createSiteRateLimiter({ minIntervalMs: 1000, burst: 2, now: () => clock, sleep: async (ms) => { sleeps.push(ms); clock += ms; }, }); await Promise.all([l.acquire(), l.acquire(), l.acquire()]); expect(sleeps).toEqual([1000]); l.penalize(5000); expect(l.tryAcquire()).toBe(false); await l.acquire(); expect(sleeps.at(-1)).toBe(5000); }); it("取消等待", async () => { const l = createSiteRateLimiter({ minIntervalMs: 60_000 }); await l.acquire(); const ac = new AbortController(); const p = l.acquire(ac.signal); ac.abort(new Error("cancelled")); await expect(p).rejects.toThrow("cancelled"); }); }); describe("解析辅助", () => { it("parseSize/parseCount/parseSiteTime", () => { expect(parseSize("78.42 GB")).toBe(84202833838); expect(parseSize("850 KB")).toBe(870400); expect(parseSize("n/a")).toBe(0); expect(parseSize("1,234.56 MiB")).toBe(1294529987); expect(parseSize("2.1 TB")).toBe(2308974418330); expect(parseCount("1,204")).toBe(1204); expect(parseCount("532 / 3")).toBe(532); expect(parseCount("--")).toBeNull(); expect(parseSiteTime("2026-09-26 21:14:03", "+08:00")).toBe("2026-09-26T13:14:03.000Z"); expect(parseSiteTime("3小时前", "+08:00")).toBeNull(); }); });