Files
plainleaf/plug-api/lib/async.test.ts
T
Zef Hemel 02bd8ed362 Migration Deno -> Node.js
* First pass
* Now using vitest for tests
2026-02-14 22:08:35 +01:00

185 lines
4.8 KiB
TypeScript

import { expect, test } from "vitest";
import {
batchRequests,
processWithConcurrency,
PromiseQueue,
sleep,
} from "./async.ts";
test("PromiseQueue test", async () => {
const q = new PromiseQueue();
let r1RanFirst = false;
const r1 = q.runInQueue(async () => {
await sleep(10);
r1RanFirst = true;
// console.log("1");
return 1;
});
const r2 = q.runInQueue(async () => {
// console.log("2");
await sleep(4);
return 2;
});
expect(await Promise.all([r1, r2])).toEqual([1, 2]);
expect(r1RanFirst).toEqual(true);
let wasRun = false;
await q.runInQueue(async () => {
await sleep(4);
wasRun = true;
});
expect(wasRun).toEqual(true);
});
test("processWithConcurrency test - basic functionality", async () => {
const items = [1, 2, 3, 4, 5];
const results = await processWithConcurrency(
items,
async (item) => {
await sleep(10);
return item * 2;
},
2,
);
expect(results).toEqual([2, 4, 6, 8, 10]);
});
test("processWithConcurrency test - maintains order", async () => {
const items = [1, 2, 3, 4, 5];
const startTimes: number[] = [];
const endTimes: number[] = [];
const results = await processWithConcurrency(
items,
async (item) => {
const start = Date.now();
startTimes[item - 1] = start;
// Longer sleep for higher numbers to test order preservation
await sleep(item * 5);
endTimes[item - 1] = Date.now();
return `item-${item}`;
},
2,
);
// Results should be in original order despite different processing times
expect(results).toEqual(["item-1", "item-2", "item-3", "item-4", "item-5"]);
});
test("processWithConcurrency test - concurrency limit", async () => {
const items = [1, 2, 3, 4, 5, 6];
const activeCount = { value: 0 };
const maxActive = { value: 0 };
const results = await processWithConcurrency(
items,
async (item) => {
activeCount.value++;
maxActive.value = Math.max(maxActive.value, activeCount.value);
await sleep(20);
activeCount.value--;
return item;
},
3,
);
expect(results).toEqual([1, 2, 3, 4, 5, 6]);
// Should never exceed concurrency limit of 3
expect(maxActive.value <= 3).toEqual(true);
});
test("processWithConcurrency test - empty array", async () => {
const results = await processWithConcurrency(
[],
(item) => Promise.resolve(item),
2,
);
expect(results).toEqual([]);
});
test("processWithConcurrency test - concurrency higher than item count", async () => {
const items = [1, 2, 3];
const results = await processWithConcurrency(
items,
async (item) => {
await sleep(10);
return item + 10;
},
10, // Higher than items.length
);
expect(results).toEqual([11, 12, 13]);
});
test("processWithConcurrency test - error handling", async () => {
const items = [1, 2, 3, 4, 5];
try {
await processWithConcurrency(
items,
(item) => {
if (item === 3) {
throw new Error(`Error processing item ${item}`);
}
return Promise.resolve(item * 2);
},
2,
);
expect(false).toEqual(true); // Should have thrown an error
} catch (error) {
expect(
error instanceof Error &&
error.message.includes("Error processing item 3"),
).toBeTruthy();
}
});
test("processWithConcurrency test - performance with concurrency", async () => {
const items = Array.from({ length: 6 }, (_, i) => i + 1);
const delay = 30;
// Test with concurrency of 1 (sequential)
const start1 = Date.now();
await processWithConcurrency(
items,
async (item) => {
await sleep(delay);
return item;
},
1,
);
const sequential = Date.now() - start1;
// Test with concurrency of 3 (parallel)
const start2 = Date.now();
await processWithConcurrency(
items,
async (item) => {
await sleep(delay);
return item;
},
3,
);
const parallel = Date.now() - start2;
// Parallel should be significantly faster than sequential
// Allow some margin for timing variations
expect(
parallel < sequential * 0.8,
).toBeTruthy(); // `Parallel (${parallel}ms) should be faster than sequential (${sequential}ms)`
});
test("Batch test", async () => {
// Generate an array with numbers up to 100
const elements = Array.from(Array(100).keys());
const multiplied = await batchRequests(elements, async (batch) => {
await sleep(2);
// Batches should be 9 or smaller (last batch will be smaller)
expect(batch.length <= 9).toBeTruthy();
return batch.map((e) => e * 2);
}, 9);
expect(multiplied).toEqual(elements.map((e) => e * 2));
const multiplied2 = await batchRequests(elements, (batch) => {
return Promise.resolve(batch.map((e) => e * 2));
}, 10000);
expect(multiplied2).toEqual(elements.map((e) => e * 2));
});