2025-08-26 20:15:46 +00:00
|
|
|
import { tap, expect } from '@git.zone/tstest/tapbundle';
|
2021-11-11 19:59:56 +01:00
|
|
|
|
2022-03-25 12:14:49 +01:00
|
|
|
import * as taskbuffer from '../ts/index.js';
|
2026-02-15 21:56:39 +00:00
|
|
|
import * as smartdelay from '@push.rocks/smartdelay';
|
2021-11-11 19:59:56 +01:00
|
|
|
|
2026-02-15 21:56:39 +00:00
|
|
|
// Test 1: Basic buffered execution with afterTask chain
|
|
|
|
|
tap.test('should run buffered tasks with afterTask chain', async () => {
|
|
|
|
|
let counter1 = 0;
|
|
|
|
|
let counter2 = 0;
|
|
|
|
|
let counter3 = 0;
|
2021-11-11 19:59:56 +01:00
|
|
|
|
2026-02-15 21:56:39 +00:00
|
|
|
const task3 = new taskbuffer.Task({
|
|
|
|
|
name: 'buffered-chain-3',
|
2021-11-11 19:59:56 +01:00
|
|
|
taskFunction: async () => {
|
2026-02-15 21:56:39 +00:00
|
|
|
counter3++;
|
|
|
|
|
await smartdelay.delayFor(50);
|
2021-11-11 19:59:56 +01:00
|
|
|
},
|
|
|
|
|
buffered: true,
|
|
|
|
|
bufferMax: 1,
|
|
|
|
|
});
|
2026-02-15 21:56:39 +00:00
|
|
|
|
2021-11-11 19:59:56 +01:00
|
|
|
const task2 = new taskbuffer.Task({
|
2026-02-15 21:56:39 +00:00
|
|
|
name: 'buffered-chain-2',
|
2021-11-11 19:59:56 +01:00
|
|
|
taskFunction: async () => {
|
|
|
|
|
counter2++;
|
2026-02-15 21:56:39 +00:00
|
|
|
await smartdelay.delayFor(50);
|
2021-11-11 19:59:56 +01:00
|
|
|
},
|
|
|
|
|
buffered: true,
|
|
|
|
|
bufferMax: 1,
|
2026-02-15 21:56:39 +00:00
|
|
|
afterTask: () => task3,
|
2021-11-11 19:59:56 +01:00
|
|
|
});
|
2026-02-15 21:56:39 +00:00
|
|
|
|
|
|
|
|
const task1 = new taskbuffer.Task({
|
|
|
|
|
name: 'buffered-chain-1',
|
2021-11-11 19:59:56 +01:00
|
|
|
taskFunction: async () => {
|
2026-02-15 21:56:39 +00:00
|
|
|
counter1++;
|
|
|
|
|
await smartdelay.delayFor(50);
|
2021-11-11 19:59:56 +01:00
|
|
|
},
|
|
|
|
|
buffered: true,
|
2021-11-11 20:48:31 +01:00
|
|
|
bufferMax: 1,
|
2026-02-15 21:56:39 +00:00
|
|
|
afterTask: () => task2,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Trigger 3 times with enough spacing for the chain to complete
|
|
|
|
|
for (let i = 0; i < 3; i++) {
|
|
|
|
|
task1.trigger();
|
|
|
|
|
await smartdelay.delayFor(250); // enough for chain of 3 x 50ms tasks
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Wait for final chain to finish
|
|
|
|
|
await smartdelay.delayFor(500);
|
|
|
|
|
|
|
|
|
|
// Each task in the chain should have run at least once
|
|
|
|
|
expect(counter1).toBeGreaterThanOrEqual(1);
|
|
|
|
|
expect(counter2).toBeGreaterThanOrEqual(1);
|
|
|
|
|
expect(counter3).toBeGreaterThanOrEqual(1);
|
|
|
|
|
|
|
|
|
|
// afterTask chain means task2 count should match task1 (each trigger chains)
|
|
|
|
|
expect(counter2).toEqual(counter1);
|
|
|
|
|
expect(counter3).toEqual(counter1);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Test 2: bufferMax limits concurrent buffered executions
|
|
|
|
|
tap.test('should respect bufferMax for concurrent buffered calls', async () => {
|
|
|
|
|
let running = 0;
|
|
|
|
|
let maxRunning = 0;
|
|
|
|
|
let totalRuns = 0;
|
|
|
|
|
|
|
|
|
|
const task = new taskbuffer.Task({
|
|
|
|
|
name: 'buffer-max-test',
|
|
|
|
|
taskFunction: async () => {
|
|
|
|
|
running++;
|
|
|
|
|
maxRunning = Math.max(maxRunning, running);
|
|
|
|
|
totalRuns++;
|
|
|
|
|
await smartdelay.delayFor(100);
|
|
|
|
|
running--;
|
|
|
|
|
},
|
|
|
|
|
buffered: true,
|
|
|
|
|
bufferMax: 2,
|
2021-11-11 19:59:56 +01:00
|
|
|
});
|
2026-02-15 21:56:39 +00:00
|
|
|
|
|
|
|
|
// Fire many triggers rapidly — only bufferMax should run concurrently
|
|
|
|
|
for (let i = 0; i < 10; i++) {
|
2021-11-11 19:59:56 +01:00
|
|
|
task.trigger();
|
|
|
|
|
}
|
2026-02-15 21:56:39 +00:00
|
|
|
|
|
|
|
|
// Wait for all buffered executions to complete
|
|
|
|
|
await smartdelay.delayFor(1000);
|
|
|
|
|
|
|
|
|
|
expect(maxRunning).toBeLessThanOrEqual(2);
|
|
|
|
|
expect(totalRuns).toBeGreaterThanOrEqual(1);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Test 3: bufferMax limits how many runs are queued during execution
|
|
|
|
|
tap.test('should limit queued runs to bufferMax during execution', async () => {
|
|
|
|
|
let runCount = 0;
|
|
|
|
|
|
|
|
|
|
const task = new taskbuffer.Task({
|
|
|
|
|
name: 'buffer-queue-test',
|
|
|
|
|
taskFunction: async () => {
|
|
|
|
|
runCount++;
|
|
|
|
|
await smartdelay.delayFor(100);
|
|
|
|
|
},
|
|
|
|
|
buffered: true,
|
|
|
|
|
bufferMax: 2,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Rapid-fire 5 triggers — bufferMax:2 means counter caps at 2
|
|
|
|
|
// so only 2 runs will happen (the initial run + 1 buffered rerun)
|
|
|
|
|
task.trigger();
|
|
|
|
|
task.trigger();
|
|
|
|
|
task.trigger();
|
|
|
|
|
task.trigger();
|
|
|
|
|
task.trigger();
|
|
|
|
|
|
|
|
|
|
await smartdelay.delayFor(500);
|
|
|
|
|
|
|
|
|
|
expect(runCount).toEqual(2);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Test 4: Triggers spaced after completion queue new runs
|
|
|
|
|
tap.test('should re-trigger after previous buffered run completes', async () => {
|
|
|
|
|
let runCount = 0;
|
|
|
|
|
|
|
|
|
|
const task = new taskbuffer.Task({
|
|
|
|
|
name: 'retrigger-test',
|
|
|
|
|
taskFunction: async () => {
|
|
|
|
|
runCount++;
|
|
|
|
|
await smartdelay.delayFor(50);
|
|
|
|
|
},
|
|
|
|
|
buffered: true,
|
|
|
|
|
bufferMax: 1,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// First trigger starts execution
|
|
|
|
|
task.trigger();
|
|
|
|
|
// Wait for it to complete
|
|
|
|
|
await smartdelay.delayFor(100);
|
|
|
|
|
|
|
|
|
|
// Second trigger starts a new execution (task is now idle)
|
|
|
|
|
task.trigger();
|
|
|
|
|
await smartdelay.delayFor(100);
|
|
|
|
|
|
|
|
|
|
// Third trigger
|
|
|
|
|
task.trigger();
|
|
|
|
|
await smartdelay.delayFor(100);
|
|
|
|
|
|
|
|
|
|
expect(runCount).toEqual(3);
|
2021-11-11 20:48:31 +01:00
|
|
|
});
|
2021-11-11 19:59:56 +01:00
|
|
|
|
2026-02-15 21:56:39 +00:00
|
|
|
export default tap.start();
|