test: add test for worker task function object
[poolifier.git] / tests / pools / selection-strategies / worker-choice-strategies-context.test.mjs
1 import { expect } from 'expect'
2 import { createStubInstance, restore, stub } from 'sinon'
3
4 import {
5 DynamicThreadPool,
6 FixedThreadPool,
7 WorkerChoiceStrategies
8 } from '../../../lib/index.cjs'
9 import { FairShareWorkerChoiceStrategy } from '../../../lib/pools/selection-strategies/fair-share-worker-choice-strategy.cjs'
10 import { InterleavedWeightedRoundRobinWorkerChoiceStrategy } from '../../../lib/pools/selection-strategies/interleaved-weighted-round-robin-worker-choice-strategy.cjs'
11 import { LeastBusyWorkerChoiceStrategy } from '../../../lib/pools/selection-strategies/least-busy-worker-choice-strategy.cjs'
12 import { LeastEluWorkerChoiceStrategy } from '../../../lib/pools/selection-strategies/least-elu-worker-choice-strategy.cjs'
13 import { LeastUsedWorkerChoiceStrategy } from '../../../lib/pools/selection-strategies/least-used-worker-choice-strategy.cjs'
14 import { RoundRobinWorkerChoiceStrategy } from '../../../lib/pools/selection-strategies/round-robin-worker-choice-strategy.cjs'
15 import { WeightedRoundRobinWorkerChoiceStrategy } from '../../../lib/pools/selection-strategies/weighted-round-robin-worker-choice-strategy.cjs'
16 import { WorkerChoiceStrategiesContext } from '../../../lib/pools/selection-strategies/worker-choice-strategies-context.cjs'
17
18 describe('Worker choice strategies context test suite', () => {
19 const min = 1
20 const max = 3
21 let fixedPool, dynamicPool
22
23 before(() => {
24 fixedPool = new FixedThreadPool(
25 max,
26 './tests/worker-files/thread/testWorker.mjs'
27 )
28 dynamicPool = new DynamicThreadPool(
29 min,
30 max,
31 './tests/worker-files/thread/testWorker.mjs'
32 )
33 })
34
35 afterEach(() => {
36 restore()
37 })
38
39 after(async () => {
40 await fixedPool.destroy()
41 await dynamicPool.destroy()
42 })
43
44 it('Verify that constructor() initializes the context with the default choice strategy', () => {
45 let workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
46 fixedPool
47 )
48 expect(workerChoiceStrategiesContext.workerChoiceStrategies.size).toBe(1)
49 expect(
50 workerChoiceStrategiesContext.workerChoiceStrategies.get(
51 workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
52 )
53 ).toBeInstanceOf(RoundRobinWorkerChoiceStrategy)
54 workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
55 dynamicPool
56 )
57 expect(workerChoiceStrategiesContext.workerChoiceStrategies.size).toBe(1)
58 expect(
59 workerChoiceStrategiesContext.workerChoiceStrategies.get(
60 workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
61 )
62 ).toBeInstanceOf(RoundRobinWorkerChoiceStrategy)
63 })
64
65 it('Verify that constructor() initializes the context with retries attribute properly set', () => {
66 let workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
67 fixedPool
68 )
69 expect(workerChoiceStrategiesContext.retries).toBe(
70 fixedPool.info.maxSize * 2
71 )
72 workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
73 dynamicPool
74 )
75 expect(workerChoiceStrategiesContext.retries).toBe(
76 dynamicPool.info.maxSize * 2
77 )
78 })
79
80 it('Verify that execute() throws error if null or undefined is returned after retries', () => {
81 const workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
82 fixedPool
83 )
84 expect(workerChoiceStrategiesContext.defaultWorkerChoiceStrategy).toBe(
85 WorkerChoiceStrategies.ROUND_ROBIN
86 )
87 const workerChoiceStrategyUndefinedStub = createStubInstance(
88 RoundRobinWorkerChoiceStrategy,
89 {
90 choose: stub().returns(undefined)
91 }
92 )
93 workerChoiceStrategiesContext.workerChoiceStrategies.set(
94 workerChoiceStrategiesContext.defaultWorkerChoiceStrategy,
95 workerChoiceStrategyUndefinedStub
96 )
97 expect(() => workerChoiceStrategiesContext.execute()).toThrow(
98 new Error(
99 `Worker node key chosen is null or undefined after ${workerChoiceStrategiesContext.retries} retries`
100 )
101 )
102 const workerChoiceStrategyNullStub = createStubInstance(
103 RoundRobinWorkerChoiceStrategy,
104 {
105 choose: stub().returns(null)
106 }
107 )
108 workerChoiceStrategiesContext.workerChoiceStrategies.set(
109 workerChoiceStrategiesContext.defaultWorkerChoiceStrategy,
110 workerChoiceStrategyNullStub
111 )
112 expect(() => workerChoiceStrategiesContext.execute()).toThrow(
113 new Error(
114 `Worker node key chosen is null or undefined after ${workerChoiceStrategiesContext.retries} retries`
115 )
116 )
117 })
118
119 it('Verify that execute() retry until a worker node is chosen', () => {
120 const workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
121 fixedPool
122 )
123 const workerChoiceStrategyStub = createStubInstance(
124 RoundRobinWorkerChoiceStrategy,
125 {
126 choose: stub()
127 .onCall(0)
128 .returns(undefined)
129 .onCall(1)
130 .returns(undefined)
131 .onCall(2)
132 .returns(undefined)
133 .onCall(3)
134 .returns(undefined)
135 .onCall(4)
136 .returns(undefined)
137 .returns(1)
138 }
139 )
140 expect(workerChoiceStrategiesContext.defaultWorkerChoiceStrategy).toBe(
141 WorkerChoiceStrategies.ROUND_ROBIN
142 )
143 workerChoiceStrategiesContext.workerChoiceStrategies.set(
144 workerChoiceStrategiesContext.defaultWorkerChoiceStrategy,
145 workerChoiceStrategyStub
146 )
147 const chosenWorkerKey = workerChoiceStrategiesContext.execute()
148 expect(
149 workerChoiceStrategiesContext.workerChoiceStrategies.get(
150 workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
151 ).choose.callCount
152 ).toBe(6)
153 expect(chosenWorkerKey).toBe(1)
154 })
155
156 it('Verify that execute() return the worker node key chosen by the strategy with fixed pool', () => {
157 const workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
158 fixedPool
159 )
160 const workerChoiceStrategyStub = createStubInstance(
161 RoundRobinWorkerChoiceStrategy,
162 {
163 choose: stub().returns(0)
164 }
165 )
166 expect(workerChoiceStrategiesContext.defaultWorkerChoiceStrategy).toBe(
167 WorkerChoiceStrategies.ROUND_ROBIN
168 )
169 workerChoiceStrategiesContext.workerChoiceStrategies.set(
170 workerChoiceStrategiesContext.defaultWorkerChoiceStrategy,
171 workerChoiceStrategyStub
172 )
173 const chosenWorkerKey = workerChoiceStrategiesContext.execute()
174 expect(
175 workerChoiceStrategiesContext.workerChoiceStrategies.get(
176 workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
177 ).choose.calledOnce
178 ).toBe(true)
179 expect(chosenWorkerKey).toBe(0)
180 })
181
182 it('Verify that execute() return the worker node key chosen by the strategy with dynamic pool', () => {
183 const workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
184 dynamicPool
185 )
186 const workerChoiceStrategyStub = createStubInstance(
187 RoundRobinWorkerChoiceStrategy,
188 {
189 choose: stub().returns(0)
190 }
191 )
192 expect(workerChoiceStrategiesContext.defaultWorkerChoiceStrategy).toBe(
193 WorkerChoiceStrategies.ROUND_ROBIN
194 )
195 workerChoiceStrategiesContext.workerChoiceStrategies.set(
196 workerChoiceStrategiesContext.defaultWorkerChoiceStrategy,
197 workerChoiceStrategyStub
198 )
199 const chosenWorkerKey = workerChoiceStrategiesContext.execute()
200 expect(
201 workerChoiceStrategiesContext.workerChoiceStrategies.get(
202 workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
203 ).choose.calledOnce
204 ).toBe(true)
205 expect(chosenWorkerKey).toBe(0)
206 })
207
208 it('Verify that setDefaultWorkerChoiceStrategy() works with ROUND_ROBIN and fixed pool', () => {
209 const workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
210 fixedPool
211 )
212 expect(
213 workerChoiceStrategiesContext.workerChoiceStrategies.get(
214 workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
215 )
216 ).toBeInstanceOf(RoundRobinWorkerChoiceStrategy)
217 workerChoiceStrategiesContext.setDefaultWorkerChoiceStrategy(
218 WorkerChoiceStrategies.ROUND_ROBIN
219 )
220 expect(
221 workerChoiceStrategiesContext.workerChoiceStrategies.get(
222 workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
223 )
224 ).toBeInstanceOf(RoundRobinWorkerChoiceStrategy)
225 })
226
227 it('Verify that setDefaultWorkerChoiceStrategy() works with ROUND_ROBIN and dynamic pool', () => {
228 const workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
229 dynamicPool
230 )
231 expect(
232 workerChoiceStrategiesContext.workerChoiceStrategies.get(
233 workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
234 )
235 ).toBeInstanceOf(RoundRobinWorkerChoiceStrategy)
236 workerChoiceStrategiesContext.setDefaultWorkerChoiceStrategy(
237 WorkerChoiceStrategies.ROUND_ROBIN
238 )
239 expect(
240 workerChoiceStrategiesContext.workerChoiceStrategies.get(
241 workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
242 )
243 ).toBeInstanceOf(RoundRobinWorkerChoiceStrategy)
244 })
245
246 it('Verify that setDefaultWorkerChoiceStrategy() works with LEAST_USED and fixed pool', () => {
247 const workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
248 fixedPool
249 )
250 workerChoiceStrategiesContext.setDefaultWorkerChoiceStrategy(
251 WorkerChoiceStrategies.LEAST_USED
252 )
253 expect(
254 workerChoiceStrategiesContext.workerChoiceStrategies.get(
255 workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
256 )
257 ).toBeInstanceOf(LeastUsedWorkerChoiceStrategy)
258 })
259
260 it('Verify that setDefaultWorkerChoiceStrategy() works with LEAST_USED and dynamic pool', () => {
261 const workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
262 dynamicPool
263 )
264 workerChoiceStrategiesContext.setDefaultWorkerChoiceStrategy(
265 WorkerChoiceStrategies.LEAST_USED
266 )
267 expect(
268 workerChoiceStrategiesContext.workerChoiceStrategies.get(
269 workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
270 )
271 ).toBeInstanceOf(LeastUsedWorkerChoiceStrategy)
272 })
273
274 it('Verify that setDefaultWorkerChoiceStrategy() works with LEAST_BUSY and fixed pool', () => {
275 const workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
276 fixedPool
277 )
278 workerChoiceStrategiesContext.setDefaultWorkerChoiceStrategy(
279 WorkerChoiceStrategies.LEAST_BUSY
280 )
281 expect(
282 workerChoiceStrategiesContext.workerChoiceStrategies.get(
283 workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
284 )
285 ).toBeInstanceOf(LeastBusyWorkerChoiceStrategy)
286 })
287
288 it('Verify that setDefaultWorkerChoiceStrategy() works with LEAST_BUSY and dynamic pool', () => {
289 const workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
290 dynamicPool
291 )
292 workerChoiceStrategiesContext.setDefaultWorkerChoiceStrategy(
293 WorkerChoiceStrategies.LEAST_BUSY
294 )
295 expect(
296 workerChoiceStrategiesContext.workerChoiceStrategies.get(
297 workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
298 )
299 ).toBeInstanceOf(LeastBusyWorkerChoiceStrategy)
300 })
301
302 it('Verify that setDefaultWorkerChoiceStrategy() works with LEAST_ELU and fixed pool', () => {
303 const workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
304 fixedPool
305 )
306 workerChoiceStrategiesContext.setDefaultWorkerChoiceStrategy(
307 WorkerChoiceStrategies.LEAST_ELU
308 )
309 expect(
310 workerChoiceStrategiesContext.workerChoiceStrategies.get(
311 workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
312 )
313 ).toBeInstanceOf(LeastEluWorkerChoiceStrategy)
314 })
315
316 it('Verify that setDefaultWorkerChoiceStrategy() works with LEAST_ELU and dynamic pool', () => {
317 const workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
318 dynamicPool
319 )
320 workerChoiceStrategiesContext.setDefaultWorkerChoiceStrategy(
321 WorkerChoiceStrategies.LEAST_ELU
322 )
323 expect(
324 workerChoiceStrategiesContext.workerChoiceStrategies.get(
325 workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
326 )
327 ).toBeInstanceOf(LeastEluWorkerChoiceStrategy)
328 })
329
330 it('Verify that setDefaultWorkerChoiceStrategy() works with FAIR_SHARE and fixed pool', () => {
331 const workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
332 fixedPool
333 )
334 workerChoiceStrategiesContext.setDefaultWorkerChoiceStrategy(
335 WorkerChoiceStrategies.FAIR_SHARE
336 )
337 expect(
338 workerChoiceStrategiesContext.workerChoiceStrategies.get(
339 workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
340 )
341 ).toBeInstanceOf(FairShareWorkerChoiceStrategy)
342 })
343
344 it('Verify that setDefaultWorkerChoiceStrategy() works with FAIR_SHARE and dynamic pool', () => {
345 const workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
346 dynamicPool
347 )
348 workerChoiceStrategiesContext.setDefaultWorkerChoiceStrategy(
349 WorkerChoiceStrategies.FAIR_SHARE
350 )
351 expect(
352 workerChoiceStrategiesContext.workerChoiceStrategies.get(
353 workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
354 )
355 ).toBeInstanceOf(FairShareWorkerChoiceStrategy)
356 })
357
358 it('Verify that setDefaultWorkerChoiceStrategy() works with WEIGHTED_ROUND_ROBIN and fixed pool', () => {
359 const workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
360 fixedPool
361 )
362 workerChoiceStrategiesContext.setDefaultWorkerChoiceStrategy(
363 WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
364 )
365 expect(
366 workerChoiceStrategiesContext.workerChoiceStrategies.get(
367 workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
368 )
369 ).toBeInstanceOf(WeightedRoundRobinWorkerChoiceStrategy)
370 })
371
372 it('Verify that setDefaultWorkerChoiceStrategy() works with WEIGHTED_ROUND_ROBIN and dynamic pool', () => {
373 const workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
374 dynamicPool
375 )
376 workerChoiceStrategiesContext.setDefaultWorkerChoiceStrategy(
377 WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
378 )
379 expect(
380 workerChoiceStrategiesContext.workerChoiceStrategies.get(
381 workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
382 )
383 ).toBeInstanceOf(WeightedRoundRobinWorkerChoiceStrategy)
384 })
385
386 it('Verify that setDefaultWorkerChoiceStrategy() works with INTERLEAVED_WEIGHTED_ROUND_ROBIN and fixed pool', () => {
387 const workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
388 fixedPool
389 )
390 workerChoiceStrategiesContext.setDefaultWorkerChoiceStrategy(
391 WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN
392 )
393 expect(
394 workerChoiceStrategiesContext.workerChoiceStrategies.get(
395 workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
396 )
397 ).toBeInstanceOf(InterleavedWeightedRoundRobinWorkerChoiceStrategy)
398 })
399
400 it('Verify that setDefaultWorkerChoiceStrategy() works with INTERLEAVED_WEIGHTED_ROUND_ROBIN and dynamic pool', () => {
401 const workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
402 dynamicPool
403 )
404 workerChoiceStrategiesContext.setDefaultWorkerChoiceStrategy(
405 WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN
406 )
407 expect(
408 workerChoiceStrategiesContext.workerChoiceStrategies.get(
409 workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
410 )
411 ).toBeInstanceOf(InterleavedWeightedRoundRobinWorkerChoiceStrategy)
412 })
413
414 it('Verify that worker choice strategy options enable median runtime pool statistics', () => {
415 const wwrWorkerChoiceStrategy = WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
416 let workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
417 fixedPool,
418 [wwrWorkerChoiceStrategy],
419 {
420 runTime: { median: true }
421 }
422 )
423 expect(
424 workerChoiceStrategiesContext.getTaskStatisticsRequirements().runTime
425 .average
426 ).toBe(false)
427 expect(
428 workerChoiceStrategiesContext.getTaskStatisticsRequirements().runTime
429 .median
430 ).toBe(true)
431 workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
432 dynamicPool,
433 [wwrWorkerChoiceStrategy],
434 {
435 runTime: { median: true }
436 }
437 )
438 expect(
439 workerChoiceStrategiesContext.getTaskStatisticsRequirements().runTime
440 .average
441 ).toBe(false)
442 expect(
443 workerChoiceStrategiesContext.getTaskStatisticsRequirements().runTime
444 .median
445 ).toBe(true)
446 const fsWorkerChoiceStrategy = WorkerChoiceStrategies.FAIR_SHARE
447 workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
448 fixedPool,
449 [fsWorkerChoiceStrategy],
450 {
451 runTime: { median: true }
452 }
453 )
454 expect(
455 workerChoiceStrategiesContext.getTaskStatisticsRequirements().runTime
456 .average
457 ).toBe(false)
458 expect(
459 workerChoiceStrategiesContext.getTaskStatisticsRequirements().runTime
460 .median
461 ).toBe(true)
462 workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
463 dynamicPool,
464 [fsWorkerChoiceStrategy],
465 {
466 runTime: { median: true }
467 }
468 )
469 expect(
470 workerChoiceStrategiesContext.getTaskStatisticsRequirements().runTime
471 .average
472 ).toBe(false)
473 expect(
474 workerChoiceStrategiesContext.getTaskStatisticsRequirements().runTime
475 .median
476 ).toBe(true)
477 })
478 })