132 lines
3.6 KiB
JavaScript
132 lines
3.6 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
|
|
import {
|
|
resolveConcurrency,
|
|
runWithConcurrency,
|
|
} from "../build-standalone-public-packages.mjs";
|
|
|
|
function deferred() {
|
|
let resolve;
|
|
let reject;
|
|
const promise = new Promise((res, rej) => {
|
|
resolve = res;
|
|
reject = rej;
|
|
});
|
|
return { promise, resolve, reject };
|
|
}
|
|
|
|
test("runs every item and preserves result order", async () => {
|
|
const items = [1, 2, 3, 4, 5];
|
|
const results = await runWithConcurrency(items, 2, async (value) => value * 10);
|
|
assert.deepEqual(results, [10, 20, 30, 40, 50]);
|
|
});
|
|
|
|
test("never exceeds the concurrency limit", async () => {
|
|
const items = Array.from({ length: 8 }, (_, index) => index);
|
|
const limit = 3;
|
|
let active = 0;
|
|
let peak = 0;
|
|
const gates = items.map(() => deferred());
|
|
|
|
const run = runWithConcurrency(items, limit, async (value) => {
|
|
active += 1;
|
|
peak = Math.max(peak, active);
|
|
assert.ok(active <= limit, `active ${active} exceeded limit ${limit}`);
|
|
await gates[value].promise;
|
|
active -= 1;
|
|
return value;
|
|
});
|
|
|
|
// Release gates progressively; the pool must keep at most `limit` in flight.
|
|
for (const gate of gates) {
|
|
await Promise.resolve();
|
|
gate.resolve();
|
|
}
|
|
|
|
await run;
|
|
assert.equal(peak, limit);
|
|
});
|
|
|
|
test("aggregates failures and still runs remaining items", async () => {
|
|
const items = [0, 1, 2, 3];
|
|
const processed = [];
|
|
|
|
await assert.rejects(
|
|
() =>
|
|
runWithConcurrency(items, 2, async (value) => {
|
|
processed.push(value);
|
|
if (value === 1) {
|
|
throw new Error(`boom-${value}`);
|
|
}
|
|
return value;
|
|
}),
|
|
(error) => {
|
|
assert.match(error.message, /1 standalone package build\(s\) failed/);
|
|
assert.equal(error.failures.length, 1);
|
|
assert.equal(error.failures[0].index, 1);
|
|
assert.match(error.failures[0].error.message, /boom-1/);
|
|
return true;
|
|
},
|
|
);
|
|
|
|
// A single failure must not abort the rest of the queue.
|
|
assert.deepEqual(processed.sort((a, b) => a - b), [0, 1, 2, 3]);
|
|
});
|
|
|
|
test("reports failures sorted by original index", async () => {
|
|
const items = [0, 1, 2, 3, 4];
|
|
|
|
await assert.rejects(
|
|
() =>
|
|
runWithConcurrency(items, 5, async (value) => {
|
|
if (value === 3 || value === 1) {
|
|
throw new Error(`fail-${value}`);
|
|
}
|
|
return value;
|
|
}),
|
|
(error) => {
|
|
assert.deepEqual(
|
|
error.failures.map(({ index }) => index),
|
|
[1, 3],
|
|
);
|
|
return true;
|
|
},
|
|
);
|
|
});
|
|
|
|
test("resolveConcurrency honors a valid env override, capped by package count", () => {
|
|
const previous = process.env.STANDALONE_BUILD_CONCURRENCY;
|
|
try {
|
|
process.env.STANDALONE_BUILD_CONCURRENCY = "2";
|
|
assert.equal(resolveConcurrency(7), 2);
|
|
assert.equal(resolveConcurrency(1), 1);
|
|
assert.equal(resolveConcurrency(0), 1);
|
|
|
|
process.env.STANDALONE_BUILD_CONCURRENCY = "0";
|
|
assert.ok(resolveConcurrency(7) >= 1);
|
|
|
|
process.env.STANDALONE_BUILD_CONCURRENCY = "not-a-number";
|
|
assert.ok(resolveConcurrency(7) >= 1);
|
|
} finally {
|
|
if (previous === undefined) {
|
|
delete process.env.STANDALONE_BUILD_CONCURRENCY;
|
|
} else {
|
|
process.env.STANDALONE_BUILD_CONCURRENCY = previous;
|
|
}
|
|
}
|
|
});
|
|
|
|
test("resolveConcurrency never returns more than the package count", () => {
|
|
const previous = process.env.STANDALONE_BUILD_CONCURRENCY;
|
|
try {
|
|
delete process.env.STANDALONE_BUILD_CONCURRENCY;
|
|
assert.equal(resolveConcurrency(0), 1);
|
|
assert.ok(resolveConcurrency(3) <= 3);
|
|
} finally {
|
|
if (previous !== undefined) {
|
|
process.env.STANDALONE_BUILD_CONCURRENCY = previous;
|
|
}
|
|
}
|
|
});
|