fix: ensure the number of worker choice retries is enough for WRR
[poolifier.git] / tests / utils.test.mjs
1 import { Worker } from 'node:worker_threads'
2 import cluster from 'node:cluster'
3 import os from 'node:os'
4 import { randomInt } from 'node:crypto'
5 import { expect } from 'expect'
6 import {
7 DEFAULT_MEASUREMENT_STATISTICS_REQUIREMENTS,
8 DEFAULT_TASK_NAME,
9 EMPTY_FUNCTION,
10 availableParallelism,
11 average,
12 exponentialDelay,
13 getDefaultInternalWorkerChoiceStrategyOptions,
14 getWorkerId,
15 getWorkerType,
16 isAsyncFunction,
17 isKillBehavior,
18 isPlainObject,
19 max,
20 median,
21 min,
22 // once,
23 round,
24 secureRandom,
25 sleep
26 } from '../lib/utils.js'
27 import { KillBehaviors, WorkerTypes } from '../lib/index.js'
28
29 describe('Utils test suite', () => {
30 it('Verify DEFAULT_TASK_NAME value', () => {
31 expect(DEFAULT_TASK_NAME).toBe('default')
32 })
33
34 it('Verify EMPTY_FUNCTION value', () => {
35 expect(EMPTY_FUNCTION).toStrictEqual(expect.any(Function))
36 })
37
38 it('Verify getDefaultInternalWorkerChoiceStrategyOptions() values', () => {
39 const poolMaxSize = 10
40 expect(
41 getDefaultInternalWorkerChoiceStrategyOptions(poolMaxSize)
42 ).toStrictEqual({
43 retries: poolMaxSize,
44 runTime: { median: false },
45 waitTime: { median: false },
46 elu: { median: false }
47 })
48 })
49
50 it('Verify DEFAULT_MEASUREMENT_STATISTICS_REQUIREMENTS values', () => {
51 expect(DEFAULT_MEASUREMENT_STATISTICS_REQUIREMENTS).toStrictEqual({
52 aggregate: false,
53 average: false,
54 median: false
55 })
56 })
57
58 it('Verify availableParallelism() behavior', () => {
59 const parallelism = availableParallelism()
60 expect(typeof parallelism === 'number').toBe(true)
61 expect(Number.isSafeInteger(parallelism)).toBe(true)
62 let expectedParallelism = 1
63 try {
64 expectedParallelism = os.availableParallelism()
65 } catch {
66 expectedParallelism = os.cpus().length
67 }
68 expect(parallelism).toBe(expectedParallelism)
69 })
70
71 it('Verify getWorkerType() behavior', () => {
72 expect(
73 getWorkerType(new Worker('./tests/worker-files/thread/testWorker.mjs'))
74 ).toBe(WorkerTypes.thread)
75 expect(getWorkerType(cluster.fork())).toBe(WorkerTypes.cluster)
76 })
77
78 it('Verify getWorkerId() behavior', () => {
79 const threadWorker = new Worker(
80 './tests/worker-files/thread/testWorker.mjs'
81 )
82 const clusterWorker = cluster.fork()
83 expect(getWorkerId(threadWorker)).toBe(threadWorker.threadId)
84 expect(getWorkerId(clusterWorker)).toBe(clusterWorker.id)
85 })
86
87 it('Verify sleep() behavior', async () => {
88 const start = performance.now()
89 const sleepMs = 1000
90 await sleep(sleepMs)
91 const elapsed = performance.now() - start
92 expect(elapsed).toBeGreaterThanOrEqual(sleepMs - 1)
93 })
94
95 it('Verify exponentialDelay() behavior', () => {
96 const delay = exponentialDelay(randomInt(1000))
97 expect(typeof delay === 'number').toBe(true)
98 expect(delay).toBeGreaterThanOrEqual(Number.MIN_VALUE)
99 expect(delay).toBeLessThanOrEqual(Number.MAX_VALUE)
100 })
101
102 it('Verify average() computation', () => {
103 expect(average([])).toBe(0)
104 expect(average([0.08])).toBe(0.08)
105 expect(average([0.25, 4.75, 3.05, 6.04, 1.01, 2.02, 5.03])).toBe(
106 3.1642857142857146
107 )
108 expect(average([0.25, 4.75, 3.05, 6.04, 1.01, 2.02])).toBe(
109 2.8533333333333335
110 )
111 })
112
113 it('Verify median() computation', () => {
114 expect(median([])).toBe(0)
115 expect(median([0.08])).toBe(0.08)
116 expect(median([0.25, 4.75, 3.05, 6.04, 1.01, 2.02, 5.03])).toBe(3.05)
117 expect(median([0.25, 4.75, 3.05, 6.04, 1.01, 2.02])).toBe(2.535)
118 })
119
120 it('Verify round() behavior', () => {
121 expect(round(0)).toBe(0)
122 expect(round(0.5, 0)).toBe(1)
123 expect(round(0.5)).toBe(0.5)
124 expect(round(-0.5, 0)).toBe(-1)
125 expect(round(-0.5)).toBe(-0.5)
126 expect(round(1.005)).toBe(1.01)
127 expect(round(2.175)).toBe(2.18)
128 expect(round(5.015)).toBe(5.02)
129 expect(round(-1.005)).toBe(-1.01)
130 expect(round(-2.175)).toBe(-2.18)
131 expect(round(-5.015)).toBe(-5.02)
132 })
133
134 it('Verify isPlainObject() behavior', () => {
135 expect(isPlainObject(null)).toBe(false)
136 expect(isPlainObject(undefined)).toBe(false)
137 expect(isPlainObject(true)).toBe(false)
138 expect(isPlainObject(false)).toBe(false)
139 expect(isPlainObject(0)).toBe(false)
140 expect(isPlainObject('')).toBe(false)
141 expect(isPlainObject([])).toBe(false)
142 expect(isPlainObject(() => {})).toBe(false)
143 expect(isPlainObject(new Date())).toBe(false)
144 expect(isPlainObject(new RegExp())).toBe(false)
145 expect(isPlainObject(new Error())).toBe(false)
146 expect(isPlainObject(new Map())).toBe(false)
147 expect(isPlainObject(new Set())).toBe(false)
148 expect(isPlainObject(new WeakMap())).toBe(false)
149 expect(isPlainObject(new WeakSet())).toBe(false)
150 expect(isPlainObject(new Int8Array())).toBe(false)
151 expect(isPlainObject(new Uint8Array())).toBe(false)
152 expect(isPlainObject(new Uint8ClampedArray())).toBe(false)
153 expect(isPlainObject(new Int16Array())).toBe(false)
154 expect(isPlainObject(new Uint16Array())).toBe(false)
155 expect(isPlainObject(new Int32Array())).toBe(false)
156 expect(isPlainObject(new Uint32Array())).toBe(false)
157 expect(isPlainObject(new Float32Array())).toBe(false)
158 expect(isPlainObject(new Float64Array())).toBe(false)
159 expect(isPlainObject(new BigInt64Array())).toBe(false)
160 expect(isPlainObject(new BigUint64Array())).toBe(false)
161 expect(isPlainObject(new Promise(() => {}))).toBe(false)
162 expect(isPlainObject(new WeakRef({}))).toBe(false)
163 expect(isPlainObject(new FinalizationRegistry(() => {}))).toBe(false)
164 expect(isPlainObject(new ArrayBuffer())).toBe(false)
165 expect(isPlainObject(new SharedArrayBuffer())).toBe(false)
166 expect(isPlainObject(new DataView(new ArrayBuffer()))).toBe(false)
167 expect(isPlainObject({})).toBe(true)
168 expect(isPlainObject({ a: 1 })).toBe(true)
169 })
170
171 it('Verify isKillBehavior() behavior', () => {
172 expect(isKillBehavior(KillBehaviors.SOFT, KillBehaviors.SOFT)).toBe(true)
173 expect(isKillBehavior(KillBehaviors.SOFT, KillBehaviors.HARD)).toBe(false)
174 expect(isKillBehavior(KillBehaviors.HARD, KillBehaviors.HARD)).toBe(true)
175 expect(isKillBehavior(KillBehaviors.HARD, KillBehaviors.SOFT)).toBe(false)
176 expect(isKillBehavior(KillBehaviors.SOFT)).toBe(false)
177 expect(isKillBehavior(KillBehaviors.HARD)).toBe(false)
178 expect(isKillBehavior(KillBehaviors.HARD, null)).toBe(false)
179 expect(isKillBehavior(KillBehaviors.HARD, undefined)).toBe(false)
180 expect(isKillBehavior(KillBehaviors.SOFT, 'unknown')).toBe(false)
181 })
182
183 it('Verify isAsyncFunction() behavior', () => {
184 expect(isAsyncFunction(null)).toBe(false)
185 expect(isAsyncFunction(undefined)).toBe(false)
186 expect(isAsyncFunction(true)).toBe(false)
187 expect(isAsyncFunction(false)).toBe(false)
188 expect(isAsyncFunction(0)).toBe(false)
189 expect(isAsyncFunction('')).toBe(false)
190 expect(isAsyncFunction([])).toBe(false)
191 expect(isAsyncFunction(new Date())).toBe(false)
192 expect(isAsyncFunction(new RegExp())).toBe(false)
193 expect(isAsyncFunction(new Error())).toBe(false)
194 expect(isAsyncFunction(new Map())).toBe(false)
195 expect(isAsyncFunction(new Set())).toBe(false)
196 expect(isAsyncFunction(new WeakMap())).toBe(false)
197 expect(isAsyncFunction(new WeakSet())).toBe(false)
198 expect(isAsyncFunction(new Int8Array())).toBe(false)
199 expect(isAsyncFunction(new Uint8Array())).toBe(false)
200 expect(isAsyncFunction(new Uint8ClampedArray())).toBe(false)
201 expect(isAsyncFunction(new Int16Array())).toBe(false)
202 expect(isAsyncFunction(new Uint16Array())).toBe(false)
203 expect(isAsyncFunction(new Int32Array())).toBe(false)
204 expect(isAsyncFunction(new Uint32Array())).toBe(false)
205 expect(isAsyncFunction(new Float32Array())).toBe(false)
206 expect(isAsyncFunction(new Float64Array())).toBe(false)
207 expect(isAsyncFunction(new BigInt64Array())).toBe(false)
208 expect(isAsyncFunction(new BigUint64Array())).toBe(false)
209 expect(isAsyncFunction(new Promise(() => {}))).toBe(false)
210 expect(isAsyncFunction(new WeakRef({}))).toBe(false)
211 expect(isAsyncFunction(new FinalizationRegistry(() => {}))).toBe(false)
212 expect(isAsyncFunction(new ArrayBuffer())).toBe(false)
213 expect(isAsyncFunction(new SharedArrayBuffer())).toBe(false)
214 expect(isAsyncFunction(new DataView(new ArrayBuffer()))).toBe(false)
215 expect(isAsyncFunction({})).toBe(false)
216 expect(isAsyncFunction({ a: 1 })).toBe(false)
217 expect(isAsyncFunction(() => {})).toBe(false)
218 expect(isAsyncFunction(function () {})).toBe(false)
219 expect(isAsyncFunction(function named () {})).toBe(false)
220 expect(isAsyncFunction(async () => {})).toBe(true)
221 expect(isAsyncFunction(async function () {})).toBe(true)
222 expect(isAsyncFunction(async function named () {})).toBe(true)
223 })
224
225 it('Verify secureRandom() behavior', () => {
226 const randomNumber = secureRandom()
227 expect(typeof randomNumber === 'number').toBe(true)
228 expect(randomNumber).toBeGreaterThanOrEqual(0)
229 expect(randomNumber).toBeLessThan(1)
230 })
231
232 it('Verify min() behavior', () => {
233 expect(min()).toBe(Infinity)
234 expect(min(1, 2)).toBe(1)
235 expect(min(2, 1)).toBe(1)
236 expect(min(1, 1)).toBe(1)
237 })
238
239 it('Verify max() behavior', () => {
240 expect(max()).toBe(-Infinity)
241 expect(max(1, 2)).toBe(2)
242 expect(max(2, 1)).toBe(2)
243 expect(max(1, 1)).toBe(1)
244 })
245
246 // it('Verify once()', () => {
247 // let called = 0
248 // const fn = () => ++called
249 // const onceFn = once(fn, this)
250 // const result1 = onceFn()
251 // expect(called).toBe(1)
252 // expect(result1).toBe(1)
253 // const result2 = onceFn()
254 // expect(called).toBe(1)
255 // expect(result2).toBe(1)
256 // const result3 = onceFn()
257 // expect(called).toBe(1)
258 // expect(result3).toBe(1)
259 // })
260 })