1 import { expect } from 'expect'
2 import { createStubInstance, restore, stub } from 'sinon'
7 } from '../../../lib/index.cjs'
8 import { WorkerChoiceStrategyContext } from '../../../lib/pools/selection-strategies/worker-choice-strategy-context.cjs'
9 import { RoundRobinWorkerChoiceStrategy } from '../../../lib/pools/selection-strategies/round-robin-worker-choice-strategy.cjs'
10 import { LeastUsedWorkerChoiceStrategy } from '../../../lib/pools/selection-strategies/least-used-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 { FairShareWorkerChoiceStrategy } from '../../../lib/pools/selection-strategies/fair-share-worker-choice-strategy.cjs'
14 import { WeightedRoundRobinWorkerChoiceStrategy } from '../../../lib/pools/selection-strategies/weighted-round-robin-worker-choice-strategy.cjs'
15 import { InterleavedWeightedRoundRobinWorkerChoiceStrategy } from '../../../lib/pools/selection-strategies/interleaved-weighted-round-robin-worker-choice-strategy.cjs'
17 describe('Worker choice strategy context test suite', () => {
20 let fixedPool, dynamicPool
23 fixedPool = new FixedThreadPool(
25 './tests/worker-files/thread/testWorker.mjs'
27 dynamicPool = new DynamicThreadPool(
30 './tests/worker-files/thread/testWorker.mjs'
39 await fixedPool.destroy()
40 await dynamicPool.destroy()
43 it('Verify that constructor() initializes the context with all the available worker choice strategies', () => {
44 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
47 expect(workerChoiceStrategyContext.workerChoiceStrategies.size).toBe(
48 Object.keys(WorkerChoiceStrategies).length
52 it('Verify that execute() return the worker node key chosen by the strategy with fixed pool', () => {
53 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
56 const workerChoiceStrategyStub = createStubInstance(
57 RoundRobinWorkerChoiceStrategy,
59 hasPoolWorkerNodesReady: stub().returns(true),
60 choose: stub().returns(0)
63 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
64 WorkerChoiceStrategies.ROUND_ROBIN
66 workerChoiceStrategyContext.workerChoiceStrategies.set(
67 workerChoiceStrategyContext.workerChoiceStrategy,
68 workerChoiceStrategyStub
70 const chosenWorkerKey = workerChoiceStrategyContext.execute()
72 workerChoiceStrategyContext.workerChoiceStrategies.get(
73 workerChoiceStrategyContext.workerChoiceStrategy
76 expect(chosenWorkerKey).toBe(0)
79 it('Verify that execute() throws error if null or undefined is returned after retries', () => {
80 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
83 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
84 WorkerChoiceStrategies.ROUND_ROBIN
86 const workerChoiceStrategyUndefinedStub = createStubInstance(
87 RoundRobinWorkerChoiceStrategy,
89 hasPoolWorkerNodesReady: stub().returns(true),
90 choose: stub().returns(undefined)
93 workerChoiceStrategyContext.workerChoiceStrategies.set(
94 workerChoiceStrategyContext.workerChoiceStrategy,
95 workerChoiceStrategyUndefinedStub
97 expect(() => workerChoiceStrategyContext.execute()).toThrow(
99 `Worker node key chosen is null or undefined after ${
100 fixedPool.info.maxSize +
101 Object.keys(workerChoiceStrategyContext.opts.weights).length
105 const workerChoiceStrategyNullStub = createStubInstance(
106 RoundRobinWorkerChoiceStrategy,
108 hasPoolWorkerNodesReady: stub().returns(true),
109 choose: stub().returns(null)
112 workerChoiceStrategyContext.workerChoiceStrategies.set(
113 workerChoiceStrategyContext.workerChoiceStrategy,
114 workerChoiceStrategyNullStub
116 expect(() => workerChoiceStrategyContext.execute()).toThrow(
118 `Worker node key chosen is null or undefined after ${
119 fixedPool.info.maxSize +
120 Object.keys(workerChoiceStrategyContext.opts.weights).length
126 it('Verify that execute() retry until a worker node is ready and chosen', () => {
127 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
130 const workerChoiceStrategyStub = createStubInstance(
131 RoundRobinWorkerChoiceStrategy,
133 hasPoolWorkerNodesReady: stub()
145 choose: stub().returns(1)
148 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
149 WorkerChoiceStrategies.ROUND_ROBIN
151 workerChoiceStrategyContext.workerChoiceStrategies.set(
152 workerChoiceStrategyContext.workerChoiceStrategy,
153 workerChoiceStrategyStub
155 const chosenWorkerKey = workerChoiceStrategyContext.execute()
157 workerChoiceStrategyContext.workerChoiceStrategies.get(
158 workerChoiceStrategyContext.workerChoiceStrategy
159 ).hasPoolWorkerNodesReady.callCount
162 workerChoiceStrategyContext.workerChoiceStrategies.get(
163 workerChoiceStrategyContext.workerChoiceStrategy
166 expect(chosenWorkerKey).toBe(1)
169 it('Verify that execute() throws error if worker choice strategy recursion reach the maximum depth', () => {
170 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
173 const workerChoiceStrategyStub = createStubInstance(
174 RoundRobinWorkerChoiceStrategy,
176 hasPoolWorkerNodesReady: stub().returns(false),
177 choose: stub().returns(0)
180 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
181 WorkerChoiceStrategies.ROUND_ROBIN
183 workerChoiceStrategyContext.workerChoiceStrategies.set(
184 workerChoiceStrategyContext.workerChoiceStrategy,
185 workerChoiceStrategyStub
187 expect(() => workerChoiceStrategyContext.execute()).toThrow(
188 new RangeError('Maximum call stack size exceeded')
192 it('Verify that execute() return the worker node key chosen by the strategy with dynamic pool', () => {
193 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
196 const workerChoiceStrategyStub = createStubInstance(
197 RoundRobinWorkerChoiceStrategy,
199 hasPoolWorkerNodesReady: stub().returns(true),
200 choose: stub().returns(0)
203 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
204 WorkerChoiceStrategies.ROUND_ROBIN
206 workerChoiceStrategyContext.workerChoiceStrategies.set(
207 workerChoiceStrategyContext.workerChoiceStrategy,
208 workerChoiceStrategyStub
210 const chosenWorkerKey = workerChoiceStrategyContext.execute()
212 workerChoiceStrategyContext.workerChoiceStrategies.get(
213 workerChoiceStrategyContext.workerChoiceStrategy
216 expect(chosenWorkerKey).toBe(0)
219 it('Verify that setWorkerChoiceStrategy() works with ROUND_ROBIN and fixed pool', () => {
220 const workerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN
221 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
225 workerChoiceStrategyContext.workerChoiceStrategies.get(
228 ).toBeInstanceOf(RoundRobinWorkerChoiceStrategy)
229 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
232 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
234 workerChoiceStrategyContext.workerChoiceStrategies.get(
237 ).toBeInstanceOf(RoundRobinWorkerChoiceStrategy)
238 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
243 it('Verify that setWorkerChoiceStrategy() works with ROUND_ROBIN and dynamic pool', () => {
244 const workerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN
245 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
249 workerChoiceStrategyContext.workerChoiceStrategies.get(
252 ).toBeInstanceOf(RoundRobinWorkerChoiceStrategy)
253 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
256 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
258 workerChoiceStrategyContext.workerChoiceStrategies.get(
261 ).toBeInstanceOf(RoundRobinWorkerChoiceStrategy)
262 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
267 it('Verify that setWorkerChoiceStrategy() works with LEAST_USED and fixed pool', () => {
268 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_USED
269 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
272 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
274 workerChoiceStrategyContext.workerChoiceStrategies.get(
277 ).toBeInstanceOf(LeastUsedWorkerChoiceStrategy)
278 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
283 it('Verify that setWorkerChoiceStrategy() works with LEAST_USED and dynamic pool', () => {
284 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_USED
285 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
288 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
290 workerChoiceStrategyContext.workerChoiceStrategies.get(
293 ).toBeInstanceOf(LeastUsedWorkerChoiceStrategy)
294 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
299 it('Verify that setWorkerChoiceStrategy() works with LEAST_BUSY and fixed pool', () => {
300 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_BUSY
301 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
304 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
306 workerChoiceStrategyContext.workerChoiceStrategies.get(
309 ).toBeInstanceOf(LeastBusyWorkerChoiceStrategy)
310 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
315 it('Verify that setWorkerChoiceStrategy() works with LEAST_BUSY and dynamic pool', () => {
316 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_BUSY
317 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
320 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
322 workerChoiceStrategyContext.workerChoiceStrategies.get(
325 ).toBeInstanceOf(LeastBusyWorkerChoiceStrategy)
326 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
331 it('Verify that setWorkerChoiceStrategy() works with LEAST_ELU and fixed pool', () => {
332 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_ELU
333 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
336 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
338 workerChoiceStrategyContext.workerChoiceStrategies.get(
341 ).toBeInstanceOf(LeastEluWorkerChoiceStrategy)
342 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
347 it('Verify that setWorkerChoiceStrategy() works with LEAST_ELU and dynamic pool', () => {
348 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_ELU
349 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
352 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
354 workerChoiceStrategyContext.workerChoiceStrategies.get(
357 ).toBeInstanceOf(LeastEluWorkerChoiceStrategy)
358 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
363 it('Verify that setWorkerChoiceStrategy() works with FAIR_SHARE and fixed pool', () => {
364 const workerChoiceStrategy = WorkerChoiceStrategies.FAIR_SHARE
365 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
368 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
370 workerChoiceStrategyContext.workerChoiceStrategies.get(
373 ).toBeInstanceOf(FairShareWorkerChoiceStrategy)
374 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
379 it('Verify that setWorkerChoiceStrategy() works with FAIR_SHARE and dynamic pool', () => {
380 const workerChoiceStrategy = WorkerChoiceStrategies.FAIR_SHARE
381 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
384 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
386 workerChoiceStrategyContext.workerChoiceStrategies.get(
389 ).toBeInstanceOf(FairShareWorkerChoiceStrategy)
390 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
395 it('Verify that setWorkerChoiceStrategy() works with WEIGHTED_ROUND_ROBIN and fixed pool', () => {
396 const workerChoiceStrategy = WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
397 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
400 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
402 workerChoiceStrategyContext.workerChoiceStrategies.get(
405 ).toBeInstanceOf(WeightedRoundRobinWorkerChoiceStrategy)
406 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
411 it('Verify that setWorkerChoiceStrategy() works with WEIGHTED_ROUND_ROBIN and dynamic pool', () => {
412 const workerChoiceStrategy = WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
413 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
416 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
418 workerChoiceStrategyContext.workerChoiceStrategies.get(
421 ).toBeInstanceOf(WeightedRoundRobinWorkerChoiceStrategy)
422 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
427 it('Verify that setWorkerChoiceStrategy() works with INTERLEAVED_WEIGHTED_ROUND_ROBIN and fixed pool', () => {
428 const workerChoiceStrategy =
429 WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN
430 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
433 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
435 workerChoiceStrategyContext.workerChoiceStrategies.get(
438 ).toBeInstanceOf(InterleavedWeightedRoundRobinWorkerChoiceStrategy)
439 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
444 it('Verify that setWorkerChoiceStrategy() works with INTERLEAVED_WEIGHTED_ROUND_ROBIN and dynamic pool', () => {
445 const workerChoiceStrategy =
446 WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN
447 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
450 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
452 workerChoiceStrategyContext.workerChoiceStrategies.get(
455 ).toBeInstanceOf(InterleavedWeightedRoundRobinWorkerChoiceStrategy)
456 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
461 it('Verify that worker choice strategy options enable median runtime pool statistics', () => {
462 const wwrWorkerChoiceStrategy = WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
463 let workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
465 wwrWorkerChoiceStrategy,
467 runTime: { median: true }
471 workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime
475 workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.median
477 workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
479 wwrWorkerChoiceStrategy,
481 runTime: { median: true }
485 workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime
489 workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.median
491 const fsWorkerChoiceStrategy = WorkerChoiceStrategies.FAIR_SHARE
492 workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
494 fsWorkerChoiceStrategy,
496 runTime: { median: true }
500 workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime
504 workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.median
506 workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
508 fsWorkerChoiceStrategy,
510 runTime: { median: true }
514 workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime
518 workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.median