refactor: factor out measurement statistics requirements default
[poolifier.git] / tests / pools / cluster / fixed.test.js
CommitLineData
a61a0724 1const { expect } = require('expect')
cdace0e5 2const { FixedClusterPool, PoolEvents } = require('../../../lib')
2d2e32c2 3const { WorkerFunctions } = require('../../test-types')
85a3f8a7 4const TestUtils = require('../../test-utils')
325f50bc 5
a35560ba 6describe('Fixed cluster pool test suite', () => {
e1ffb94f
JB
7 const numberOfWorkers = 6
8 const pool = new FixedClusterPool(
9 numberOfWorkers,
10 './tests/worker-files/cluster/testWorker.js',
11 {
12 errorHandler: e => console.error(e)
13 }
14 )
594bfb84
JB
15 const queuePool = new FixedClusterPool(
16 numberOfWorkers,
17 './tests/worker-files/cluster/testWorker.js',
18 {
19 enableTasksQueue: true,
20 tasksQueueOptions: {
21 concurrency: 2
22 },
23 errorHandler: e => console.error(e)
24 }
25 )
e1ffb94f
JB
26 const emptyPool = new FixedClusterPool(
27 numberOfWorkers,
28 './tests/worker-files/cluster/emptyWorker.js',
29 { exitHandler: () => console.log('empty pool worker exited') }
30 )
31 const echoPool = new FixedClusterPool(
32 numberOfWorkers,
33 './tests/worker-files/cluster/echoWorker.js'
34 )
35 const errorPool = new FixedClusterPool(
36 numberOfWorkers,
37 './tests/worker-files/cluster/errorWorker.js',
38 {
39 errorHandler: e => console.error(e)
40 }
41 )
42 const asyncErrorPool = new FixedClusterPool(
43 numberOfWorkers,
44 './tests/worker-files/cluster/asyncErrorWorker.js',
45 {
46 errorHandler: e => console.error(e)
47 }
48 )
49 const asyncPool = new FixedClusterPool(
50 numberOfWorkers,
51 './tests/worker-files/cluster/asyncWorker.js'
52 )
53
8bc77620
APA
54 after('Destroy all pools', async () => {
55 // We need to clean up the resources after our test
56 await echoPool.destroy()
57 await asyncPool.destroy()
58 await errorPool.destroy()
59 await asyncErrorPool.destroy()
60 await emptyPool.destroy()
594bfb84 61 await queuePool.destroy()
8bc77620
APA
62 })
63
325f50bc 64 it('Verify that the function is executed in a worker cluster', async () => {
6db75ad9
JB
65 let result = await pool.execute({
66 function: WorkerFunctions.fibonacci
67 })
024daf59 68 expect(result).toBe(75025)
6db75ad9
JB
69 result = await pool.execute({
70 function: WorkerFunctions.factorial
71 })
70a4f5ea 72 expect(result).toBe(9.33262154439441e157)
325f50bc
S
73 })
74
318d4156 75 it('Verify that is possible to invoke the execute() method without input', async () => {
325f50bc 76 const result = await pool.execute()
6db75ad9 77 expect(result).toBe(false)
325f50bc
S
78 })
79
aee46736 80 it("Verify that 'busy' event is emitted", async () => {
7c0ba920 81 let poolBusy = 0
aee46736 82 pool.emitter.on(PoolEvents.busy, () => ++poolBusy)
7c0ba920 83 for (let i = 0; i < numberOfWorkers * 2; i++) {
8cbb82eb 84 pool.execute()
7c0ba920 85 }
14916bf9
JB
86 // The `busy` event is triggered when the number of submitted tasks at once reach the number of fixed pool workers.
87 // So in total numberOfWorkers + 1 times for a loop submitting up to numberOfWorkers * 2 tasks to the fixed pool.
88 expect(poolBusy).toBe(numberOfWorkers + 1)
7c0ba920
JB
89 })
90
594bfb84 91 it('Verify that tasks queuing is working', async () => {
d3d4b67d 92 const promises = new Set()
ee9f5295 93 const maxMultiplier = 2
594bfb84 94 for (let i = 0; i < numberOfWorkers * maxMultiplier; i++) {
d3d4b67d 95 promises.add(queuePool.execute())
594bfb84 96 }
d3d4b67d 97 expect(promises.size).toBe(numberOfWorkers * maxMultiplier)
594bfb84 98 for (const workerNode of queuePool.workerNodes) {
a4e07f72 99 expect(workerNode.workerUsage.tasks.executing).toBeLessThanOrEqual(
594bfb84
JB
100 queuePool.opts.tasksQueueOptions.concurrency
101 )
a4e07f72 102 expect(workerNode.workerUsage.tasks.executed).toBe(0)
9c16fb4b 103 expect(workerNode.workerUsage.tasks.queued).toBeGreaterThan(0)
df593701 104 expect(workerNode.workerUsage.tasks.maxQueued).toBeGreaterThan(0)
594bfb84 105 }
a4e07f72 106 expect(queuePool.info.executingTasks).toBe(numberOfWorkers)
6b27d407
JB
107 expect(queuePool.info.queuedTasks).toBe(
108 numberOfWorkers * maxMultiplier - numberOfWorkers
109 )
110 expect(queuePool.info.maxQueuedTasks).toBe(
d3d4b67d
JB
111 numberOfWorkers * maxMultiplier - numberOfWorkers
112 )
594bfb84
JB
113 await Promise.all(promises)
114 for (const workerNode of queuePool.workerNodes) {
a4e07f72
JB
115 expect(workerNode.workerUsage.tasks.executing).toBe(0)
116 expect(workerNode.workerUsage.tasks.executed).toBeGreaterThan(0)
117 expect(workerNode.workerUsage.tasks.executed).toBeLessThanOrEqual(
118 maxMultiplier
119 )
9c16fb4b 120 expect(workerNode.workerUsage.tasks.queued).toBe(0)
df593701 121 expect(workerNode.workerUsage.tasks.maxQueued).toBe(1)
594bfb84
JB
122 }
123 })
124
325f50bc
S
125 it('Verify that is possible to have a worker that return undefined', async () => {
126 const result = await emptyPool.execute()
6db75ad9 127 expect(result).toBeUndefined()
325f50bc
S
128 })
129
130 it('Verify that data are sent to the worker correctly', async () => {
131 const data = { f: 10 }
132 const result = await echoPool.execute(data)
e1ffb94f 133 expect(result).toStrictEqual(data)
325f50bc
S
134 })
135
136 it('Verify that error handling is working properly:sync', async () => {
137 const data = { f: 10 }
d46660cd
JB
138 let taskError
139 errorPool.emitter.on(PoolEvents.taskError, e => {
140 taskError = e
141 })
325f50bc
S
142 let inError
143 try {
144 await errorPool.execute(data)
145 } catch (e) {
146 inError = e
147 }
148 expect(inError).toBeDefined()
8620fb25 149 expect(typeof inError === 'string').toBe(true)
325f50bc 150 expect(inError).toBe('Error Message from ClusterWorker')
d46660cd 151 expect(taskError).toStrictEqual({
82f36766
JB
152 message: 'Error Message from ClusterWorker',
153 data
d46660cd 154 })
18482cec
JB
155 expect(
156 errorPool.workerNodes.some(
a4e07f72 157 workerNode => workerNode.workerUsage.tasks.failed === 1
18482cec
JB
158 )
159 ).toBe(true)
325f50bc
S
160 })
161
162 it('Verify that error handling is working properly:async', async () => {
163 const data = { f: 10 }
4f0b85b3
JB
164 let taskError
165 asyncErrorPool.emitter.on(PoolEvents.taskError, e => {
166 taskError = e
167 })
325f50bc
S
168 let inError
169 try {
170 await asyncErrorPool.execute(data)
171 } catch (e) {
172 inError = e
173 }
174 expect(inError).toBeDefined()
8620fb25 175 expect(typeof inError === 'string').toBe(true)
325f50bc 176 expect(inError).toBe('Error Message from ClusterWorker:async')
4f0b85b3 177 expect(taskError).toStrictEqual({
82f36766
JB
178 message: 'Error Message from ClusterWorker:async',
179 data
4f0b85b3 180 })
18482cec
JB
181 expect(
182 asyncErrorPool.workerNodes.some(
a4e07f72 183 workerNode => workerNode.workerUsage.tasks.failed === 1
18482cec
JB
184 )
185 ).toBe(true)
325f50bc
S
186 })
187
188 it('Verify that async function is working properly', async () => {
189 const data = { f: 10 }
15e5141f 190 const startTime = performance.now()
325f50bc 191 const result = await asyncPool.execute(data)
15e5141f 192 const usedTime = performance.now() - startTime
e1ffb94f 193 expect(result).toStrictEqual(data)
325f50bc
S
194 expect(usedTime).toBeGreaterThanOrEqual(2000)
195 })
196
197 it('Shutdown test', async () => {
2c039e43
JB
198 const exitPromise = TestUtils.waitWorkerEvents(
199 pool,
200 'exit',
201 numberOfWorkers
202 )
45dbbb14 203 await pool.destroy()
bdacc2d2
JB
204 const numberOfExitEvents = await exitPromise
205 expect(numberOfExitEvents).toBe(numberOfWorkers)
325f50bc
S
206 })
207
1a76932b
JB
208 it('Verify that cluster pool options are checked', async () => {
209 const workerFilePath = './tests/worker-files/cluster/testWorker.js'
210 let pool1 = new FixedClusterPool(numberOfWorkers, workerFilePath)
211 expect(pool1.opts.env).toBeUndefined()
212 expect(pool1.opts.settings).toBeUndefined()
213 await pool1.destroy()
214 pool1 = new FixedClusterPool(numberOfWorkers, workerFilePath, {
215 env: { TEST: 'test' },
216 settings: { args: ['--use', 'http'], silent: true }
217 })
218 expect(pool1.opts.env).toStrictEqual({ TEST: 'test' })
219 expect(pool1.opts.settings).toStrictEqual({
220 args: ['--use', 'http'],
221 silent: true
222 })
223 expect({ ...pool1.opts.settings, exec: workerFilePath }).toStrictEqual({
224 args: ['--use', 'http'],
225 silent: true,
226 exec: workerFilePath
227 })
228 await pool1.destroy()
229 })
230
325f50bc
S
231 it('Should work even without opts in input', async () => {
232 const pool1 = new FixedClusterPool(
e1ffb94f 233 numberOfWorkers,
76b1e974 234 './tests/worker-files/cluster/testWorker.js'
325f50bc 235 )
6db75ad9
JB
236 const res = await pool1.execute()
237 expect(res).toBe(false)
8bc77620
APA
238 // We need to clean up the resources after our test
239 await pool1.destroy()
325f50bc 240 })
8d3782fa
JB
241
242 it('Verify that a pool with zero worker fails', async () => {
243 expect(
244 () =>
245 new FixedClusterPool(0, './tests/worker-files/cluster/testWorker.js')
d4aeae5a 246 ).toThrowError('Cannot instantiate a fixed pool with no worker')
8d3782fa 247 })
325f50bc 248})