Merge branch 'master' into feature/task-functions
[poolifier.git] / tests / utils.test.js
1 const { randomInt } = require('node:crypto')
2 const { Worker } = require('node:worker_threads')
3 const cluster = require('node:cluster')
4 const os = require('node:os')
5 const { expect } = require('expect')
6 const {
7 CircularArray,
8 DEFAULT_CIRCULAR_ARRAY_SIZE
9 } = require('../lib/circular-array')
10 const {
11 DEFAULT_MEASUREMENT_STATISTICS_REQUIREMENTS,
12 DEFAULT_TASK_NAME,
13 DEFAULT_WORKER_CHOICE_STRATEGY_OPTIONS,
14 EMPTY_FUNCTION,
15 availableParallelism,
16 average,
17 exponentialDelay,
18 getWorkerType,
19 getWorkerId,
20 isAsyncFunction,
21 isKillBehavior,
22 isPlainObject,
23 max,
24 median,
25 min,
26 round,
27 secureRandom,
28 sleep,
29 updateMeasurementStatistics
30 } = require('../lib/utils')
31 const { KillBehaviors, WorkerTypes } = require('../lib')
32
33 describe('Utils test suite', () => {
34 it('Verify DEFAULT_TASK_NAME value', () => {
35 expect(DEFAULT_TASK_NAME).toBe('default')
36 })
37
38 it('Verify EMPTY_FUNCTION value', () => {
39 expect(EMPTY_FUNCTION).toStrictEqual(expect.any(Function))
40 })
41
42 it('Verify DEFAULT_WORKER_CHOICE_STRATEGY_OPTIONS values', () => {
43 expect(DEFAULT_WORKER_CHOICE_STRATEGY_OPTIONS).toStrictEqual({
44 retries: 6,
45 runTime: { median: false },
46 waitTime: { median: false },
47 elu: { median: false }
48 })
49 })
50
51 it('Verify DEFAULT_MEASUREMENT_STATISTICS_REQUIREMENTS values', () => {
52 expect(DEFAULT_MEASUREMENT_STATISTICS_REQUIREMENTS).toStrictEqual({
53 aggregate: false,
54 average: false,
55 median: false
56 })
57 })
58
59 it('Verify availableParallelism() behavior', () => {
60 const parallelism = availableParallelism()
61 expect(typeof parallelism === 'number').toBe(true)
62 expect(Number.isSafeInteger(parallelism)).toBe(true)
63 let expectedParallelism = 1
64 try {
65 expectedParallelism = os.availableParallelism()
66 } catch {
67 expectedParallelism = os.cpus().length
68 }
69 expect(parallelism).toBe(expectedParallelism)
70 })
71
72 it('Verify getWorkerType() behavior', () => {
73 expect(
74 getWorkerType(new Worker('./tests/worker-files/thread/testWorker.js'))
75 ).toBe(WorkerTypes.thread)
76 expect(getWorkerType(cluster.fork())).toBe(WorkerTypes.cluster)
77 })
78
79 it('Verify getWorkerId() behavior', () => {
80 const threadWorker = new Worker('./tests/worker-files/thread/testWorker.js')
81 const clusterWorker = cluster.fork()
82 expect(getWorkerId(threadWorker)).toBe(threadWorker.threadId)
83 expect(getWorkerId(clusterWorker)).toBe(clusterWorker.id)
84 })
85
86 it('Verify sleep() behavior', async () => {
87 const start = performance.now()
88 await sleep(1000)
89 const elapsed = performance.now() - start
90 expect(elapsed).toBeGreaterThanOrEqual(999)
91 })
92
93 it('Verify exponentialDelay() behavior', () => {
94 const delay = exponentialDelay(randomInt(1000))
95 expect(typeof delay === 'number').toBe(true)
96 expect(delay).toBeGreaterThanOrEqual(Number.MIN_VALUE)
97 expect(delay).toBeLessThanOrEqual(Number.MAX_VALUE)
98 })
99
100 it('Verify average() computation', () => {
101 expect(average([])).toBe(0)
102 expect(average([0.08])).toBe(0.08)
103 expect(average([0.25, 4.75, 3.05, 6.04, 1.01, 2.02, 5.03])).toBe(
104 3.1642857142857146
105 )
106 expect(average([0.25, 4.75, 3.05, 6.04, 1.01, 2.02])).toBe(
107 2.8533333333333335
108 )
109 })
110
111 it('Verify median() computation', () => {
112 expect(median([])).toBe(0)
113 expect(median([0.08])).toBe(0.08)
114 expect(median([0.25, 4.75, 3.05, 6.04, 1.01, 2.02, 5.03])).toBe(3.05)
115 expect(median([0.25, 4.75, 3.05, 6.04, 1.01, 2.02])).toBe(2.535)
116 })
117
118 it('Verify round() behavior', () => {
119 expect(round(0)).toBe(0)
120 expect(round(0.5, 0)).toBe(1)
121 expect(round(0.5)).toBe(0.5)
122 expect(round(-0.5, 0)).toBe(-1)
123 expect(round(-0.5)).toBe(-0.5)
124 expect(round(1.005)).toBe(1.01)
125 expect(round(2.175)).toBe(2.18)
126 expect(round(5.015)).toBe(5.02)
127 expect(round(-1.005)).toBe(-1.01)
128 expect(round(-2.175)).toBe(-2.18)
129 expect(round(-5.015)).toBe(-5.02)
130 })
131
132 it('Verify isPlainObject() behavior', () => {
133 expect(isPlainObject(null)).toBe(false)
134 expect(isPlainObject(undefined)).toBe(false)
135 expect(isPlainObject(true)).toBe(false)
136 expect(isPlainObject(false)).toBe(false)
137 expect(isPlainObject(0)).toBe(false)
138 expect(isPlainObject('')).toBe(false)
139 expect(isPlainObject([])).toBe(false)
140 expect(isPlainObject(() => {})).toBe(false)
141 expect(isPlainObject(new Date())).toBe(false)
142 expect(isPlainObject(new RegExp())).toBe(false)
143 expect(isPlainObject(new Error())).toBe(false)
144 expect(isPlainObject(new Map())).toBe(false)
145 expect(isPlainObject(new Set())).toBe(false)
146 expect(isPlainObject(new WeakMap())).toBe(false)
147 expect(isPlainObject(new WeakSet())).toBe(false)
148 expect(isPlainObject(new Int8Array())).toBe(false)
149 expect(isPlainObject(new Uint8Array())).toBe(false)
150 expect(isPlainObject(new Uint8ClampedArray())).toBe(false)
151 expect(isPlainObject(new Int16Array())).toBe(false)
152 expect(isPlainObject(new Uint16Array())).toBe(false)
153 expect(isPlainObject(new Int32Array())).toBe(false)
154 expect(isPlainObject(new Uint32Array())).toBe(false)
155 expect(isPlainObject(new Float32Array())).toBe(false)
156 expect(isPlainObject(new Float64Array())).toBe(false)
157 expect(isPlainObject(new BigInt64Array())).toBe(false)
158 expect(isPlainObject(new BigUint64Array())).toBe(false)
159 expect(isPlainObject(new Promise(() => {}))).toBe(false)
160 expect(isPlainObject(new WeakRef({}))).toBe(false)
161 expect(isPlainObject(new FinalizationRegistry(() => {}))).toBe(false)
162 expect(isPlainObject(new ArrayBuffer())).toBe(false)
163 expect(isPlainObject(new SharedArrayBuffer())).toBe(false)
164 expect(isPlainObject(new DataView(new ArrayBuffer()))).toBe(false)
165 expect(isPlainObject({})).toBe(true)
166 expect(isPlainObject({ a: 1 })).toBe(true)
167 })
168
169 it('Verify isKillBehavior() behavior', () => {
170 expect(isKillBehavior(KillBehaviors.SOFT, KillBehaviors.SOFT)).toBe(true)
171 expect(isKillBehavior(KillBehaviors.SOFT, KillBehaviors.HARD)).toBe(false)
172 expect(isKillBehavior(KillBehaviors.HARD, KillBehaviors.HARD)).toBe(true)
173 expect(isKillBehavior(KillBehaviors.HARD, KillBehaviors.SOFT)).toBe(false)
174 expect(isKillBehavior(KillBehaviors.SOFT)).toBe(false)
175 expect(isKillBehavior(KillBehaviors.HARD)).toBe(false)
176 expect(isKillBehavior(KillBehaviors.HARD, null)).toBe(false)
177 expect(isKillBehavior(KillBehaviors.HARD, undefined)).toBe(false)
178 expect(isKillBehavior(KillBehaviors.SOFT, 'unknown')).toBe(false)
179 })
180
181 it('Verify isAsyncFunction() behavior', () => {
182 expect(isAsyncFunction(null)).toBe(false)
183 expect(isAsyncFunction(undefined)).toBe(false)
184 expect(isAsyncFunction(true)).toBe(false)
185 expect(isAsyncFunction(false)).toBe(false)
186 expect(isAsyncFunction(0)).toBe(false)
187 expect(isAsyncFunction('')).toBe(false)
188 expect(isAsyncFunction([])).toBe(false)
189 expect(isAsyncFunction(new Date())).toBe(false)
190 expect(isAsyncFunction(new RegExp())).toBe(false)
191 expect(isAsyncFunction(new Error())).toBe(false)
192 expect(isAsyncFunction(new Map())).toBe(false)
193 expect(isAsyncFunction(new Set())).toBe(false)
194 expect(isAsyncFunction(new WeakMap())).toBe(false)
195 expect(isAsyncFunction(new WeakSet())).toBe(false)
196 expect(isAsyncFunction(new Int8Array())).toBe(false)
197 expect(isAsyncFunction(new Uint8Array())).toBe(false)
198 expect(isAsyncFunction(new Uint8ClampedArray())).toBe(false)
199 expect(isAsyncFunction(new Int16Array())).toBe(false)
200 expect(isAsyncFunction(new Uint16Array())).toBe(false)
201 expect(isAsyncFunction(new Int32Array())).toBe(false)
202 expect(isAsyncFunction(new Uint32Array())).toBe(false)
203 expect(isAsyncFunction(new Float32Array())).toBe(false)
204 expect(isAsyncFunction(new Float64Array())).toBe(false)
205 expect(isAsyncFunction(new BigInt64Array())).toBe(false)
206 expect(isAsyncFunction(new BigUint64Array())).toBe(false)
207 expect(isAsyncFunction(new Promise(() => {}))).toBe(false)
208 expect(isAsyncFunction(new WeakRef({}))).toBe(false)
209 expect(isAsyncFunction(new FinalizationRegistry(() => {}))).toBe(false)
210 expect(isAsyncFunction(new ArrayBuffer())).toBe(false)
211 expect(isAsyncFunction(new SharedArrayBuffer())).toBe(false)
212 expect(isAsyncFunction(new DataView(new ArrayBuffer()))).toBe(false)
213 expect(isAsyncFunction({})).toBe(false)
214 expect(isAsyncFunction({ a: 1 })).toBe(false)
215 expect(isAsyncFunction(() => {})).toBe(false)
216 expect(isAsyncFunction(function () {})).toBe(false)
217 expect(isAsyncFunction(function named () {})).toBe(false)
218 expect(isAsyncFunction(async () => {})).toBe(true)
219 expect(isAsyncFunction(async function () {})).toBe(true)
220 expect(isAsyncFunction(async function named () {})).toBe(true)
221 })
222
223 it('Verify updateMeasurementStatistics() behavior', () => {
224 const measurementStatistics = {
225 history: new CircularArray()
226 }
227 updateMeasurementStatistics(
228 measurementStatistics,
229 { aggregate: true, average: false, median: false },
230 0.01
231 )
232 expect(measurementStatistics).toStrictEqual({
233 aggregate: 0.01,
234 maximum: 0.01,
235 minimum: 0.01,
236 history: new CircularArray()
237 })
238 updateMeasurementStatistics(
239 measurementStatistics,
240 { aggregate: true, average: false, median: false },
241 0.02
242 )
243 expect(measurementStatistics).toStrictEqual({
244 aggregate: 0.03,
245 maximum: 0.02,
246 minimum: 0.01,
247 history: new CircularArray()
248 })
249 updateMeasurementStatistics(
250 measurementStatistics,
251 { aggregate: true, average: true, median: false },
252 0.001
253 )
254 expect(measurementStatistics).toStrictEqual({
255 aggregate: 0.031,
256 maximum: 0.02,
257 minimum: 0.001,
258 average: 0.001,
259 history: new CircularArray(DEFAULT_CIRCULAR_ARRAY_SIZE, 0.001)
260 })
261 updateMeasurementStatistics(
262 measurementStatistics,
263 { aggregate: true, average: true, median: false },
264 0.003
265 )
266 expect(measurementStatistics).toStrictEqual({
267 aggregate: 0.034,
268 maximum: 0.02,
269 minimum: 0.001,
270 average: 0.002,
271 history: new CircularArray(DEFAULT_CIRCULAR_ARRAY_SIZE, 0.001, 0.003)
272 })
273 updateMeasurementStatistics(
274 measurementStatistics,
275 { aggregate: true, average: false, median: true },
276 0.006
277 )
278 expect(measurementStatistics).toStrictEqual({
279 aggregate: 0.04,
280 maximum: 0.02,
281 minimum: 0.001,
282 median: 0.003,
283 history: new CircularArray(
284 DEFAULT_CIRCULAR_ARRAY_SIZE,
285 0.001,
286 0.003,
287 0.006
288 )
289 })
290 updateMeasurementStatistics(
291 measurementStatistics,
292 { aggregate: true, average: true, median: false },
293 0.01
294 )
295 expect(measurementStatistics).toStrictEqual({
296 aggregate: 0.05,
297 maximum: 0.02,
298 minimum: 0.001,
299 average: 0.005,
300 history: new CircularArray(
301 DEFAULT_CIRCULAR_ARRAY_SIZE,
302 0.001,
303 0.003,
304 0.006,
305 0.01
306 )
307 })
308 })
309
310 it('Verify secureRandom() behavior', () => {
311 const randomNumber = secureRandom()
312 expect(typeof randomNumber === 'number').toBe(true)
313 expect(randomNumber).toBeGreaterThanOrEqual(0)
314 expect(randomNumber).toBeLessThan(1)
315 })
316
317 it('Verify min() behavior', () => {
318 expect(min()).toBe(Infinity)
319 expect(min(1, 2)).toBe(1)
320 expect(min(2, 1)).toBe(1)
321 expect(min(1, 1)).toBe(1)
322 })
323
324 it('Verify max() behavior', () => {
325 expect(max()).toBe(-Infinity)
326 expect(max(1, 2)).toBe(2)
327 expect(max(2, 1)).toBe(2)
328 expect(max(1, 1)).toBe(1)
329 })
330 })