Files
Multi-Hoster-Upload/tests/coalesced-set.test.js
T
Sucukdeluxe 76a228fb7c release: v2.0.6
Redesign the desktop workspace with task sidebars, live filters, clearer settings, and an accessible update dialog.

Harden encrypted backup imports, configuration persistence, history retention, queue snapshots, shutdown recovery, and update installation ordering.

Publish verified Windows artifacts and refreshed English documentation.
2026-08-10 09:28:28 +02:00

145 lines
4.5 KiB
JavaScript

const { test } = require('node:test');
const assert = require('node:assert/strict');
const { makeCoalescedSet } = require('../lib/coalesced-set');
// Synchronous scheduler stand-in: collects callbacks instead of running
// them, so tests can drive the timing explicitly.
function makeManualScheduler() {
const queue = [];
const fn = (cb) => queue.push(cb);
fn.flush = () => {
while (queue.length) {
const cb = queue.shift();
cb();
}
};
fn.queueLength = () => queue.length;
return fn;
}
test('throws if apply callback missing', () => {
assert.throws(() => makeCoalescedSet());
assert.throws(() => makeCoalescedSet({}));
assert.throws(() => makeCoalescedSet({ apply: 'not-a-fn' }));
});
test('multiple adds in one tick coalesce into one apply call', () => {
const sched = makeManualScheduler();
const calls = [];
const cs = makeCoalescedSet({
apply: (drop) => calls.push([...drop].sort()),
scheduler: sched
});
cs.add('a'); cs.add('b'); cs.add('c');
assert.equal(sched.queueLength(), 1, 'only one microtask scheduled');
assert.equal(cs.pendingSize(), 3);
sched.flush();
assert.deepEqual(calls, [['a', 'b', 'c']]);
assert.equal(cs.pendingSize(), 0);
});
test('duplicate adds are deduplicated', () => {
const sched = makeManualScheduler();
const calls = [];
const cs = makeCoalescedSet({ apply: (d) => calls.push([...d]), scheduler: sched });
cs.add('a'); cs.add('a'); cs.add('a');
sched.flush();
assert.deepEqual(calls, [['a']]);
});
test('two batches in series stay independent', () => {
const sched = makeManualScheduler();
const calls = [];
const cs = makeCoalescedSet({ apply: (d) => calls.push([...d]), scheduler: sched });
cs.add('x'); cs.add('y');
sched.flush();
cs.add('z');
sched.flush();
assert.deepEqual(calls, [['x', 'y'], ['z']]);
});
test('add after flush re-schedules a new microtask', () => {
const sched = makeManualScheduler();
const cs = makeCoalescedSet({ apply: () => {}, scheduler: sched });
cs.add('a');
assert.equal(sched.queueLength(), 1);
sched.flush();
assert.equal(sched.queueLength(), 0);
assert.equal(cs.isScheduled(), false);
cs.add('b');
assert.equal(sched.queueLength(), 1, 'new add → new microtask');
});
test('drainSync flushes synchronously without waiting for scheduler', () => {
const sched = makeManualScheduler();
const calls = [];
const cs = makeCoalescedSet({ apply: (d) => calls.push([...d]), scheduler: sched });
cs.add('p'); cs.add('q');
cs.drainSync();
assert.deepEqual(calls, [['p', 'q']]);
assert.equal(cs.pendingSize(), 0);
// Pending microtask was for the same ids — when it runs, pending is empty
// → apply NOT called twice.
sched.flush();
assert.equal(calls.length, 1, 'queued microtask is a no-op after drainSync');
});
test('drainSync on empty set is a no-op', () => {
let called = 0;
const cs = makeCoalescedSet({ apply: () => called++ });
cs.drainSync();
assert.equal(called, 0);
});
test('throwing apply does not lock out subsequent batches', () => {
const sched = makeManualScheduler();
let attempt = 0;
const cs = makeCoalescedSet({
apply: () => { attempt++; if (attempt === 1) throw new Error('boom'); },
scheduler: sched
});
cs.add('a');
// First flush throws inside apply but is swallowed; coalescer must still work.
sched.flush();
cs.add('b');
sched.flush();
assert.equal(attempt, 2, 'second batch still ran despite first throwing');
});
test('default scheduler is queueMicrotask (or Promise fallback) — runs eventually', async () => {
const calls = [];
const cs = makeCoalescedSet({ apply: (d) => calls.push([...d]) });
cs.add('z');
// Wait one microtask
await Promise.resolve();
assert.deepEqual(calls, [['z']]);
});
test('no-op tick: scheduler fires while pending is empty (e.g. drained)', () => {
const sched = makeManualScheduler();
let called = 0;
const cs = makeCoalescedSet({ apply: () => called++, scheduler: sched });
cs.add('a');
cs.drainSync();
assert.equal(called, 1);
// Pending microtask still in queue → flush; pending is empty → apply NOT called again.
sched.flush();
assert.equal(called, 1);
});
test('large burst of 5000 adds coalesces to one apply call', () => {
const sched = makeManualScheduler();
const calls = [];
const cs = makeCoalescedSet({ apply: (d) => calls.push(d.size), scheduler: sched });
for (let i = 0; i < 5000; i++) cs.add('id-' + i);
assert.equal(sched.queueLength(), 1);
sched.flush();
assert.deepEqual(calls, [5000]);
});