feat: add pool and worker readyness tracking infrastructure
authorJérôme Benoit <jerome.benoit@sap.com>
Sat, 8 Jul 2023 19:05:52 +0000 (21:05 +0200)
committerJérôme Benoit <jerome.benoit@sap.com>
Sat, 8 Jul 2023 19:05:52 +0000 (21:05 +0200)
Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
16 files changed:
CHANGELOG.md
README.md
examples/dynamicExample.js
examples/fixedExample.js
src/pools/abstract-pool.ts
src/pools/cluster/dynamic.ts
src/pools/pool.ts
src/pools/thread/dynamic.ts
src/pools/worker-node.ts
src/worker/abstract-worker.ts
tests/pools/abstract/abstract-pool.test.js
tests/pools/cluster/fixed.test.js
tests/pools/thread/fixed.test.js
tests/worker/abstract-worker.test.js
tests/worker/cluster-worker.test.js
tests/worker/thread-worker.test.js

index bdfa3268f2414fb122f9c2dedacc8be95dbfa3fb..bc877031fcca1413f98776103e2b3ada229da7e2 100644 (file)
@@ -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
index 3ebb9b7cca227268674759371678851afec438e6..e6d676ecdb28a27141de20c1ea08acfd4d064c7f 100644 (file)
--- 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,
index bb457af892ff2ff913ecd6317290e016f3ccd63d..f36138ac8085df0c07712f28150616dd7095a6f2 100644 (file)
@@ -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
index 2346f7b4f0b68b207ced0df9ffd86f125e36a1e5..884ab59e33ea44a81241b89983f34f9135d623f7 100644 (file)
@@ -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
index 53bce7d2b7523f6a819cc5fd2dba3cbf5c84cc6a..6c9a0fd185f7c560f4f7fa5ac597ab784f6d6dbd 100644 (file)
@@ -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<void>)
       }
     })
+    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<Response>) => 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<Response>): void {
index a9ccd6977255bcadc43bee150d8684a3ee3ad75e..75296f7a1a894d45b51b65727610f53f361d6fc9 100644 (file)
@@ -31,6 +31,7 @@ export class DynamicClusterPool<
     opts: ClusterPoolOptions = {}
   ) {
     super(min, filePath, opts)
+    this.checkDynamicPoolSize(this.numberOfWorkers, this.max)
   }
 
   /** @inheritDoc */
index b1892752111884b3a9edcf82f8e33a776b34172e..78d298f90f20381d3cb2157d276e32ced109f4a2 100644 (file)
@@ -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.
    */
index b03ca47e27ab272233338e8e4fb751bb8de0d0e4..1d3f3f5d3c64d0d94a367c17f92a6df1d2b4d75f 100644 (file)
@@ -31,6 +31,7 @@ export class DynamicThreadPool<
     opts: ThreadPoolOptions = {}
   ) {
     super(min, filePath, opts)
+    this.checkDynamicPoolSize(this.numberOfWorkers, this.max)
   }
 
   /** @inheritDoc */
index 0a6b8de79c9d47f5b5b8706f4781acd1fabd46ab..ea50ebdbe036353070af773d4470213c10061237 100644 (file)
@@ -75,7 +75,7 @@ implements IWorkerNode<Worker, Data> {
       id: this.getWorkerId(worker, workerType),
       type: workerType,
       dynamic: false,
-      ready: true
+      ready: false
     }
   }
 
index 92d68bcb6e9d4f38fdb3cc87170a038c4f226722..4e92be01ae340da26d4699595ff692566075f006 100644 (file)
@@ -145,7 +145,10 @@ export abstract class AbstractWorker<
    * @param message - Message received.
    */
   protected messageListener (message: MessageValue<Data, Data>): 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.
    */
index 0e31ad5c21927c135ce77d1d69508c7b7061d611..caec34fd47a0ae267f9c6b5dcfe7648a352e1a2a 100644 (file)
@@ -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 }
index a0331317e7d14809e03dd776c96b1c6bcbd08051..e0ad0ccce3078a947473438921efb3464640156d 100644 (file)
@@ -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')
   })
 })
index a94745e397f42655378deebdb6c4245c5d6c3b45..4ffc354771f49e3636172a8dc7d16f65e8a9ed02 100644 (file)
@@ -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')
   })
 })
index e5bf4c81005f94a33cee53bc2f04a10b48549847..bf3dc28e6874cdf91f4714eee90bac11f4de75af 100644 (file)
@@ -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', () => {
index 2d9104bb3c185b52bf05f8ec114d8d7008745583..085e07a34e517e963b1e9a39cea56bb49f4d7cd9 100644 (file)
@@ -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)
index ff3cbff3f53e07d461f101b2e85098b06303aa5d..c08adffd9816a9c0f4bd78337b857a664fa4553f 100644 (file)
@@ -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)