diff --git a/test/test.1.task.ts b/test/test.1.task.ts index b1a3352..5daf62d 100644 --- a/test/test.1.task.ts +++ b/test/test.1.task.ts @@ -1,116 +1,96 @@ import { expect, tap } from '@push.rocks/tapbundle'; - import * as taskbuffer from '../ts/index.js'; - import * as smartpromise from '@push.rocks/smartpromise'; import * as smartdelay from '@push.rocks/smartdelay'; -// setup some testData to work with -let testTask: taskbuffer.Task; - -let testPreTask = new taskbuffer.Task({ - taskFunction: async () => { - console.log('preTask executed'); - }, - preTask: testTask, -}); - -// some more tasks to test with -let task1Counter = 0; // how often task 1 is being executed -let task1 = new taskbuffer.Task({ - name: 'Task 1', - taskFunction: () => { - let done = smartpromise.defer(); - console.log('Task1 started'); - setTimeout(() => { - task1Counter++; - console.log('Task1 executed'); - done.resolve(); - }, 5000); - return done.promise; - }, -}); - -let task2 = new taskbuffer.Task({ - name: 'Task 1', - taskFunction: async () => { - const done = smartpromise.defer(); - console.log('Task2 started'); - setTimeout(() => { - console.log('Task2 executed'); - done.resolve(); - }, 5000); - await done.promise; - }, -}); - -let task3 = new taskbuffer.Task({ - name: 'Task 3', - taskFunction: () => { - let done = smartpromise.defer(); - console.log('Task3 started'); - setTimeout(() => { - console.log('Task3 executed'); - done.resolve(); - }, 5000); - return done.promise; - }, -}); - tap.test('new Task() should return a new task', async () => { - testTask = new taskbuffer.Task({ + const testTask = new taskbuffer.Task({ taskFunction: async () => { console.log('executed twice'); }, - preTask: testPreTask, }); -}); - -tap.test('expect testTask to be an instance of Task', async () => { expect(testTask).toBeInstanceOf(taskbuffer.Task); }); -tap.test('expect testTask.idle is true', async () => { - if (!testTask.idle) { - throw new Error('testTask.idle is not true'); - } +tap.test('should be able to get the task state', async () => { + const testTask = new taskbuffer.Task({ + taskFunction: async () => {}, + }); + expect(testTask.state).toEqual('ready'); }); -tap.test('testTask.running should be of type boolean and initially false', async () => { - expect(testTask.running).toBeTypeofBoolean(); - // tslint:disable-next-line:no-unused-expression - expect(testTask.running).toBeFalse(); +tap.test('should have bufferMax set to the provided value', async () => { + const task2 = new taskbuffer.Task({ + taskFunction: async () => {}, + }); + expect(task2.bufferMax).toBeUndefined(); // test for a task without bufferMax set + const bufferedTask = new taskbuffer.Task({ + taskFunction: async () => {}, + buffered: true, + bufferMax: 3, + }); + expect(bufferedTask.bufferMax).toEqual(3); }); -tap.test('testTask.trigger() should return Promise', async () => { - expect(testTask.trigger()).toBeInstanceOf(Promise); -}); - -tap.test('testTask.trigger() returned Promise should be fullfilled', async () => { - await testTask.trigger(); -}); - -tap.test('expect to run a task without pre and afterTask errorless', async () => { - let localTestTask = new taskbuffer.Task({ - taskFunction: async () => { - console.log('only once'); +tap.test('should be able to trigger tasks multiple times', async () => { + let task1Counter = 0; + const task1 = new taskbuffer.Task({ + name: 'Task 1', + taskFunction: () => { + let done = smartpromise.defer(); + console.log('Task1 started'); + setTimeout(() => { + task1Counter++; + console.log('Task1 executed'); + done.resolve(); + }, 5000); + return done.promise; }, }); - await localTestTask.trigger(); + await task1.trigger(); + await task1.trigger(); + expect(task1Counter).toEqual(2); }); -tap.test('expect task to run in buffered mode', async () => { - let localTestTask = new taskbuffer.Task({ +tap.test('should execute setup function before the task function', async () => { + const task2 = new taskbuffer.Task({ + name: 'Task 2', + taskSetup: async () => { + console.log('this is the setup function for task 2. It should only run once.') + return { + nice: 'yes', + } + }, + taskFunction: async (before, setupArg) => { + expect(setupArg).toEqual({ nice: 'yes' }); + const done = smartpromise.defer(); + console.log('Task2 started'); + setTimeout(() => { + console.log('Task2 executed'); + done.resolve(); + }, 5000); + await done.promise; + }, + }); + await task2.trigger(); +}); + +tap.test('should not exceed bufferMax when task is buffered', async () => { + let counter = 0; + const bufferedTask = new taskbuffer.Task({ taskFunction: async () => { - await smartdelay.delayFor(3000); + counter++; + await smartdelay.delayFor(2000); + counter--; }, buffered: true, bufferMax: 2, }); - localTestTask.trigger(); - localTestTask.trigger(); - localTestTask.trigger(); - await localTestTask.trigger(); + bufferedTask.trigger(); + bufferedTask.trigger(); + bufferedTask.trigger(); + await smartdelay.delayFor(100); + expect(counter <= bufferedTask.bufferMax).toBeTrue(); }); tap.start(); diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index b727389..d54f114 100644 --- a/ts/00_commitinfo_data.ts +++ b/ts/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@push.rocks/taskbuffer', - version: '3.0.12', + version: '3.0.13', description: 'flexible task management. TypeScript ready!' } diff --git a/ts/taskbuffer.classes.task.ts b/ts/taskbuffer.classes.task.ts index 522410e..cc7700c 100644 --- a/ts/taskbuffer.classes.task.ts +++ b/ts/taskbuffer.classes.task.ts @@ -4,20 +4,24 @@ import { CycleCounter } from './taskbuffer.classes.cyclecounter.js'; import { logger } from './taskbuffer.logging.js'; -export interface ITaskFunction { - (x?: any): PromiseLike; +export interface ITaskFunction { + (x?: any, setupValue?: T): PromiseLike; } -export type TPreOrAfterTaskFunction = () => Task; +export interface ITaskSetupFunction { + (): Promise; +} -export class Task { +export type TPreOrAfterTaskFunction = () => Task; + +export class Task { // STATIC - public static extractTask(preOrAfterTaskArg: Task | TPreOrAfterTaskFunction): Task { + public static extractTask(preOrAfterTaskArg: Task | TPreOrAfterTaskFunction): Task { switch (true) { case !preOrAfterTaskArg: return null; case preOrAfterTaskArg instanceof Task: - return preOrAfterTaskArg as Task; + return preOrAfterTaskArg as Task; case typeof preOrAfterTaskArg === 'function': const taskFunction = preOrAfterTaskArg as TPreOrAfterTaskFunction; return taskFunction(); @@ -32,7 +36,7 @@ export class Task { return done.promise; }; - public static isTask = (taskArg: Task): boolean => { + public static isTask = (taskArg: Task): boolean => { if (taskArg instanceof Task && typeof taskArg.taskFunction === 'function') { return true; } else { @@ -40,10 +44,10 @@ export class Task { } }; - public static isTaskTouched = ( - taskArg: Task | TPreOrAfterTaskFunction, - touchedTasksArray: Task[] - ): boolean => { + public static isTaskTouched ( + taskArg: Task | TPreOrAfterTaskFunction, + touchedTasksArray: Task[] + ): boolean { const taskToCheck = Task.extractTask(taskArg); let result = false; for (const keyArg in touchedTasksArray) { @@ -54,42 +58,39 @@ export class Task { return result; }; - public static runTask = async ( - taskArg: Task | TPreOrAfterTaskFunction, - optionsArg: { x?: any; touchedTasksArray?: Task[] } + public static runTask = async ( + taskArg: Task | TPreOrAfterTaskFunction, + optionsArg: { x?: any; touchedTasksArray?: Task[] } ) => { - // extracts the task in case it is specified as a return value of a function const taskToRun = Task.extractTask(taskArg); const done = plugins.smartpromise.defer(); - // pay respect to execDelay + if (!taskToRun.setupValue && taskToRun.taskSetup) { + taskToRun.setupValue = await taskToRun.taskSetup(); + } + if (taskToRun.execDelay) { await plugins.smartdelay.delayFor(taskToRun.execDelay); } - // set running params taskToRun.running = true; done.promise.then(async () => { taskToRun.running = false; }); - // handle options const options = { ...{ x: undefined, touchedTasksArray: [] }, ...optionsArg, }; const x = options.x; - const touchedTasksArray: Task[] = options.touchedTasksArray; + const touchedTasksArray: Task[] = options.touchedTasksArray; touchedTasksArray.push(taskToRun); - // run the task cascade const localDeferred = plugins.smartpromise.defer(); localDeferred.promise .then(() => { - // lets run any preTask - if (taskToRun.preTask && !Task.isTaskTouched(taskToRun.preTask, touchedTasksArray)) { return Task.runTask(taskToRun.preTask, { x, touchedTasksArray }); } else { @@ -99,9 +100,8 @@ export class Task { } }) .then(async (x) => { - // lets run the main task try { - return await taskToRun.taskFunction(x); + return await taskToRun.taskFunction(x, taskToRun.setupValue); } catch (e) { console.log(e); } @@ -126,15 +126,9 @@ export class Task { }; // INSTANCE - // mandatory properties public name: string; - /** - * the version of the task - * should follow semver - * might be important for DistributedCoordinator - */ public version: string; - public taskFunction: ITaskFunction; + public taskFunction: ITaskFunction; public buffered: boolean; public cronJob: plugins.smarttime.CronJob; @@ -142,11 +136,9 @@ export class Task { public execDelay: number; public timeout: number; - // tasks to run before and after - public preTask: Task | TPreOrAfterTaskFunction; - public afterTask: Task | TPreOrAfterTaskFunction; + public preTask: Task | TPreOrAfterTaskFunction; + public afterTask: Task | TPreOrAfterTaskFunction; - // initialize by default public running: boolean = false; public bufferRunner = new BufferRunner(this); public cycleCounter = new CycleCounter(this); @@ -154,36 +146,18 @@ export class Task { public idle: boolean = true; private _state: string = 'ready'; + public taskSetup: ITaskSetupFunction; + public setupValue: T; + constructor(optionsArg: { - /** - * the task function to run, must return promise - */ - taskFunction: ITaskFunction; - /** - * any other task to run before - */ - preTask?: Task | TPreOrAfterTaskFunction; - /** - * any other task to run after - */ - afterTask?: Task | TPreOrAfterTaskFunction; - /** - * wether this task should run buffered - */ + taskFunction: ITaskFunction; + preTask?: Task | TPreOrAfterTaskFunction; + afterTask?: Task | TPreOrAfterTaskFunction; buffered?: boolean; - /** - * the maximum buffer - */ bufferMax?: number; - /** - * the execution delay, before the task is executed - * only makes sense when running in buffered mode - */ execDelay?: number; - /** - * the name of the task - */ name?: string; + taskSetup?: ITaskSetupFunction; }) { this.taskFunction = optionsArg.taskFunction; this.preTask = optionsArg.preTask; @@ -193,11 +167,9 @@ export class Task { this.bufferMax = optionsArg.bufferMax; this.execDelay = optionsArg.execDelay; this.name = optionsArg.name; + this.taskSetup = optionsArg.taskSetup; } - /** - * trigger the task. Will trigger buffered if this.buffered is true - */ public trigger(x?: any): Promise { if (this.buffered) { return this.triggerBuffered(x); @@ -206,18 +178,10 @@ export class Task { } } - /** - * trigger task unbuffered. - * will actually run the task, not considering any buffered limits. - */ public triggerUnBuffered(x?: any): Promise { - return Task.runTask(this, { x: x }); + return Task.runTask(this, { x: x }); } - /** - * trigger task buffered. - * note: .trigger() also calls this function - */ public triggerBuffered(x?: any): Promise { return this.bufferRunner.trigger(x); }