@nostr-fetch/kernel
Version:
Kernel of nostr-fetch
144 lines (121 loc) • 3.4 kB
text/typescript
import { setTimeout as wait } from "node:timers/promises";
import { inspect } from "node:util";
import { describe, expect, test } from "vitest";
import { Channel } from "./channel";
describe("channel", () => {
test.concurrent("convey message (receiver is faster)", async () => {
const [tx, chIter] = Channel.make<number>();
let n = 0;
const i = setInterval(() => {
if (n >= 10) {
tx.close();
clearInterval(i);
return;
}
tx.send(n);
n++;
}, 100);
const res = [];
for await (const d of chIter) {
res.push(d);
}
expect(res).toEqual([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
});
test.concurrent("convey message (sender is faster)", async () => {
const [tx, chIter] = Channel.make<number>();
let n = 0;
const i = setInterval(() => {
if (n >= 10) {
tx.close();
clearInterval(i);
return;
}
tx.send(n);
n++;
}, 0);
const res = [];
for await (const d of chIter) {
res.push(d);
await wait(100);
}
expect(res).toEqual([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
});
test.concurrent("convey error and it interrupts receiver", async () => {
const [tx, chIter] = Channel.make<number>();
let n = 0;
const i = setInterval(() => {
if (n === 5) {
tx.error("rejects 5!");
return;
}
if (n >= 10) {
tx.close();
clearInterval(i);
return;
}
tx.send(n);
n++;
}, 0);
await expect(async () => {
const res = [];
for await (const d of chIter) {
res.push(d);
}
return res;
}).rejects.toThrow("rejects 5!");
});
test.concurrent("can't be iterated multiple times", async () => {
const [tx, chIter] = Channel.make<number>();
let n = 0;
const i = setInterval(() => {
if (n >= 10) {
tx.close();
clearInterval(i);
return;
}
tx.send(n);
n++;
}, 100);
const res = [];
const iterate = async (iter: AsyncIterable<number>) => {
for await (const n of iter) {
res.push(n);
}
};
await expect(async () => {
await Promise.all([iterate(chIter), iterate(chIter)]);
}).rejects.toThrow("Iterating a single channel in multiple location is not allowed");
});
test.concurrent("backpressure", async () => {
const [tx, chIter] = Channel.make<number>({ highWaterMark: 3 });
for (const n of [0, 1, 2, 3]) {
tx.send(n);
}
tx.close();
// check if `wait` is pending
const wait1 = tx.waitUntilDrained();
const wait2 = tx.waitUntilDrained();
[wait1, wait2].forEach((w) => {
expect(inspect(w).includes("pending")).toBe(true);
});
const res = [];
// drain first item -> sendQ is now drained enough
const iterBody = chIter[Symbol.asyncIterator]();
const fst = await iterBody.next();
res.push(fst.value);
// check if `wait` have been resolved
[wait1, wait2].forEach((w) => {
expect(inspect(w).includes("pending")).toBe(false);
});
await expect(Promise.all([wait1, wait2])).resolves.toEqual([undefined, undefined]);
// drain remaining items
while (true) {
const { done, value } = await iterBody.next();
if (done) {
break;
}
res.push(value);
}
expect(res).toEqual([0, 1, 2, 3]);
});
});