chore(deps-dev): apply updates
[e-mobility-charging-stations-simulator.git] / tests / utils / Utils.test.ts
CommitLineData
66a7748d
JB
1import { hoursToMilliseconds, hoursToSeconds } from 'date-fns'
2import { expect } from 'expect'
840ca85d 3import { CircularBuffer } from 'mnemonist'
0749233f
JB
4import { randomInt } from 'node:crypto'
5import { version } from 'node:process'
6import { describe, it } from 'node:test'
8f801a93 7import { satisfies } from 'semver'
a2111e8d 8
840ca85d 9import type { TimestampedData } from '../../src/types/index.js'
0749233f
JB
10
11import { runtime, runtimes } from '../../scripts/runtime.js'
66a7748d 12import { Constants } from '../../src/utils/Constants.js'
9bf0ef23 13import {
40615072 14 clone,
9bf0ef23
JB
15 convertToBoolean,
16 convertToDate,
17 convertToFloat,
18 convertToInt,
1553f6df 19 extractTimeSeriesValues,
be4c6702 20 formatDurationMilliSeconds,
8053c842 21 formatDurationSeconds,
9bf0ef23
JB
22 generateUUID,
23 getRandomFloat,
9bf0ef23 24 hasOwnProp,
055d4c4c 25 insertAt,
80c58041 26 isArraySorted,
bcf95df1 27 isAsyncFunction,
9bf0ef23
JB
28 isNotEmptyArray,
29 isNotEmptyString,
9bf0ef23 30 isObject,
5dc7c990 31 isValidDate,
9bf0ef23
JB
32 roundTo,
33 secureRandom,
34 sleep,
d1f5bfd8 35 validateUUID,
66a7748d 36} from '../../src/utils/Utils.js'
a2111e8d 37
6c43b441
JB
38await describe('Utils test suite', async () => {
39 await it('Verify generateUUID()/validateUUID()', () => {
66a7748d
JB
40 const uuid = generateUUID()
41 expect(uuid).toBeDefined()
42 expect(uuid.length).toEqual(36)
43 expect(validateUUID(uuid)).toBe(true)
44 expect(validateUUID('abcdef00-0000-4000-0000-000000000000')).toBe(true)
45 expect(validateUUID('')).toBe(false)
878e026c 46 // Shall invalidate Nil UUID
66a7748d
JB
47 expect(validateUUID('00000000-0000-0000-0000-000000000000')).toBe(false)
48 expect(validateUUID('987FBC9-4BED-3078-CF07A-9141BA07C9F3')).toBe(false)
49 })
8bd02502 50
6c43b441 51 await it('Verify sleep()', async () => {
66a7748d
JB
52 const start = performance.now()
53 await sleep(1000)
54 const stop = performance.now()
55 expect(stop - start).toBeGreaterThanOrEqual(1000)
56 })
45999aab 57
6c43b441 58 await it('Verify formatDurationMilliSeconds()', () => {
66a7748d
JB
59 expect(formatDurationMilliSeconds(0)).toBe('0 seconds')
60 expect(formatDurationMilliSeconds(900)).toBe('0 seconds')
61 expect(formatDurationMilliSeconds(1000)).toBe('1 second')
62 expect(formatDurationMilliSeconds(hoursToMilliseconds(4380))).toBe('182 days 12 hours')
63 })
be4c6702 64
6c43b441 65 await it('Verify formatDurationSeconds()', () => {
66a7748d
JB
66 expect(formatDurationSeconds(0)).toBe('0 seconds')
67 expect(formatDurationSeconds(0.9)).toBe('0 seconds')
68 expect(formatDurationSeconds(1)).toBe('1 second')
69 expect(formatDurationSeconds(hoursToSeconds(4380))).toBe('182 days 12 hours')
70 })
8053c842 71
40615072 72 await it('Verify isValidDate()', () => {
5dc7c990
JB
73 expect(isValidDate(undefined)).toBe(false)
74 expect(isValidDate(-1)).toBe(true)
75 expect(isValidDate(0)).toBe(true)
76 expect(isValidDate(1)).toBe(true)
77 expect(isValidDate(-0.5)).toBe(true)
78 expect(isValidDate(0.5)).toBe(true)
79 expect(isValidDate(new Date())).toBe(true)
66a7748d 80 })
ac8178a4 81
6c43b441 82 await it('Verify convertToDate()', () => {
66a7748d 83 expect(convertToDate(undefined)).toBe(undefined)
79fd697f 84 expect(convertToDate(null)).toBe(undefined)
66a7748d
JB
85 expect(() => convertToDate('')).toThrow(new Error("Cannot convert to date: ''"))
86 expect(() => convertToDate('00:70:61')).toThrow(new Error("Cannot convert to date: '00:70:61'"))
87 expect(convertToDate(0)).toStrictEqual(new Date('1970-01-01T00:00:00.000Z'))
88 expect(convertToDate(-1)).toStrictEqual(new Date('1969-12-31T23:59:59.999Z'))
89 const dateStr = '2020-01-01T00:00:00.000Z'
90 let date = convertToDate(dateStr)
91 expect(date).toBeInstanceOf(Date)
92 expect(date).toStrictEqual(new Date(dateStr))
93 date = convertToDate(new Date(dateStr))
94 expect(date).toBeInstanceOf(Date)
95 expect(date).toStrictEqual(new Date(dateStr))
96 })
df645d8f 97
6c43b441 98 await it('Verify convertToInt()', () => {
66a7748d
JB
99 expect(convertToInt(undefined)).toBe(0)
100 expect(convertToInt(null)).toBe(0)
101 expect(convertToInt(0)).toBe(0)
fcda9151 102 const randomInteger = randomInt(Constants.MAX_RANDOM_INTEGER)
66a7748d
JB
103 expect(convertToInt(randomInteger)).toEqual(randomInteger)
104 expect(convertToInt('-1')).toBe(-1)
105 expect(convertToInt('1')).toBe(1)
106 expect(convertToInt('1.1')).toBe(1)
107 expect(convertToInt('1.9')).toBe(1)
108 expect(convertToInt('1.999')).toBe(1)
109 expect(convertToInt(-1)).toBe(-1)
110 expect(convertToInt(1)).toBe(1)
111 expect(convertToInt(1.1)).toBe(1)
112 expect(convertToInt(1.9)).toBe(1)
113 expect(convertToInt(1.999)).toBe(1)
878e026c 114 expect(() => {
66a7748d
JB
115 convertToInt('NaN')
116 }).toThrow("Cannot convert to integer: 'NaN'")
117 })
df645d8f 118
6c43b441 119 await it('Verify convertToFloat()', () => {
66a7748d
JB
120 expect(convertToFloat(undefined)).toBe(0)
121 expect(convertToFloat(null)).toBe(0)
122 expect(convertToFloat(0)).toBe(0)
123 const randomFloat = getRandomFloat()
124 expect(convertToFloat(randomFloat)).toEqual(randomFloat)
125 expect(convertToFloat('-1')).toBe(-1)
126 expect(convertToFloat('1')).toBe(1)
127 expect(convertToFloat('1.1')).toBe(1.1)
128 expect(convertToFloat('1.9')).toBe(1.9)
129 expect(convertToFloat('1.999')).toBe(1.999)
130 expect(convertToFloat(-1)).toBe(-1)
131 expect(convertToFloat(1)).toBe(1)
132 expect(convertToFloat(1.1)).toBe(1.1)
133 expect(convertToFloat(1.9)).toBe(1.9)
134 expect(convertToFloat(1.999)).toBe(1.999)
878e026c 135 expect(() => {
66a7748d
JB
136 convertToFloat('NaN')
137 }).toThrow("Cannot convert to float: 'NaN'")
138 })
df645d8f 139
6c43b441 140 await it('Verify convertToBoolean()', () => {
66a7748d
JB
141 expect(convertToBoolean(undefined)).toBe(false)
142 expect(convertToBoolean(null)).toBe(false)
143 expect(convertToBoolean('true')).toBe(true)
144 expect(convertToBoolean('false')).toBe(false)
145 expect(convertToBoolean('TRUE')).toBe(true)
146 expect(convertToBoolean('FALSE')).toBe(false)
147 expect(convertToBoolean('1')).toBe(true)
148 expect(convertToBoolean('0')).toBe(false)
149 expect(convertToBoolean(1)).toBe(true)
150 expect(convertToBoolean(0)).toBe(false)
151 expect(convertToBoolean(true)).toBe(true)
152 expect(convertToBoolean(false)).toBe(false)
153 expect(convertToBoolean('')).toBe(false)
154 expect(convertToBoolean('NoNBoolean')).toBe(false)
155 })
df645d8f 156
6c43b441 157 await it('Verify secureRandom()', () => {
66a7748d
JB
158 const random = secureRandom()
159 expect(typeof random === 'number').toBe(true)
160 expect(random).toBeGreaterThanOrEqual(0)
161 expect(random).toBeLessThan(1)
162 })
a2111e8d 163
6c43b441 164 await it('Verify roundTo()', () => {
66a7748d
JB
165 expect(roundTo(0, 2)).toBe(0)
166 expect(roundTo(0.5, 0)).toBe(1)
167 expect(roundTo(0.5, 2)).toBe(0.5)
168 expect(roundTo(-0.5, 0)).toBe(-1)
169 expect(roundTo(-0.5, 2)).toBe(-0.5)
170 expect(roundTo(1.005, 0)).toBe(1)
171 expect(roundTo(1.005, 2)).toBe(1.01)
172 expect(roundTo(2.175, 2)).toBe(2.18)
173 expect(roundTo(5.015, 2)).toBe(5.02)
174 expect(roundTo(-1.005, 2)).toBe(-1.01)
175 expect(roundTo(-2.175, 2)).toBe(-2.18)
176 expect(roundTo(-5.015, 2)).toBe(-5.02)
177 })
316d1564 178
6c43b441 179 await it('Verify getRandomFloat()', () => {
66a7748d
JB
180 let randomFloat = getRandomFloat()
181 expect(typeof randomFloat === 'number').toBe(true)
182 expect(randomFloat).toBeGreaterThanOrEqual(0)
183 expect(randomFloat).toBeLessThanOrEqual(Number.MAX_VALUE)
184 expect(randomFloat).not.toEqual(getRandomFloat())
185 expect(() => getRandomFloat(0, 1)).toThrow(new RangeError('Invalid interval'))
68f56fdf 186 expect(() => getRandomFloat(Number.MAX_VALUE, -Number.MAX_VALUE)).toThrow(
66a7748d
JB
187 new RangeError('Invalid interval')
188 )
189 randomFloat = getRandomFloat(0, -Number.MAX_VALUE)
190 expect(randomFloat).toBeGreaterThanOrEqual(-Number.MAX_VALUE)
191 expect(randomFloat).toBeLessThanOrEqual(0)
192 })
a2111e8d 193
6c43b441 194 await it('Verify extractTimeSeriesValues()', () => {
1553f6df 195 expect(
840ca85d
JB
196 extractTimeSeriesValues(
197 new CircularBuffer<TimestampedData>(Array, Constants.DEFAULT_CIRCULAR_BUFFER_CAPACITY)
198 )
199 ).toEqual([])
200 const circularBuffer = new CircularBuffer<TimestampedData>(
201 Array,
202 Constants.DEFAULT_CIRCULAR_BUFFER_CAPACITY
203 )
204 circularBuffer.push({ timestamp: Date.now(), value: 1.1 })
205 circularBuffer.push({ timestamp: Date.now(), value: 2.2 })
206 circularBuffer.push({ timestamp: Date.now(), value: 3.3 })
207 expect(extractTimeSeriesValues(circularBuffer)).toEqual([1.1, 2.2, 3.3])
66a7748d 208 })
1553f6df 209
6c43b441 210 await it('Verify isObject()', () => {
66a7748d
JB
211 expect(isObject('test')).toBe(false)
212 expect(isObject(undefined)).toBe(false)
213 expect(isObject(null)).toBe(false)
214 expect(isObject(0)).toBe(false)
215 expect(isObject([])).toBe(false)
216 expect(isObject([0, 1])).toBe(false)
217 expect(isObject(['0', '1'])).toBe(false)
218 expect(isObject({})).toBe(true)
219 expect(isObject({ 1: 1 })).toBe(true)
220 expect(isObject({ 1: '1' })).toBe(true)
221 expect(isObject(new Map())).toBe(true)
222 expect(isObject(new Set())).toBe(true)
223 expect(isObject(new WeakMap())).toBe(true)
224 expect(isObject(new WeakSet())).toBe(true)
225 })
a2111e8d 226
bcf95df1
JB
227 await it('Verify isAsyncFunction()', () => {
228 expect(isAsyncFunction(null)).toBe(false)
229 expect(isAsyncFunction(undefined)).toBe(false)
230 expect(isAsyncFunction(true)).toBe(false)
231 expect(isAsyncFunction(false)).toBe(false)
232 expect(isAsyncFunction(0)).toBe(false)
233 expect(isAsyncFunction('')).toBe(false)
234 expect(isAsyncFunction([])).toBe(false)
235 expect(isAsyncFunction(new Date())).toBe(false)
48847bc0 236 expect(isAsyncFunction(/[a-z]/i)).toBe(false)
bcf95df1
JB
237 expect(isAsyncFunction(new Error())).toBe(false)
238 expect(isAsyncFunction(new Map())).toBe(false)
239 expect(isAsyncFunction(new Set())).toBe(false)
240 expect(isAsyncFunction(new WeakMap())).toBe(false)
241 expect(isAsyncFunction(new WeakSet())).toBe(false)
242 expect(isAsyncFunction(new Int8Array())).toBe(false)
243 expect(isAsyncFunction(new Uint8Array())).toBe(false)
244 expect(isAsyncFunction(new Uint8ClampedArray())).toBe(false)
245 expect(isAsyncFunction(new Int16Array())).toBe(false)
246 expect(isAsyncFunction(new Uint16Array())).toBe(false)
247 expect(isAsyncFunction(new Int32Array())).toBe(false)
248 expect(isAsyncFunction(new Uint32Array())).toBe(false)
249 expect(isAsyncFunction(new Float32Array())).toBe(false)
250 expect(isAsyncFunction(new Float64Array())).toBe(false)
251 expect(isAsyncFunction(new BigInt64Array())).toBe(false)
252 expect(isAsyncFunction(new BigUint64Array())).toBe(false)
253 // eslint-disable-next-line @typescript-eslint/no-empty-function
254 expect(isAsyncFunction(new Promise(() => {}))).toBe(false)
255 expect(isAsyncFunction(new WeakRef({}))).toBe(false)
256 // eslint-disable-next-line @typescript-eslint/no-empty-function
257 expect(isAsyncFunction(new FinalizationRegistry(() => {}))).toBe(false)
258 expect(isAsyncFunction(new ArrayBuffer(16))).toBe(false)
259 expect(isAsyncFunction(new SharedArrayBuffer(16))).toBe(false)
260 expect(isAsyncFunction(new DataView(new ArrayBuffer(16)))).toBe(false)
261 expect(isAsyncFunction({})).toBe(false)
262 expect(isAsyncFunction({ a: 1 })).toBe(false)
263 // eslint-disable-next-line @typescript-eslint/no-empty-function
264 expect(isAsyncFunction(() => {})).toBe(false)
265 // eslint-disable-next-line @typescript-eslint/no-empty-function
266 expect(isAsyncFunction(function () {})).toBe(false)
267 // eslint-disable-next-line @typescript-eslint/no-empty-function
268 expect(isAsyncFunction(function named () {})).toBe(false)
269 // eslint-disable-next-line @typescript-eslint/no-empty-function
270 expect(isAsyncFunction(async () => {})).toBe(true)
271 // eslint-disable-next-line @typescript-eslint/no-empty-function
272 expect(isAsyncFunction(async function () {})).toBe(true)
273 // eslint-disable-next-line @typescript-eslint/no-empty-function
274 expect(isAsyncFunction(async function named () {})).toBe(true)
275 class TestClass {
276 // eslint-disable-next-line @typescript-eslint/no-empty-function
0749233f 277 public testArrowAsync = async (): Promise<void> => {}
bcf95df1
JB
278 // eslint-disable-next-line @typescript-eslint/no-empty-function
279 public testArrowSync = (): void => {}
280 // eslint-disable-next-line @typescript-eslint/no-empty-function
0749233f 281 public static async testStaticAsync (): Promise<void> {}
bcf95df1
JB
282 // eslint-disable-next-line @typescript-eslint/no-empty-function
283 public static testStaticSync (): void {}
284 // eslint-disable-next-line @typescript-eslint/no-empty-function
0749233f
JB
285 public async testAsync (): Promise<void> {}
286 // eslint-disable-next-line @typescript-eslint/no-empty-function
287 public testSync (): void {}
bcf95df1
JB
288 }
289 const testClass = new TestClass()
290 // eslint-disable-next-line @typescript-eslint/unbound-method
291 expect(isAsyncFunction(testClass.testSync)).toBe(false)
292 // eslint-disable-next-line @typescript-eslint/unbound-method
293 expect(isAsyncFunction(testClass.testAsync)).toBe(true)
294 expect(isAsyncFunction(testClass.testArrowSync)).toBe(false)
295 expect(isAsyncFunction(testClass.testArrowAsync)).toBe(true)
296 // eslint-disable-next-line @typescript-eslint/unbound-method
297 expect(isAsyncFunction(TestClass.testStaticSync)).toBe(false)
298 // eslint-disable-next-line @typescript-eslint/unbound-method
299 expect(isAsyncFunction(TestClass.testStaticAsync)).toBe(true)
300 })
301
40615072 302 await it('Verify clone()', () => {
66a7748d 303 const obj = { 1: 1 }
40615072
JB
304 expect(clone(obj)).toStrictEqual(obj)
305 expect(clone(obj) === obj).toBe(false)
66a7748d 306 const nestedObj = { 1: obj, 2: obj }
40615072
JB
307 expect(clone(nestedObj)).toStrictEqual(nestedObj)
308 expect(clone(nestedObj) === nestedObj).toBe(false)
66a7748d 309 const array = [1, 2]
40615072
JB
310 expect(clone(array)).toStrictEqual(array)
311 expect(clone(array) === array).toBe(false)
66a7748d 312 const objArray = [obj, obj]
40615072
JB
313 expect(clone(objArray)).toStrictEqual(objArray)
314 expect(clone(objArray) === objArray).toBe(false)
66a7748d 315 const date = new Date()
40615072
JB
316 expect(clone(date)).toStrictEqual(date)
317 expect(clone(date) === date).toBe(false)
53ec800a 318 if (runtime === runtimes.node && satisfies(version, '>=22.0.0')) {
8f801a93
JB
319 const url = new URL('https://domain.tld')
320 expect(() => clone(url)).toThrowError(new Error('Cannot clone object of unsupported type.'))
321 }
66a7748d 322 const map = new Map([['1', '2']])
3fad0dec 323 expect(clone(map)).toStrictEqual(map)
cfab8dc9 324 expect(clone(map) === map).toBe(false)
66a7748d 325 const set = new Set(['1'])
3fad0dec 326 expect(clone(set)).toStrictEqual(set)
cfab8dc9 327 expect(clone(set) === set).toBe(false)
66a7748d 328 const weakMap = new WeakMap([[{ 1: 1 }, { 2: 2 }]])
8f801a93 329 expect(() => clone(weakMap)).toThrowError(new Error('#<WeakMap> could not be cloned.'))
66a7748d 330 const weakSet = new WeakSet([{ 1: 1 }, { 2: 2 }])
8f801a93 331 expect(() => clone(weakSet)).toThrowError(new Error('#<WeakSet> could not be cloned.'))
66a7748d 332 })
dd5a1d6c 333
6c43b441 334 await it('Verify hasOwnProp()', () => {
66a7748d
JB
335 expect(hasOwnProp('test', '')).toBe(false)
336 expect(hasOwnProp(undefined, '')).toBe(false)
337 expect(hasOwnProp(null, '')).toBe(false)
338 expect(hasOwnProp([], '')).toBe(false)
339 expect(hasOwnProp({}, '')).toBe(false)
340 expect(hasOwnProp({ 1: 1 }, 1)).toBe(true)
341 expect(hasOwnProp({ 1: 1 }, '1')).toBe(true)
342 expect(hasOwnProp({ 1: 1 }, 2)).toBe(false)
343 expect(hasOwnProp({ 1: 1 }, '2')).toBe(false)
344 expect(hasOwnProp({ 1: '1' }, '1')).toBe(true)
345 expect(hasOwnProp({ 1: '1' }, 1)).toBe(true)
346 expect(hasOwnProp({ 1: '1' }, '2')).toBe(false)
347 expect(hasOwnProp({ 1: '1' }, 2)).toBe(false)
348 })
af29b598 349
6c43b441 350 await it('Verify isNotEmptyString()', () => {
66a7748d
JB
351 expect(isNotEmptyString('')).toBe(false)
352 expect(isNotEmptyString(' ')).toBe(false)
353 expect(isNotEmptyString(' ')).toBe(false)
354 expect(isNotEmptyString('test')).toBe(true)
355 expect(isNotEmptyString(' test')).toBe(true)
356 expect(isNotEmptyString('test ')).toBe(true)
357 expect(isNotEmptyString(undefined)).toBe(false)
358 expect(isNotEmptyString(null)).toBe(false)
359 expect(isNotEmptyString(0)).toBe(false)
360 expect(isNotEmptyString({})).toBe(false)
361 expect(isNotEmptyString([])).toBe(false)
362 expect(isNotEmptyString(new Map())).toBe(false)
363 expect(isNotEmptyString(new Set())).toBe(false)
364 expect(isNotEmptyString(new WeakMap())).toBe(false)
365 expect(isNotEmptyString(new WeakSet())).toBe(false)
366 })
429f8c9d 367
6c43b441 368 await it('Verify isNotEmptyArray()', () => {
66a7748d
JB
369 expect(isNotEmptyArray([])).toBe(false)
370 expect(isNotEmptyArray([1, 2])).toBe(true)
371 expect(isNotEmptyArray(['1', '2'])).toBe(true)
372 expect(isNotEmptyArray(undefined)).toBe(false)
373 expect(isNotEmptyArray(null)).toBe(false)
374 expect(isNotEmptyArray('')).toBe(false)
375 expect(isNotEmptyArray('test')).toBe(false)
376 expect(isNotEmptyArray(0)).toBe(false)
377 expect(isNotEmptyArray({})).toBe(false)
378 expect(isNotEmptyArray(new Map())).toBe(false)
379 expect(isNotEmptyArray(new Set())).toBe(false)
380 expect(isNotEmptyArray(new WeakMap())).toBe(false)
381 expect(isNotEmptyArray(new WeakSet())).toBe(false)
382 })
878e026c 383
055d4c4c
JB
384 await it('Verify insertAt()', () => {
385 expect(insertAt('test', 'ing', 'test'.length)).toBe('testing')
386 expect(insertAt('test', 'ing', 2)).toBe('teingst')
387 })
388
6c43b441 389 await it('Verify isArraySorted()', () => {
65a1581a
JB
390 expect(isArraySorted<number>([], (a, b) => a - b)).toBe(true)
391 expect(isArraySorted<number>([1], (a, b) => a - b)).toBe(true)
66a7748d
JB
392 expect(isArraySorted<number>([1, 2, 3, 4, 5], (a, b) => a - b)).toBe(true)
393 expect(isArraySorted<number>([1, 2, 3, 5, 4], (a, b) => a - b)).toBe(false)
394 expect(isArraySorted<number>([2, 1, 3, 4, 5], (a, b) => a - b)).toBe(false)
395 })
66a7748d 396})