]> Piment Noir Git Repositories - e-mobility-charging-stations-simulator.git/commitdiff
fix: avoid circular module dependency
authorJérôme Benoit <jerome.benoit@sap.com>
Thu, 23 Oct 2025 21:57:38 +0000 (23:57 +0200)
committerJérôme Benoit <jerome.benoit@sap.com>
Thu, 23 Oct 2025 21:57:38 +0000 (23:57 +0200)
Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
pnpm-lock.yaml
src/utils/Configuration.ts
src/utils/ConfigurationUtils.ts
src/worker/WorkerUtils.ts
src/worker/index.ts
tests/utils/ConfigurationUtils.test.ts
tests/worker/WorkerUtils.test.ts [new file with mode: 0644]

index 052d632c8b789c435dfeb5e9c9589b7045b01157..742144bf0fb8001057024b77a4aad66e2aaa3296 100644 (file)
@@ -5047,11 +5047,6 @@ packages:
   resolve-pkg-maps@1.0.0:
     resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
 
-  resolve@1.22.10:
-    resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==}
-    engines: {node: '>= 0.4'}
-    hasBin: true
-
   resolve@1.22.11:
     resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==}
     engines: {node: '>= 0.4'}
@@ -8115,7 +8110,7 @@ snapshots:
   brfs@2.0.2:
     dependencies:
       quote-stream: 1.0.2
-      resolve: 1.22.10
+      resolve: 1.22.11
       static-module: 3.0.4
       through2: 2.0.5
 
@@ -8134,7 +8129,7 @@ snapshots:
 
   browser-resolve@2.0.0:
     dependencies:
-      resolve: 1.22.10
+      resolve: 1.22.11
 
   browserify-aes@1.2.0:
     dependencies:
@@ -8215,7 +8210,7 @@ snapshots:
       querystring-es3: 0.2.1
       read-only-stream: 2.0.0
       readable-stream: 2.3.8
-      resolve: 1.22.10
+      resolve: 1.22.11
       shasum-object: 1.0.1
       shell-quote: 1.8.3
       stream-browserify: 3.0.0
@@ -10992,7 +10987,7 @@ snapshots:
       inherits: 2.0.4
       parents: 1.0.1
       readable-stream: 2.3.8
-      resolve: 1.22.10
+      resolve: 1.22.11
       stream-combiner2: 1.1.1
       subarg: 1.0.0
       through2: 2.0.5
@@ -11483,7 +11478,7 @@ snapshots:
       postcss: 8.5.6
       postcss-value-parser: 4.2.0
       read-cache: 1.0.0
-      resolve: 1.22.10
+      resolve: 1.22.11
 
   postcss-selector-parser@6.1.2:
     dependencies:
@@ -11746,12 +11741,6 @@ snapshots:
 
   resolve-pkg-maps@1.0.0: {}
 
-  resolve@1.22.10:
-    dependencies:
-      is-core-module: 2.16.1
-      path-parse: 1.0.7
-      supports-preserve-symlinks-flag: 1.0.0
-
   resolve@1.22.11:
     dependencies:
       is-core-module: 2.16.1
