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