1import { describe, expect, it } from "@effect/vitest";
2
3import { SerializedAsyncQueue } from "./serialized-async-queue";
4
5function deferred() {
6 let resolve!: () => void;
7 const promise = new Promise<void>((next) => {
8 resolve = next;
9 });
10 return { promise, resolve };
11}
12
13describe("SerializedAsyncQueue", () => {
14 it("does not let a newer operation overtake an in-flight operation", async () => {
15 const queue = new SerializedAsyncQueue();
16 const firstGate = deferred();
17 const events: string[] = [];
18
19 const first = queue.run(async () => {
20 events.push("first:start");
21 await firstGate.promise;
22 events.push("first:end");
23 });
24 const second = queue.run(async () => {
25 events.push("second");
26 });
27
28 await Promise.resolve();
29 expect(events).toEqual(["first:start"]);
30 firstGate.resolve();
31 await Promise.all([first, second]);
32 expect(events).toEqual(["first:start", "first:end", "second"]);
33 });
34
35 it("continues after a rejected operation", async () => {
36 const queue = new SerializedAsyncQueue();
37 const first = queue.run(async () => {
38 throw new Error("failed");
39 });
40 const second = queue.run(async () => "recovered");
41
42 await expect(first).rejects.toThrow("failed");
43 await expect(second).resolves.toBe("recovered");
44 });
45});