index 1ddb2d2e5c51c2f792ac79e454553fe5387e7221..2fe2890006b15609dd5b6674442f09b9cc58ebd4 100644 (file)
@@ -19,6 +19,7 @@ import {
   type WorkerConfiguration,
 } from '../types/index.js'
 import {
+  checkWorkerProcessType,
   DEFAULT_ELEMENT_ADD_DELAY,
   DEFAULT_POOL_MAX_SIZE,
   DEFAULT_POOL_MIN_SIZE,
@@ -28,7 +29,6 @@ import {
 import {
   buildPerformanceUriFilePath,
   checkWorkerElementsPerWorker,
-  checkWorkerProcessType,
   getDefaultPerformanceStorageUri,
   handleFileException,
   logPrefix,
index 034de130af91da69b495a097ed5f58650039bcdb..f1f5c0cc5929f924f777fc5b490ccac253ff6201 100644 (file)
@@ -3,7 +3,6 @@ import { dirname, join, resolve } from 'node:path'
 import { fileURLToPath, pathToFileURL } from 'node:url'
 
 import { type ElementsPerWorkerType, type FileType, StorageType } from '../types/index.js'
-import { WorkerProcessType } from '../worker/index.js'
 import { Constants } from './Constants.js'
 import { isNotEmptyString, logPrefix as utilsLogPrefix } from './Utils.js'
 
@@ -76,14 +75,6 @@ export const handleFileException = (
   throw error
 }
 
-export const checkWorkerProcessType = (workerProcessType: WorkerProcessType): void => {
-  if (!Object.values(WorkerProcessType).includes(workerProcessType)) {
-    throw new SyntaxError(
-      `Invalid worker process type '${workerProcessType}' defined in configuration`
-    )
-  }
-}
-
 export const checkWorkerElementsPerWorker = (
   elementsPerWorker: ElementsPerWorkerType | undefined
 ): void => {
index b982cf05e99d260ff4b09332b7e19cae189321e6..a5a06908dcbffa2d18ad3cef65bd6e3d2766f092 100644 (file)
@@ -1,6 +1,8 @@
 import chalk from 'chalk'
 import { getRandomValues } from 'node:crypto'
 
+import { WorkerProcessType } from './WorkerTypes.js'
+
 export const sleep = async (milliSeconds: number): Promise<NodeJS.Timeout> => {
   return await new Promise<NodeJS.Timeout>(resolve => {
     const timeout = setTimeout(() => {
@@ -30,6 +32,14 @@ export const randomizeDelay = (delay: number): number => {
   return delay + sign * randomSum
 }
 
+export const checkWorkerProcessType = (workerProcessType: WorkerProcessType): void => {
+  if (!Object.values(WorkerProcessType).includes(workerProcessType)) {
+    throw new SyntaxError(
+      `Invalid worker process type '${workerProcessType}' defined in configuration`
+    )
+  }
+}
+
 /**
  * Generates a cryptographically secure random number in the [0,1[ range
  * @returns A number in the [0,1[ range
index 32368203c86c302c24dffa1e5b41f640f3998321..003081708ff329121f2750789468734dc9c7af9c 100644 (file)
@@ -15,3 +15,4 @@ export {
   WorkerMessageEvents,
   WorkerProcessType,
 } from './WorkerTypes.js'
+export { checkWorkerProcessType } from './WorkerUtils.js'
index f90945fd2ab201ab0da595209c7595dedf52c5c6..73a4d087bde912f71b00088fffeeb4e9cf2622b1 100644 (file)
@@ -1,22 +1,77 @@
-// /* eslint-disable @typescript-eslint/no-unsafe-member-access */
-// import { expect } from '@std/expect'
-// import { describe, it } from 'node:test'
-
-// import { FileType } from '../../src/types/index.js'
-// import { handleFileException, logPrefix } from '../../src/utils/ConfigurationUtils.js'
-
-// await describe('ConfigurationUtils test suite', async () => {
-//   await it('Verify logPrefix()', () => {
-//     expect(logPrefix()).toContain(' Simulator configuration |')
-//   })
-
-//   await it('Verify handleFileException()', t => {
-//     t.mock.method(console, 'error')
-//     const error = new Error()
-//     error.code = 'ENOENT'
-//     expect(() => {
-//       handleFileException('path/to/module.js', FileType.Authorization, error, 'log prefix |')
-//     }).toThrow(error)
-//     expect(console.error.mock.calls.length).toBe(1)
-//   })
-// })
+import { expect } from '@std/expect'
+import { describe, it } from 'node:test'
+
+import { FileType, StorageType } from '../../src/types/index.js'
+import {
+  buildPerformanceUriFilePath,
+  checkWorkerElementsPerWorker,
+  getDefaultPerformanceStorageUri,
+  handleFileException,
+  logPrefix,
+} from '../../src/utils/ConfigurationUtils.js'
+
+await describe('ConfigurationUtils test suite', async () => {
+  await it('Verify logPrefix()', () => {
+    expect(logPrefix()).toContain(' Simulator configuration |')
+  })
+
+  await it('Verify buildPerformanceUriFilePath()', () => {
+    const result = buildPerformanceUriFilePath('test.json')
+    expect(result).toContain('test.json')
+    expect(result).toMatch(/^file:\/\/.*test\.json$/)
+  })
+
+  await it('Verify getDefaultPerformanceStorageUri()', () => {
+    // Test JSON_FILE storage type
+    const jsonUri = getDefaultPerformanceStorageUri(StorageType.JSON_FILE)
+    expect(jsonUri).toMatch(/^file:\/\/.*\.json$/)
+    expect(jsonUri).toContain('performanceRecords.json')
+
+    // Test SQLITE storage type
+    const sqliteUri = getDefaultPerformanceStorageUri(StorageType.SQLITE)
+    expect(sqliteUri).toMatch(/^file:\/\/.*\.db$/)
+    expect(sqliteUri).toContain('charging-stations-simulator.db')
+
+    // Test unsupported storage type
+    expect(() => {
+      getDefaultPerformanceStorageUri('unsupported' as StorageType)
+    }).toThrow(Error)
+  })
+
+  await it('Verify handleFileException()', t => {
+    const mockConsoleError = t.mock.method(console, 'error')
+    const error = new Error() as NodeJS.ErrnoException
+    error.code = 'ENOENT'
+    expect(() => {
+      handleFileException('path/to/module.js', FileType.Authorization, error, 'log prefix |')
+    }).toThrow(error)
+    expect(mockConsoleError.mock.calls.length).toBe(1)
+  })
+
+  await it('Verify checkWorkerElementsPerWorker()', () => {
+    // These calls should not throw exceptions
+    expect(() => {
+      checkWorkerElementsPerWorker(undefined)
+    }).not.toThrow()
+    expect(() => {
+      checkWorkerElementsPerWorker('auto')
+    }).not.toThrow()
+    expect(() => {
+      checkWorkerElementsPerWorker('all')
+    }).not.toThrow()
+    expect(() => {
+      checkWorkerElementsPerWorker(4)
+    }).not.toThrow()
+
+    // These calls should throw exceptions
+    expect(() => {
+      checkWorkerElementsPerWorker(0)
+    }).toThrow(RangeError)
+    expect(() => {
+      checkWorkerElementsPerWorker(-1)
+    }).toThrow(RangeError)
+    expect(() => {
+      checkWorkerElementsPerWorker(1.5)
+    }).toThrow(SyntaxError)
+  })
+})
diff --git a/tests/worker/WorkerUtils.test.ts b/tests/worker/WorkerUtils.test.ts
new file mode 100644 (file)
index 0000000..28166ad
--- /dev/null
@@ -0,0 +1,130 @@
+import { expect } from '@std/expect'
+import { describe, it } from 'node:test'
+
+import { WorkerProcessType } from '../../src/worker/WorkerTypes.js'
+import {
+  checkWorkerProcessType,
+  defaultErrorHandler,
+  defaultExitHandler,
+  randomizeDelay,
+  sleep,
+} from '../../src/worker/WorkerUtils.js'
+
+await describe('WorkerUtils test suite', async () => {
+  await it('Verify checkWorkerProcessType()', () => {
+    // Valid worker process types should not throw
+    expect(() => {
+      checkWorkerProcessType(WorkerProcessType.dynamicPool)
+    }).not.toThrow()
+    expect(() => {
+      checkWorkerProcessType(WorkerProcessType.fixedPool)
+    }).not.toThrow()
+    expect(() => {
+      checkWorkerProcessType(WorkerProcessType.workerSet)
+    }).not.toThrow()
+
+    // Invalid worker process type should throw
+    expect(() => {
+      checkWorkerProcessType('invalidType' as WorkerProcessType)
+    }).toThrow(SyntaxError)
+  })
+
+  await it('Verify sleep()', async () => {
+    const startTime = Date.now()
+    const delay = 10 // 10ms for fast test execution
+
+    const timeout = await sleep(delay)
+    const endTime = Date.now()
+    const actualDelay = endTime - startTime
+
+    // Verify timeout object is returned
+    expect(timeout).toBeDefined()
+    expect(typeof timeout).toBe('object')
+
+    // Verify actual delay is approximately correct (within reasonable tolerance)
+    expect(actualDelay).toBeGreaterThanOrEqual(delay)
+    expect(actualDelay).toBeLessThan(delay + 50) // Allow 50ms tolerance for system variance
+
+    // Clean up timeout
+    clearTimeout(timeout)
+  })
+
+  await it('Verify defaultExitHandler()', t => {
+    const mockConsoleInfo = t.mock.method(console, 'info')
+    const mockConsoleError = t.mock.method(console, 'error')
+
+    // Test successful exit (code 0)
+    defaultExitHandler(0)
+    expect(mockConsoleInfo.mock.calls.length).toBe(1)
+    expect(mockConsoleError.mock.calls.length).toBe(0)
+
+    // Reset mocks
+    mockConsoleInfo.mock.resetCalls()
+    mockConsoleError.mock.resetCalls()
+
+    // Test terminated successfully (code 1)
+    defaultExitHandler(1)
+    expect(mockConsoleInfo.mock.calls.length).toBe(1)
+    expect(mockConsoleError.mock.calls.length).toBe(0)
+
+    // Reset mocks
+    mockConsoleInfo.mock.resetCalls()
+    mockConsoleError.mock.resetCalls()
+
+    // Test error exit (code > 1)
+    defaultExitHandler(2)
+    expect(mockConsoleInfo.mock.calls.length).toBe(0)
+    expect(mockConsoleError.mock.calls.length).toBe(1)
+
+    // Test another error code
+    mockConsoleError.mock.resetCalls()
+    defaultExitHandler(5)
+    expect(mockConsoleError.mock.calls.length).toBe(1)
+  })
+
+  await it('Verify defaultErrorHandler()', t => {
+    const mockConsoleError = t.mock.method(console, 'error')
+    const testError = new Error('Test error message')
+
+    defaultErrorHandler(testError)
+
+    expect(mockConsoleError.mock.calls.length).toBe(1)
+
+    // Test with different error types
+    const syntaxError = new SyntaxError('Syntax error')
+    defaultErrorHandler(syntaxError)
+    expect(mockConsoleError.mock.calls.length).toBe(2)
+  })
+
+  await it('Verify randomizeDelay()', () => {
+    const baseDelay = 1000
+    const tolerance = baseDelay * 0.2 // 20% tolerance as per implementation
+
+    // Test multiple random variations to verify range
+    const results: number[] = []
+    for (let i = 0; i < 100; i++) {
+      const randomized = randomizeDelay(baseDelay)
+      results.push(randomized)
+
+      // Each result should be within ±20% of base delay
+      expect(randomized).toBeGreaterThanOrEqual(baseDelay - tolerance)
+      expect(randomized).toBeLessThanOrEqual(baseDelay + tolerance)
+    }
+
+    // Verify we get some variation (not all values identical)
+    const uniqueValues = new Set(results)
+    expect(uniqueValues.size).toBeGreaterThan(1)
+
+    // Test with zero delay
+    const zeroResult = randomizeDelay(0)
+    expect(zeroResult).toBeGreaterThanOrEqual(-0)
+    expect(zeroResult).toBeLessThanOrEqual(0)
+
+    // Test with negative delay (edge case)
+    const negativeDelay = -100
+    const negativeResult = randomizeDelay(negativeDelay)
+    const negativeTolerance = Math.abs(negativeDelay) * 0.2
+    expect(negativeResult).toBeGreaterThanOrEqual(negativeDelay - negativeTolerance)
+    expect(negativeResult).toBeLessThanOrEqual(negativeDelay + negativeTolerance)
+  })
+})