import { describe, expect, it, vi } from "vitest"; import { memoizeAsyncWithRetry } from "#src/async-cache"; describe("memoizeAsyncWithRetry", () => { it("invokes the factory once and shares the resolved value across calls", async () => { const factory = vi.fn<() => Promise>().mockResolvedValue(42); const memoized = memoizeAsyncWithRetry(factory); const results = await Promise.all([memoized(), memoized(), memoized()]); expect(results).toEqual([42, 42, 42]); expect(factory).toHaveBeenCalledTimes(1); }); it("caches the same promise instance on success", async () => { const factory = vi.fn<() => Promise>().mockResolvedValue("parser"); const memoized = memoizeAsyncWithRetry(factory); await memoized(); await memoized(); expect(factory).toHaveBeenCalledTimes(1); }); it("surfaces the rejection to the caller each time the factory fails", async () => { const error = new Error("init failed"); const factory = vi.fn<() => Promise>().mockRejectedValue(error); const memoized = memoizeAsyncWithRetry(factory); await expect(memoized()).rejects.toThrow("init failed"); await expect(memoized()).rejects.toThrow("init failed"); }); it("drops a rejected result so the next call re-invokes the factory", async () => { const factory = vi .fn<() => Promise>() .mockRejectedValueOnce(new Error("transient")) .mockResolvedValue(7); const memoized = memoizeAsyncWithRetry(factory); await expect(memoized()).rejects.toThrow("transient"); const recovered = await memoized(); expect(recovered).toBe(7); expect(factory).toHaveBeenCalledTimes(2); }); });