From 2431bdb4c2dc637169bf623a40fc6562f685e56e Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Sat, 8 Jul 2023 21:05:52 +0200 Subject: [PATCH] feat: add pool and worker readyness tracking infrastructure MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Jérôme Benoit --- CHANGELOG.md | 10 ++ README.md | 2 + examples/dynamicExample.js | 3 + examples/fixedExample.js | 3 + src/pools/abstract-pool.ts | 47 ++++++- src/pools/cluster/dynamic.ts | 1 + src/pools/pool.ts | 8 +- src/pools/thread/dynamic.ts | 1 + src/pools/worker-node.ts | 2 +- src/worker/abstract-worker.ts | 12 +- tests/pools/abstract/abstract-pool.test.js | 140 ++++++++++++++++++--- tests/pools/cluster/fixed.test.js | 2 +- tests/pools/thread/fixed.test.js | 2 +- tests/worker/abstract-worker.test.js | 11 +- tests/worker/cluster-worker.test.js | 8 +- tests/worker/thread-worker.test.js | 10 +- 16 files changed, 219 insertions(+), 43 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bdfa3268..bc877031 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed + +- Ensure workers are not recreated on error at pool startup. + +### Added + +- Add `ready` and `strategy` fields to pool information. +- Add pool event `ready` to notify when the number of workers created in the pool has reached the maximum size expected and are ready. +- Add dynamic pool sizes checks. + ## [2.6.9] - 2023-07-07 ### Fixed diff --git a/README.md b/README.md index 3ebb9b7c..e6d676ec 100644 --- a/README.md +++ b/README.md @@ -122,6 +122,7 @@ const pool = new FixedThreadPool(availableParallelism(), './yourWorker.js', { onlineHandler: () => console.info('worker is online') }) +pool.emitter.on(PoolEvents.ready, () => console.info('Pool is ready')) pool.emitter.on(PoolEvents.busy, () => console.info('Pool is busy')) // or a dynamic worker-threads pool @@ -131,6 +132,7 @@ const pool = new DynamicThreadPool(Math.floor(availableParallelism() / 2), avail }) pool.emitter.on(PoolEvents.full, () => console.info('Pool is full')) +pool.emitter.on(PoolEvents.ready, () => console.info('Pool is ready')) pool.emitter.on(PoolEvents.busy, () => console.info('Pool is busy')) // the execute method signature is the same for both implementations, diff --git a/examples/dynamicExample.js b/examples/dynamicExample.js index bb457af8..f36138ac 100644 --- a/examples/dynamicExample.js +++ b/examples/dynamicExample.js @@ -14,8 +14,10 @@ const pool = new DynamicThreadPool( } ) let poolFull = 0 +let poolReady = 0 let poolBusy = 0 pool.emitter.on(PoolEvents.full, () => poolFull++) +pool.emitter.on(PoolEvents.ready, () => poolReady++) pool.emitter.on(PoolEvents.busy, () => poolBusy++) let resolved = 0 @@ -29,6 +31,7 @@ for (let i = 1; i <= iterations; i++) { if (resolved === iterations) { console.info('Time taken is ' + (performance.now() - start)) console.info('The pool was full for ' + poolFull + ' times') + console.info('The pool was ready for ' + poolReady + ' times') return console.info('The pool was busy for ' + poolBusy + ' times') } return null diff --git a/examples/fixedExample.js b/examples/fixedExample.js index 2346f7b4..884ab59e 100644 --- a/examples/fixedExample.js +++ b/examples/fixedExample.js @@ -8,7 +8,9 @@ const pool = new FixedThreadPool(availableParallelism(), './yourWorker.js', { errorHandler: e => console.error(e), onlineHandler: () => console.info('worker is online') }) +let poolReady = 0 let poolBusy = 0 +pool.emitter.on(PoolEvents.ready, () => poolReady++) pool.emitter.on(PoolEvents.busy, () => poolBusy++) let resolved = 0 @@ -21,6 +23,7 @@ for (let i = 1; i <= iterations; i++) { resolved++ if (resolved === iterations) { console.info('Time taken is ' + (performance.now() - start)) + console.info('The pool was ready for ' + poolReady + ' times') return console.info('The pool was busy for ' + poolBusy + ' times') } return null diff --git a/src/pools/abstract-pool.ts b/src/pools/abstract-pool.ts index 53bce7d2..6c9a0fd1 100644 --- a/src/pools/abstract-pool.ts +++ b/src/pools/abstract-pool.ts @@ -153,7 +153,19 @@ export abstract class AbstractPool< 'Cannot instantiate a pool with a negative number of workers' ) } else if (this.type === PoolTypes.fixed && numberOfWorkers === 0) { - throw new Error('Cannot instantiate a fixed pool with no worker') + throw new RangeError('Cannot instantiate a fixed pool with zero worker') + } + } + + protected checkDynamicPoolSize (min: number, max: number): void { + if (this.type === PoolTypes.dynamic && min > max) { + throw new RangeError( + 'Cannot instantiate a dynamic pool with a maximum pool size inferior to the minimum pool size' + ) + } else if (this.type === PoolTypes.dynamic && min === max) { + throw new RangeError( + 'Cannot instantiate a dynamic pool with a minimum pool size equal to the maximum pool size. Use a fixed pool instead' + ) } } @@ -252,6 +264,8 @@ export abstract class AbstractPool< version, type: this.type, worker: this.worker, + ready: this.ready, + strategy: this.opts.workerChoiceStrategy as WorkerChoiceStrategy, minSize: this.minSize, maxSize: this.maxSize, ...(this.workerChoiceStrategyContext.getTaskStatisticsRequirements() @@ -381,6 +395,19 @@ export abstract class AbstractPool< } } + private get starting (): boolean { + return ( + !this.full || + (this.full && this.workerNodes.some(workerNode => !workerNode.info.ready)) + ) + } + + private get ready (): boolean { + return ( + this.full && this.workerNodes.every(workerNode => workerNode.info.ready) + ) + } + /** * Gets the approximate pool utilization. * @@ -864,6 +891,13 @@ export abstract class AbstractPool< protected afterWorkerSetup (worker: Worker): void { // Listen to worker messages. this.registerWorkerMessageListener(worker, this.workerListener()) + // Send startup message to worker. + this.sendToWorker(worker, { + ready: false, + workerId: this.getWorkerInfo(this.getWorkerNodeKey(worker)).id + }) + // Setup worker task statistics computation. + this.setWorkerStatistics(worker) } /** @@ -883,7 +917,7 @@ export abstract class AbstractPool< if (this.opts.enableTasksQueue === true) { this.redistributeQueuedTasks(worker) } - if (this.opts.restartWorkerOnError === true) { + if (this.opts.restartWorkerOnError === true && !this.starting) { if (this.getWorkerInfo(this.getWorkerNodeKey(worker)).dynamic) { this.createAndSetupDynamicWorker() } else { @@ -899,8 +933,6 @@ export abstract class AbstractPool< this.pushWorkerNode(worker) - this.setWorkerStatistics(worker) - this.afterWorkerSetup(worker) return worker @@ -941,7 +973,6 @@ export abstract class AbstractPool< */ protected createAndSetupDynamicWorker (): Worker { const worker = this.createAndSetupWorker() - this.getWorkerInfo(this.getWorkerNodeKey(worker)).dynamic = true this.registerWorkerMessageListener(worker, message => { const workerNodeKey = this.getWorkerNodeKey(worker) if ( @@ -957,6 +988,7 @@ export abstract class AbstractPool< void (this.destroyWorker(worker) as Promise) } }) + this.getWorkerInfo(this.getWorkerNodeKey(worker)).dynamic = true this.sendToWorker(worker, { checkAlive: true }) return worker } @@ -968,7 +1000,7 @@ export abstract class AbstractPool< */ protected workerListener (): (message: MessageValue) => void { return message => { - if (message.workerId != null && message.ready != null) { + if (message.ready != null && message.workerId != null) { // Worker ready message received this.handleWorkerReadyMessage(message) } else if (message.id != null) { @@ -990,6 +1022,9 @@ export abstract class AbstractPool< }'` ) } + if (this.emitter != null && this.ready) { + this.emitter.emit(PoolEvents.ready, this.info) + } } private handleTaskExecutionResponse (message: MessageValue): void { diff --git a/src/pools/cluster/dynamic.ts b/src/pools/cluster/dynamic.ts index a9ccd697..75296f7a 100644 --- a/src/pools/cluster/dynamic.ts +++ b/src/pools/cluster/dynamic.ts @@ -31,6 +31,7 @@ export class DynamicClusterPool< opts: ClusterPoolOptions = {} ) { super(min, filePath, opts) + this.checkDynamicPoolSize(this.numberOfWorkers, this.max) } /** @inheritDoc */ diff --git a/src/pools/pool.ts b/src/pools/pool.ts index b1892752..78d298f9 100644 --- a/src/pools/pool.ts +++ b/src/pools/pool.ts @@ -42,6 +42,7 @@ export class PoolEmitter extends EventEmitter {} */ export const PoolEvents = Object.freeze({ full: 'full', + ready: 'ready', busy: 'busy', error: 'error', taskError: 'taskError' @@ -59,6 +60,8 @@ export interface PoolInfo { readonly version: string readonly type: PoolType readonly worker: WorkerType + readonly ready: boolean + readonly strategy: WorkerChoiceStrategy readonly minSize: number readonly maxSize: number /** Pool utilization ratio. */ @@ -179,8 +182,9 @@ export interface IPool< * * Events that can currently be listened to: * - * - `'full'`: Emitted when the pool is dynamic and full. - * - `'busy'`: Emitted when the pool is busy. + * - `'full'`: Emitted when the pool is dynamic and the number of workers created has reached the maximum size expected. + * - `'ready'`: Emitted when the number of workers created in the pool has reached the maximum size expected and are ready. + * - `'busy'`: Emitted when the number of workers created in the pool has reached the maximum size expected and are executing at least one task. * - `'error'`: Emitted when an uncaught error occurs. * - `'taskError'`: Emitted when an error occurs while executing a task. */ diff --git a/src/pools/thread/dynamic.ts b/src/pools/thread/dynamic.ts index b03ca47e..1d3f3f5d 100644 --- a/src/pools/thread/dynamic.ts +++ b/src/pools/thread/dynamic.ts @@ -31,6 +31,7 @@ export class DynamicThreadPool< opts: ThreadPoolOptions = {} ) { super(min, filePath, opts) + this.checkDynamicPoolSize(this.numberOfWorkers, this.max) } /** @inheritDoc */ diff --git a/src/pools/worker-node.ts b/src/pools/worker-node.ts index 0a6b8de7..ea50ebdb 100644 --- a/src/pools/worker-node.ts +++ b/src/pools/worker-node.ts @@ -75,7 +75,7 @@ implements IWorkerNode { id: this.getWorkerId(worker, workerType), type: workerType, dynamic: false, - ready: true + ready: false } } diff --git a/src/worker/abstract-worker.ts b/src/worker/abstract-worker.ts index 92d68bcb..4e92be01 100644 --- a/src/worker/abstract-worker.ts +++ b/src/worker/abstract-worker.ts @@ -145,7 +145,10 @@ export abstract class AbstractWorker< * @param message - Message received. */ protected messageListener (message: MessageValue): void { - if (message.statistics != null) { + if (message.ready != null && message.workerId === this.id) { + // Startup message received + this.workerReady() + } else if (message.statistics != null) { // Statistics message received this.statistics = message.statistics } else if (message.checkAlive != null) { @@ -166,6 +169,13 @@ export abstract class AbstractWorker< } } + /** + * Notifies the main worker that this worker is ready to process tasks. + */ + protected workerReady (): void { + !this.isMain && this.sendToMainWorker({ ready: true, workerId: this.id }) + } + /** * Starts the worker alive check interval. */ diff --git a/tests/pools/abstract/abstract-pool.test.js b/tests/pools/abstract/abstract-pool.test.js index 0e31ad5c..caec34fd 100644 --- a/tests/pools/abstract/abstract-pool.test.js +++ b/tests/pools/abstract/abstract-pool.test.js @@ -12,6 +12,7 @@ const { const { CircularArray } = require('../../../lib/circular-array') const { Queue } = require('../../../lib/queue') const { version } = require('../../../package.json') +const { waitPoolEvents } = require('../../test-utils') describe('Abstract pool test suite', () => { const numberOfWorkers = 2 @@ -19,6 +20,7 @@ describe('Abstract pool test suite', () => { removeAllWorker () { this.workerNodes = [] this.promiseResponseMap.clear() + this.handleWorkerReadyMessage = () => {} } } class StubPoolWithIsMain extends FixedThreadPool { @@ -80,6 +82,25 @@ describe('Abstract pool test suite', () => { ) }) + it('Verify dynamic pool sizing', () => { + expect( + () => + new DynamicThreadPool(2, 1, './tests/worker-files/thread/testWorker.js') + ).toThrowError( + new RangeError( + 'Cannot instantiate a dynamic pool with a maximum pool size inferior to the minimum pool size' + ) + ) + expect( + () => + new DynamicThreadPool(1, 1, './tests/worker-files/thread/testWorker.js') + ).toThrowError( + new RangeError( + 'Cannot instantiate a dynamic pool with a minimum pool size equal to the maximum pool size. Use a fixed pool instead' + ) + ) + }) + it('Verify that pool options are checked', async () => { let pool = new FixedThreadPool( numberOfWorkers, @@ -224,7 +245,7 @@ describe('Abstract pool test suite', () => { ).toThrowError('Invalid worker tasks concurrency: must be an integer') }) - it('Verify that worker choice strategy options can be set', async () => { + it('Verify that pool worker choice strategy options can be set', async () => { const pool = new FixedThreadPool( numberOfWorkers, './tests/worker-files/thread/testWorker.js', @@ -348,7 +369,7 @@ describe('Abstract pool test suite', () => { await pool.destroy() }) - it('Verify that tasks queue can be enabled/disabled', async () => { + it('Verify that pool tasks queue can be enabled/disabled', async () => { const pool = new FixedThreadPool( numberOfWorkers, './tests/worker-files/thread/testWorker.js' @@ -367,7 +388,7 @@ describe('Abstract pool test suite', () => { await pool.destroy() }) - it('Verify that tasks queue options can be set', async () => { + it('Verify that pool tasks queue options can be set', async () => { const pool = new FixedThreadPool( numberOfWorkers, './tests/worker-files/thread/testWorker.js', @@ -397,6 +418,8 @@ describe('Abstract pool test suite', () => { version, type: PoolTypes.fixed, worker: WorkerTypes.thread, + ready: false, + strategy: WorkerChoiceStrategies.ROUND_ROBIN, minSize: numberOfWorkers, maxSize: numberOfWorkers, workerNodes: numberOfWorkers, @@ -410,18 +433,20 @@ describe('Abstract pool test suite', () => { }) await pool.destroy() pool = new DynamicClusterPool( + Math.floor(numberOfWorkers / 2), numberOfWorkers, - numberOfWorkers * 2, './tests/worker-files/cluster/testWorker.js' ) expect(pool.info).toStrictEqual({ version, type: PoolTypes.dynamic, worker: WorkerTypes.cluster, - minSize: numberOfWorkers, - maxSize: numberOfWorkers * 2, - workerNodes: numberOfWorkers, - idleWorkerNodes: numberOfWorkers, + ready: false, + strategy: WorkerChoiceStrategies.ROUND_ROBIN, + minSize: Math.floor(numberOfWorkers / 2), + maxSize: numberOfWorkers, + workerNodes: Math.floor(numberOfWorkers / 2), + idleWorkerNodes: Math.floor(numberOfWorkers / 2), busyWorkerNodes: 0, executedTasks: 0, executingTasks: 0, @@ -447,7 +472,7 @@ describe('Abstract pool test suite', () => { await pool.destroy() }) - it('Verify that worker pool tasks usage are initialized', async () => { + it('Verify that pool worker tasks usage are initialized', async () => { const pool = new FixedClusterPool( numberOfWorkers, './tests/worker-files/cluster/testWorker.js' @@ -480,8 +505,8 @@ describe('Abstract pool test suite', () => { await pool.destroy() }) - it('Verify that worker pool tasks queue are initialized', async () => { - const pool = new FixedClusterPool( + it('Verify that pool worker tasks queue are initialized', async () => { + let pool = new FixedClusterPool( numberOfWorkers, './tests/worker-files/cluster/testWorker.js' ) @@ -492,9 +517,49 @@ describe('Abstract pool test suite', () => { expect(workerNode.tasksQueue.maxSize).toBe(0) } await pool.destroy() + pool = new DynamicThreadPool( + Math.floor(numberOfWorkers / 2), + numberOfWorkers, + './tests/worker-files/thread/testWorker.js' + ) + for (const workerNode of pool.workerNodes) { + expect(workerNode.tasksQueue).toBeDefined() + expect(workerNode.tasksQueue).toBeInstanceOf(Queue) + expect(workerNode.tasksQueue.size).toBe(0) + expect(workerNode.tasksQueue.maxSize).toBe(0) + } + }) + + it('Verify that pool worker info are initialized', async () => { + let pool = new FixedClusterPool( + numberOfWorkers, + './tests/worker-files/cluster/testWorker.js' + ) + for (const workerNode of pool.workerNodes) { + expect(workerNode.info).toStrictEqual({ + id: expect.any(Number), + type: WorkerTypes.cluster, + dynamic: false, + ready: false + }) + } + await pool.destroy() + pool = new DynamicThreadPool( + Math.floor(numberOfWorkers / 2), + numberOfWorkers, + './tests/worker-files/thread/testWorker.js' + ) + for (const workerNode of pool.workerNodes) { + expect(workerNode.info).toStrictEqual({ + id: expect.any(Number), + type: WorkerTypes.thread, + dynamic: false, + ready: false + }) + } }) - it('Verify that worker pool tasks usage are computed', async () => { + it('Verify that pool worker tasks usage are computed', async () => { const pool = new FixedClusterPool( numberOfWorkers, './tests/worker-files/cluster/testWorker.js' @@ -558,9 +623,9 @@ describe('Abstract pool test suite', () => { await pool.destroy() }) - it('Verify that worker pool tasks usage are reset at worker choice strategy change', async () => { + it('Verify that pool worker tasks usage are reset at worker choice strategy change', async () => { const pool = new DynamicThreadPool( - numberOfWorkers, + Math.floor(numberOfWorkers / 2), numberOfWorkers, './tests/worker-files/thread/testWorker.js' ) @@ -630,7 +695,7 @@ describe('Abstract pool test suite', () => { it("Verify that pool event emitter 'full' event can register a callback", async () => { const pool = new DynamicThreadPool( - numberOfWorkers, + Math.floor(numberOfWorkers / 2), numberOfWorkers, './tests/worker-files/thread/testWorker.js' ) @@ -645,13 +710,48 @@ describe('Abstract pool test suite', () => { promises.add(pool.execute()) } await Promise.all(promises) - // The `full` event is triggered when the number of submitted tasks at once reach the max number of workers in the dynamic pool. - // So in total numberOfWorkers * 2 times for a loop submitting up to numberOfWorkers * 2 tasks to the dynamic pool with min = max = numberOfWorkers. - expect(poolFull).toBe(numberOfWorkers * 2) + // The `full` event is triggered when the number of submitted tasks at once reach the maximum number of workers in the dynamic pool. + // So in total numberOfWorkers * 2 - 1 times for a loop submitting up to numberOfWorkers * 2 tasks to the dynamic pool with min = (max = numberOfWorkers) / 2. + expect(poolFull).toBe(numberOfWorkers * 2 - 1) expect(poolInfo).toStrictEqual({ version, type: PoolTypes.dynamic, worker: WorkerTypes.thread, + ready: expect.any(Boolean), + strategy: WorkerChoiceStrategies.ROUND_ROBIN, + minSize: expect.any(Number), + maxSize: expect.any(Number), + workerNodes: expect.any(Number), + idleWorkerNodes: expect.any(Number), + busyWorkerNodes: expect.any(Number), + executedTasks: expect.any(Number), + executingTasks: expect.any(Number), + queuedTasks: expect.any(Number), + maxQueuedTasks: expect.any(Number), + failedTasks: expect.any(Number) + }) + await pool.destroy() + }) + + it("Verify that pool event emitter 'ready' event can register a callback", async () => { + const pool = new FixedClusterPool( + numberOfWorkers, + './tests/worker-files/cluster/testWorker.js' + ) + let poolReady = 0 + let poolInfo + pool.emitter.on(PoolEvents.ready, info => { + ++poolReady + poolInfo = info + }) + await waitPoolEvents(pool, PoolEvents.ready, 1) + expect(poolReady).toBe(1) + expect(poolInfo).toStrictEqual({ + version, + type: PoolTypes.fixed, + worker: WorkerTypes.cluster, + ready: true, + strategy: WorkerChoiceStrategies.ROUND_ROBIN, minSize: expect.any(Number), maxSize: expect.any(Number), workerNodes: expect.any(Number), @@ -689,6 +789,8 @@ describe('Abstract pool test suite', () => { version, type: PoolTypes.fixed, worker: WorkerTypes.thread, + ready: expect.any(Boolean), + strategy: WorkerChoiceStrategies.ROUND_ROBIN, minSize: expect.any(Number), maxSize: expect.any(Number), workerNodes: expect.any(Number), @@ -705,8 +807,8 @@ describe('Abstract pool test suite', () => { it('Verify that multiple tasks worker is working', async () => { const pool = new DynamicClusterPool( + Math.floor(numberOfWorkers / 2), numberOfWorkers, - numberOfWorkers * 2, './tests/worker-files/cluster/testMultiTasksWorker.js' ) const data = { n: 10 } diff --git a/tests/pools/cluster/fixed.test.js b/tests/pools/cluster/fixed.test.js index a0331317..e0ad0ccc 100644 --- a/tests/pools/cluster/fixed.test.js +++ b/tests/pools/cluster/fixed.test.js @@ -239,6 +239,6 @@ describe('Fixed cluster pool test suite', () => { expect( () => new FixedClusterPool(0, './tests/worker-files/cluster/testWorker.js') - ).toThrowError('Cannot instantiate a fixed pool with no worker') + ).toThrowError('Cannot instantiate a fixed pool with zero worker') }) }) diff --git a/tests/pools/thread/fixed.test.js b/tests/pools/thread/fixed.test.js index a94745e3..4ffc3547 100644 --- a/tests/pools/thread/fixed.test.js +++ b/tests/pools/thread/fixed.test.js @@ -237,6 +237,6 @@ describe('Fixed thread pool test suite', () => { it('Verify that a pool with zero worker fails', async () => { expect( () => new FixedThreadPool(0, './tests/worker-files/thread/testWorker.js') - ).toThrowError('Cannot instantiate a fixed pool with no worker') + ).toThrowError('Cannot instantiate a fixed pool with zero worker') }) }) diff --git a/tests/worker/abstract-worker.test.js b/tests/worker/abstract-worker.test.js index e5bf4c81..bf3dc28e 100644 --- a/tests/worker/abstract-worker.test.js +++ b/tests/worker/abstract-worker.test.js @@ -105,10 +105,13 @@ describe('Abstract worker test suite', () => { expect(typeof worker.taskFunctions.get('fn2') === 'function').toBe(true) }) - it('Verify that handleError() method is working properly', () => { - const error = new Error('My error') - const worker = new ThreadWorker(() => {}) - expect(worker.handleError(error)).toStrictEqual(error) + it('Verify that handleError() method works properly', () => { + const error = new Error('Error as an error') + const worker = new ClusterWorker(() => {}) + expect(worker.handleError(error)).not.toBeInstanceOf(Error) + expect(worker.handleError(error)).toStrictEqual(error.message) + const errorMessage = 'Error as a string' + expect(worker.handleError(errorMessage)).toStrictEqual(errorMessage) }) it('Verify that getMainWorker() throw error if main worker is not set', () => { diff --git a/tests/worker/cluster-worker.test.js b/tests/worker/cluster-worker.test.js index 2d9104bb..085e07a3 100644 --- a/tests/worker/cluster-worker.test.js +++ b/tests/worker/cluster-worker.test.js @@ -17,13 +17,7 @@ describe('Cluster worker test suite', () => { expect(worker.opts.maxInactiveTime).toStrictEqual(60000) }) - it('Verify that handleError() method works properly', () => { - const errorMessage = 'Error as a string' - const worker = new ClusterWorker(() => {}) - expect(worker.handleError(errorMessage)).toStrictEqual(errorMessage) - }) - - it('Verify worker invoke the getMainWorker() and send() methods', () => { + it('Verify worker invokes the getMainWorker() and send() methods', () => { const worker = new SpyWorker(() => {}) worker.sendToMainWorker({ ok: 1 }) expect(numberOfMessagesSent).toBe(1) diff --git a/tests/worker/thread-worker.test.js b/tests/worker/thread-worker.test.js index ff3cbff3..c08adffd 100644 --- a/tests/worker/thread-worker.test.js +++ b/tests/worker/thread-worker.test.js @@ -17,7 +17,15 @@ describe('Thread worker test suite', () => { expect(worker.opts.maxInactiveTime).toStrictEqual(60000) }) - it('Verify worker invoke the getMainWorker() and postMessage() methods', () => { + it('Verify that handleError() method is working properly', () => { + const error = new Error('Error as an error') + const worker = new ThreadWorker(() => {}) + expect(worker.handleError(error)).toStrictEqual(error) + const errorMessage = 'Error as a string' + expect(worker.handleError(errorMessage)).toStrictEqual(errorMessage) + }) + + it('Verify worker invokes the getMainWorker() and postMessage() methods', () => { const worker = new SpyWorker(() => {}) worker.sendToMainWorker({ ok: 1 }) expect(numberOfMessagesPosted).toBe(1) -- 2.34.1