Version 2.0.0 - update of everything
This commit is contained in:
85 files changed
+13065
-2107
No files matched your search
@@ -0,0 +1,120 @@
|
||||
// Tests unitaires de backend/src/utils/Limiter.js (node limiter.test.js)
|
||||
const assert = require("assert");
|
||||
const path = require("path").resolve(__dirname, "../../src/utils/Limiter.js");
|
||||
const { createLimiter, mapLimit, createInflight, createCache } = require(path);
|
||||
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
|
||||
|
||||
const tests = [];
|
||||
const test = (name, fn) => tests.push({ name, fn });
|
||||
|
||||
test("limiter: concurrence max respectée et résultats corrects", async () => {
|
||||
const lim = createLimiter(2);
|
||||
let active = 0, peak = 0;
|
||||
const res = await Promise.all([1, 2, 3, 4, 5, 6].map(i => lim.run(async () => {
|
||||
active++; peak = Math.max(peak, active);
|
||||
await sleep(20);
|
||||
active--;
|
||||
return i * 10;
|
||||
})));
|
||||
assert.deepStrictEqual(res, [10, 20, 30, 40, 50, 60]);
|
||||
assert.strictEqual(peak, 2);
|
||||
assert.strictEqual(lim.active, 0);
|
||||
assert.strictEqual(lim.pending, 0);
|
||||
});
|
||||
|
||||
test("limiter: erreurs (async et sync) propagées sans bloquer la file", async () => {
|
||||
const lim = createLimiter(1);
|
||||
await assert.rejects(lim.run(async () => { throw new Error("a"); }), /a/);
|
||||
await assert.rejects(lim.run(() => { throw new Error("b"); }), /b/);
|
||||
assert.strictEqual(await lim.run(() => 42), 42);
|
||||
});
|
||||
|
||||
test("limiter: minIntervalMs espace les démarrages", async () => {
|
||||
const lim = createLimiter(5, { minIntervalMs: 30 });
|
||||
const starts = [];
|
||||
await Promise.all([0, 1, 2, 3].map(() => lim.run(async () => { starts.push(Date.now()); })));
|
||||
for (let i = 1; i < starts.length; i++) assert.ok(starts[i] - starts[i - 1] >= 25, "écart " + (starts[i] - starts[i - 1]));
|
||||
});
|
||||
|
||||
test("mapLimit: ordre conservé, concurrence bornée, erreurs ignorées", async () => {
|
||||
let active = 0, peak = 0;
|
||||
const out = await mapLimit([30, 5, 20, 1, 10], 3, async (ms, i) => {
|
||||
active++; peak = Math.max(peak, active);
|
||||
await sleep(ms);
|
||||
active--;
|
||||
if (i === 2) throw new Error("x");
|
||||
return ms;
|
||||
}, () => null);
|
||||
assert.deepStrictEqual(out, [30, 5, null, 1, 10]);
|
||||
assert.strictEqual(peak, 3);
|
||||
assert.deepStrictEqual(await mapLimit(null, 2, async x => x), []);
|
||||
});
|
||||
|
||||
test("inflight: appels simultanés dédupliqués, libérés ensuite", async () => {
|
||||
const inf = createInflight();
|
||||
let calls = 0;
|
||||
const fn = async () => { calls++; await sleep(10); return "v"; };
|
||||
const [a, b] = await Promise.all([inf.run("k", fn), inf.run("k", fn)]);
|
||||
assert.strictEqual(a, "v"); assert.strictEqual(b, "v"); assert.strictEqual(calls, 1);
|
||||
assert.strictEqual(inf.size, 0);
|
||||
await inf.run("k", fn);
|
||||
assert.strictEqual(calls, 2);
|
||||
await assert.rejects(inf.run("e", async () => { throw new Error("boom"); }));
|
||||
assert.strictEqual(inf.size, 0);
|
||||
});
|
||||
|
||||
test("cache: TTL, null explicite, expiration", async () => {
|
||||
const c = createCache({ ttlMs: 40, max: 10 });
|
||||
c.set("a", 1);
|
||||
c.set("n", null);
|
||||
assert.strictEqual(c.get("a"), 1);
|
||||
assert.strictEqual(c.get("n"), null);
|
||||
assert.strictEqual(c.get("absent"), undefined);
|
||||
c.set("z", 1, 0); // TTL 0 → pas mis en cache
|
||||
assert.strictEqual(c.get("z"), undefined);
|
||||
await sleep(60);
|
||||
assert.strictEqual(c.get("a"), undefined);
|
||||
assert.strictEqual(c.size, 1); // "n" expiré mais pas encore lu
|
||||
});
|
||||
|
||||
test("cache: taille max et éviction LRU", () => {
|
||||
const c = createCache({ ttlMs: 10000, max: 3 });
|
||||
c.set("a", 1); c.set("b", 2); c.set("c", 3);
|
||||
c.get("a"); // a devient le plus récent
|
||||
c.set("d", 4);
|
||||
assert.strictEqual(c.size, 3);
|
||||
assert.strictEqual(c.get("b"), undefined);
|
||||
assert.strictEqual(c.get("a"), 1);
|
||||
assert.strictEqual(c.get("d"), 4);
|
||||
});
|
||||
|
||||
test("cache.wrap: hit, dédup en cours, null non mis en cache, ttlFor", async () => {
|
||||
const c = createCache({ ttlMs: 10000, max: 10 });
|
||||
let calls = 0;
|
||||
const fn = async () => { calls++; await sleep(10); return { x: 1 }; };
|
||||
const [a, b] = await Promise.all([c.wrap("k", fn), c.wrap("k", fn)]);
|
||||
assert.strictEqual(a, b); assert.strictEqual(calls, 1);
|
||||
await c.wrap("k", fn);
|
||||
assert.strictEqual(calls, 1);
|
||||
let nulls = 0;
|
||||
await c.wrap("n", async () => { nulls++; return null; });
|
||||
await c.wrap("n", async () => { nulls++; return null; });
|
||||
assert.strictEqual(nulls, 2);
|
||||
await c.wrap("m", async () => { nulls++; return null; }, { ttlFor: v => v === null ? 5000 : 10000 });
|
||||
await c.wrap("m", async () => { nulls++; return null; }, { ttlFor: v => v === null ? 5000 : 10000 });
|
||||
assert.strictEqual(nulls, 3);
|
||||
await assert.rejects(c.wrap("e", async () => { throw new Error("err"); }));
|
||||
assert.strictEqual(c.get("e"), undefined);
|
||||
const s = c.stats();
|
||||
assert.ok(s.hits >= 2 && s.misses >= 4, JSON.stringify(s));
|
||||
});
|
||||
|
||||
(async () => {
|
||||
let failed = 0;
|
||||
for (const t of tests) {
|
||||
try { await t.fn(); console.log("OK " + t.name); }
|
||||
catch (e) { failed++; console.log("FAIL " + t.name + "\n " + (e && e.stack || e)); }
|
||||
}
|
||||
console.log(`\n${tests.length - failed}/${tests.length} réussis`);
|
||||
process.exit(failed ? 1 : 0);
|
||||
})();
|
||||
Reference in new issue
Block a user