import { describe, expect, test, vi } from "vitest"; import * as mutexExecutor from "@app/lib/mutexExecutor"; import { sleep } from "@app/lib/sleep"; describe("executor", () => { test("cancels running task", async () => { const e = mutexExecutor.create(); const first = e.run(async () => { await sleep(10); return "first"; }); const second = e.run(async () => { return "second"; }); expect(await first).toBe(undefined); expect(await second).toBe("second"); const third = e.run(async () => { await sleep(10); return "third"; }); const fourth = e.run(async () => { return "fourth"; }); expect(await third).toBe(undefined); expect(await fourth).toBe("fourth"); }); test("cancels multiple tasks", async () => { const e = mutexExecutor.create(); const canceled1 = e.run(async () => { await sleep(10); return true; }); const canceled2 = e.run(async () => { await sleep(10); return true; }); const canceled3 = e.run(async () => { await sleep(10); return true; }); const last = e.run(async () => { return true; }); expect(await canceled1).toBe(undefined); expect(await canceled2).toBe(undefined); expect(await canceled3).toBe(undefined); expect(await last).toBe(true); }); test("triggers abort signal event", async () => { const e = mutexExecutor.create(); const abortListener = vi.fn(); void e.run(async abort => { abort.addEventListener("abort", abortListener); await sleep(10); return "first"; }); expect(abortListener).not.toHaveBeenCalled(); // eslint-disable-next-line @typescript-eslint/no-empty-function void e.run(async () => {}); expect(abortListener).toHaveBeenCalled(); }); test("don’t throw error on aborted task", async () => { const e = mutexExecutor.create(); const first = e.run(async () => { await sleep(10); throw new Error(); }); const second = e.run(async () => { return "second"; }); expect(await first).toBe(undefined); expect(await second).toBe("second"); }); }); describe("worker", () => { test("sequential work", async () => { const w = mutexExecutor.createWorker(async (value: number) => { await sleep(10); return value; }); const outputs: number[] = []; w.output.onValue(value => outputs.push(value)); await w.submit(1); await w.submit(2); await w.submit(3); expect(outputs).toEqual([1, 2, 3]); }); test("overlapping work cancels", async () => { const w = mutexExecutor.createWorker(async (value: number) => { await sleep(10); return value; }); const nextOutput = w.output.firstToPromise(); void w.submit(1); void w.submit(2); void w.submit(3); expect(await nextOutput).toEqual(3); }); });