fix(tests): improve buffered task tests: add chain, concurrency and queue behavior tests
This commit is contained in:
@@ -1,5 +1,13 @@
|
||||
# Changelog
|
||||
|
||||
## 2026-02-15 - 6.1.1 - fix(tests)
|
||||
improve buffered task tests: add chain, concurrency and queue behavior tests
|
||||
|
||||
- Replace tools.delayFor with @push.rocks/smartdelay for more deterministic timing in tests
|
||||
- Add tests for afterTask chaining, bufferMax concurrency, queued-run limits, and re-trigger behavior
|
||||
- Rename tasks to descriptive names and fix afterTask chaining order to avoid circular references
|
||||
- Change test runner invocation to export default tap.start() instead of calling tap.start() directly
|
||||
|
||||
## 2026-02-15 - 6.1.0 - feat(taskbuffer)
|
||||
add sliding-window rate limiting and result-sharing to TaskConstraintGroup and integrate with TaskManager
|
||||
|
||||
|
||||
@@ -1,52 +1,151 @@
|
||||
import { tap, expect } from '@git.zone/tstest/tapbundle';
|
||||
|
||||
import * as taskbuffer from '../ts/index.js';
|
||||
import * as smartdelay from '@push.rocks/smartdelay';
|
||||
|
||||
let counter1 = 0;
|
||||
let counter2 = 0;
|
||||
let counter3 = 0;
|
||||
// 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;
|
||||
|
||||
tap.test('should run buffered', async (tools) => {
|
||||
const task = new taskbuffer.Task({
|
||||
name: 'a buffered task',
|
||||
taskFunction: async () => {
|
||||
counter1++;
|
||||
await tools.delayFor(2000);
|
||||
console.log(`task 1 ran ${counter1} times`);
|
||||
},
|
||||
buffered: true,
|
||||
bufferMax: 1,
|
||||
afterTask: () => {
|
||||
return task2;
|
||||
},
|
||||
});
|
||||
const task2 = new taskbuffer.Task({
|
||||
name: 'a buffered task',
|
||||
taskFunction: async () => {
|
||||
counter2++;
|
||||
await tools.delayFor(2000);
|
||||
console.log(`task2 ran ${counter2} times`);
|
||||
},
|
||||
buffered: true,
|
||||
bufferMax: 1,
|
||||
afterTask: () => {
|
||||
return task3;
|
||||
},
|
||||
});
|
||||
const task3 = new taskbuffer.Task({
|
||||
name: 'a buffered task',
|
||||
name: 'buffered-chain-3',
|
||||
taskFunction: async () => {
|
||||
counter3++;
|
||||
await tools.delayFor(2000);
|
||||
console.log(`task3 ran ${counter3} times`);
|
||||
await smartdelay.delayFor(50);
|
||||
},
|
||||
buffered: true,
|
||||
bufferMax: 1,
|
||||
});
|
||||
while (counter1 < 10) {
|
||||
await tools.delayFor(5000);
|
||||
task.trigger();
|
||||
|
||||
const task2 = new taskbuffer.Task({
|
||||
name: 'buffered-chain-2',
|
||||
taskFunction: async () => {
|
||||
counter2++;
|
||||
await smartdelay.delayFor(50);
|
||||
},
|
||||
buffered: true,
|
||||
bufferMax: 1,
|
||||
afterTask: () => task3,
|
||||
});
|
||||
|
||||
const task1 = new taskbuffer.Task({
|
||||
name: 'buffered-chain-1',
|
||||
taskFunction: async () => {
|
||||
counter1++;
|
||||
await smartdelay.delayFor(50);
|
||||
},
|
||||
buffered: true,
|
||||
bufferMax: 1,
|
||||
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);
|
||||
});
|
||||
|
||||
tap.start();
|
||||
// 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,
|
||||
});
|
||||
|
||||
// Fire many triggers rapidly — only bufferMax should run concurrently
|
||||
for (let i = 0; i < 10; i++) {
|
||||
task.trigger();
|
||||
}
|
||||
|
||||
// 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);
|
||||
});
|
||||
|
||||
export default tap.start();
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@push.rocks/taskbuffer',
|
||||
version: '6.1.0',
|
||||
version: '6.1.1',
|
||||
description: 'A flexible task management library supporting TypeScript, allowing for task buffering, scheduling, and execution with dependency management.'
|
||||
}
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@push.rocks/taskbuffer',
|
||||
version: '6.1.0',
|
||||
version: '6.1.1',
|
||||
description: 'A flexible task management library supporting TypeScript, allowing for task buffering, scheduling, and execution with dependency management.'
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user