]> Piment Noir Git Repositories - e-mobility-charging-stations-simulator.git/commitdiff
refactor: improve helper/utility usage consistency across codebase
authorJérôme Benoit <jerome.benoit@sap.com>
Fri, 3 Apr 2026 19:23:45 +0000 (21:23 +0200)
committerJérôme Benoit <jerome.benoit@sap.com>
Fri, 3 Apr 2026 19:23:45 +0000 (21:23 +0200)
- Make worker module standalone by inlining mergeDeepRight in WorkerUtils
- Replace console/chalk with structured logger calls in Bootstrap
- Extract magic numbers to named constants (auth cache, WS reconnect)
- Use setupConnectorWithTransaction helper in transaction tests
- Remove dead test helper OCPPAuthIntegrationTest
- Fix 'shutdowning' typo in graceful shutdown log message

src/charging-station/Bootstrap.ts
src/charging-station/ChargingStation.ts
src/charging-station/ocpp/auth/cache/InMemoryAuthCache.ts
src/charging-station/ocpp/auth/factories/AuthComponentFactory.ts
src/utils/Constants.ts
src/worker/WorkerFactory.ts
src/worker/WorkerUtils.ts
tests/charging-station/ChargingStation-Transactions.test.ts
tests/helpers/OCPPAuthIntegrationTest.ts [deleted file]

index acc6388da924a8136e319d0924b711585df00370..d82574c625b07b8241b1233928eb10cc837b6d4b 100644 (file)
@@ -2,7 +2,6 @@
 
 import type { Worker } from 'node:worker_threads'
 
-import chalk from 'chalk'
 import { EventEmitter } from 'node:events'
 import { dirname, extname, join } from 'node:path'
 import process, { exit } from 'node:process'
@@ -255,10 +254,8 @@ export class Bootstrap extends EventEmitter implements IBootstrap {
                 try {
                   await this.addChargingStation(index, stationTemplateUrl.file)
                 } catch (error) {
-                  console.error(
-                    chalk.red(
-                      `Error at starting charging station with template file ${stationTemplateUrl.file}: `
-                    ),
+                  logger.error(
+                    `${this.logPrefix()} ${moduleName}.start: Error at starting charging station with template file ${stationTemplateUrl.file}:`,
                     error
                   )
                 }
@@ -271,10 +268,8 @@ export class Bootstrap extends EventEmitter implements IBootstrap {
               )
               for (const result of results) {
                 if (result.status === 'rejected') {
-                  console.error(
-                    chalk.red(
-                      `Error at starting charging station with template file ${stationTemplateUrl.file}: `
-                    ),
+                  logger.error(
+                    `${this.logPrefix()} ${moduleName}.start: Error at starting charging station with template file ${stationTemplateUrl.file}:`,
                     result.reason
                   )
                 }
@@ -284,43 +279,46 @@ export class Bootstrap extends EventEmitter implements IBootstrap {
           const workerConfiguration = Configuration.getConfigurationSection<WorkerConfiguration>(
             ConfigurationSection.worker
           )
-          console.info(
-            chalk.green(
-              `Charging stations simulator ${this.version} started with ${this.numberOfConfiguredChargingStations.toString()} configured and ${this.numberOfProvisionedChargingStations.toString()} provisioned charging station(s) from ${this.numberOfChargingStationTemplates.toString()} charging station template(s) and ${
-                Configuration.workerDynamicPoolInUse()
-                  ? // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
-                    `${workerConfiguration.poolMinSize?.toString()}/`
-                  : ''
-                // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
-              }${this.workerImplementation?.size.toString()}${
-                Configuration.workerPoolInUse()
-                  ? // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
-                    `/${workerConfiguration.poolMaxSize?.toString()}`
-                  : ''
-                // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
-              } worker(s) concurrently running in '${workerConfiguration.processType}' mode${
-                this.workerImplementation?.maxElementsPerWorker != null
-                  ? ` (${this.workerImplementation.maxElementsPerWorker.toString()} charging station(s) per worker)`
-                  : ''
-              }`
-            )
+          logger.info(
+            `${this.logPrefix()} ${moduleName}.start: Charging stations simulator ${this.version} started with ${this.numberOfConfiguredChargingStations.toString()} configured and ${this.numberOfProvisionedChargingStations.toString()} provisioned charging station(s) from ${this.numberOfChargingStationTemplates.toString()} charging station template(s) and ${
+              Configuration.workerDynamicPoolInUse()
+                ? // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
+                  `${workerConfiguration.poolMinSize?.toString()}/`
+                : ''
+              // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
+            }${this.workerImplementation?.size.toString()}${
+              Configuration.workerPoolInUse()
+                ? // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
+                  `/${workerConfiguration.poolMaxSize?.toString()}`
+                : ''
+              // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
+            } worker(s) concurrently running in '${workerConfiguration.processType}' mode${
+              this.workerImplementation?.maxElementsPerWorker != null
+                ? ` (${this.workerImplementation.maxElementsPerWorker.toString()} charging station(s) per worker)`
+                : ''
+            }`
           )
           Configuration.workerDynamicPoolInUse() &&
-            console.warn(
-              chalk.yellow(
-                'Charging stations simulator is using dynamic pool mode. This is an experimental feature with known issues.\nPlease consider using fixed pool or worker set mode instead'
-              )
+            logger.warn(
+              `${this.logPrefix()} ${moduleName}.start: Charging stations simulator is using dynamic pool mode. This is an experimental feature with known issues.\nPlease consider using fixed pool or worker set mode instead`
             )
-          console.info(chalk.green('Worker set/pool information:'), this.workerImplementation?.info)
+          logger.info(
+            `${this.logPrefix()} ${moduleName}.start: Worker set/pool information:`,
+            this.workerImplementation?.info
+          )
           this.started = true
         } finally {
           this.starting = false
         }
       } else {
-        console.error(chalk.red('Cannot start an already starting charging stations simulator'))
+        logger.error(
+          `${this.logPrefix()} ${moduleName}.start: Cannot start an already starting charging stations simulator`
+        )
       }
     } else {
-      console.error(chalk.red('Cannot start an already started charging stations simulator'))
+      logger.error(
+        `${this.logPrefix()} ${moduleName}.start: Cannot start an already started charging stations simulator`
+      )
     }
   }
 
@@ -347,17 +345,21 @@ export class Bootstrap extends EventEmitter implements IBootstrap {
           this.stopping = false
         }
       } else {
-        console.error(chalk.red('Cannot stop an already stopping charging stations simulator'))
+        logger.error(
+          `${this.logPrefix()} ${moduleName}.stop: Cannot stop an already stopping charging stations simulator`
+        )
       }
     } else {
-      console.error(chalk.red('Cannot stop an already stopped charging stations simulator'))
+      logger.error(
+        `${this.logPrefix()} ${moduleName}.stop: Cannot stop an already stopped charging stations simulator`
+      )
     }
   }
 
   private gracefulShutdown (): void {
     this.stop()
       .then(() => {
-        console.info(chalk.green('Graceful shutdown'))
+        logger.info(`${this.logPrefix()} ${moduleName}.gracefulShutdown: Graceful shutdown`)
         if (this.uiServerStarted) {
           this.uiServer.stop()
           this.uiServerStarted = false
@@ -365,7 +367,10 @@ export class Bootstrap extends EventEmitter implements IBootstrap {
         return exit(exitCodes.succeeded)
       })
       .catch((error: unknown) => {
-        console.error(chalk.red('Error while shutdowning charging stations simulator: '), error)
+        logger.error(
+          `${this.logPrefix()} ${moduleName}.gracefulShutdown: Error while shutting down charging stations simulator:`,
+          error
+        )
         exit(exitCodes.gracefulShutdownError)
       })
   }
@@ -384,16 +389,14 @@ export class Bootstrap extends EventEmitter implements IBootstrap {
         })
       }
       if (this.templateStatistics.size !== stationTemplateUrls.length) {
-        console.error(
-          chalk.red(
-            "'stationTemplateUrls' contains duplicate entries, please check your configuration"
-          )
+        logger.error(
+          `${this.logPrefix()} ${moduleName}.initializeCounters: 'stationTemplateUrls' contains duplicate entries, please check your configuration`
         )
         exit(exitCodes.duplicateChargingStationTemplateUrls)
       }
     } else {
-      console.error(
-        chalk.red("'stationTemplateUrls' not defined or empty, please check your configuration")
+      logger.error(
+        `${this.logPrefix()} ${moduleName}.initializeCounters: 'stationTemplateUrls' not defined or empty, please check your configuration`
       )
       exit(exitCodes.missingChargingStationsConfiguration)
     }
@@ -402,10 +405,8 @@ export class Bootstrap extends EventEmitter implements IBootstrap {
       Configuration.getConfigurationSection<UIServerConfiguration>(ConfigurationSection.uiServer)
         .enabled !== true
     ) {
-      console.error(
-        chalk.red(
-          "'stationTemplateUrls' has no charging station enabled and UI server is disabled, please check your configuration"
-        )
+      logger.error(
+        `${this.logPrefix()} ${moduleName}.initializeCounters: 'stationTemplateUrls' has no charging station enabled and UI server is disabled, please check your configuration`
       )
       exit(exitCodes.noChargingStationTemplates)
     }
@@ -587,8 +588,10 @@ export class Bootstrap extends EventEmitter implements IBootstrap {
         const timeoutMessage = `Timeout ${formatDurationMilliSeconds(
           Constants.STOP_CHARGING_STATIONS_TIMEOUT
         )} reached at stopping charging stations`
-        console.warn(chalk.yellow(timeoutMessage))
-        reject(new Error(timeoutMessage))
+        logger.warn(
+          `${this.logPrefix()} ${moduleName}.waitChargingStationsStopped: ${timeoutMessage}`
+        )
+        reject(new BaseError(timeoutMessage))
       }, Constants.STOP_CHARGING_STATIONS_TIMEOUT)
       waitChargingStationEvents(
         this,
index fc44f3043f86d749995042cacb524adb83bab83d..d98164b268d1db939ca5142ebad9e9536ad860c2 100644 (file)
@@ -2332,7 +2332,7 @@ export class ChargingStation extends EventEmitter {
                   this.bootNotificationResponse?.interval != null
                     ? secondsToMilliseconds(this.bootNotificationResponse.interval)
                     : Constants.DEFAULT_BOOT_NOTIFICATION_INTERVAL,
-                jitterMs: 1000,
+                jitterMs: Constants.DEFAULT_WS_RECONNECT_TIMEOUT_OFFSET,
                 retryNumber: registrationRetryCount,
               })
             )
index a0693cf06eabe075da9b3b7a57750e341d85d816..039be166c127d66fbb966239b58a17d05ec024c8 100644 (file)
@@ -121,17 +121,19 @@ export class InMemoryAuthCache implements AuthCache {
     maxEntries?: number
     rateLimit?: { enabled?: boolean; maxRequests?: number; windowMs?: number }
   }) {
-    this.defaultTtl = options?.defaultTtl ?? 3600 // 1 hour default
+    this.defaultTtl = options?.defaultTtl ?? Constants.DEFAULT_AUTH_CACHE_TTL_SECONDS
     this.maxAbsoluteLifetimeMs =
       options?.maxAbsoluteLifetimeMs ?? InMemoryAuthCache.DEFAULT_MAX_ABSOLUTE_LIFETIME_MS
     this.maxEntries = Math.max(1, options?.maxEntries ?? Constants.DEFAULT_AUTH_CACHE_MAX_ENTRIES)
     this.rateLimit = {
       enabled: options?.rateLimit?.enabled ?? false,
-      maxRequests: options?.rateLimit?.maxRequests ?? 10, // 10 requests per window
-      windowMs: options?.rateLimit?.windowMs ?? 60000, // 1 minute window
+      maxRequests:
+        options?.rateLimit?.maxRequests ?? Constants.DEFAULT_AUTH_CACHE_RATE_LIMIT_MAX_REQUESTS,
+      windowMs: options?.rateLimit?.windowMs ?? Constants.DEFAULT_AUTH_CACHE_RATE_LIMIT_WINDOW_MS,
     }
 
-    const cleanupSeconds = options?.cleanupIntervalSeconds ?? 300
+    const cleanupSeconds =
+      options?.cleanupIntervalSeconds ?? Constants.DEFAULT_AUTH_CACHE_CLEANUP_INTERVAL_SECONDS
     if (cleanupSeconds > 0) {
       const intervalMs = secondsToMilliseconds(cleanupSeconds)
       this.cleanupInterval = setInterval(() => {
index fa938a360b9a4cc6eab299032c67c591cf0788f4..81f57e1656d33ada13b29d10bec5703568da0cb4 100644 (file)
@@ -70,7 +70,7 @@ export class AuthComponentFactory {
    */
   static createAuthCache (config: AuthConfiguration): AuthCache {
     return new InMemoryAuthCache({
-      defaultTtl: config.authorizationCacheLifetime ?? 3600,
+      defaultTtl: config.authorizationCacheLifetime ?? Constants.DEFAULT_AUTH_CACHE_TTL_SECONDS,
       maxEntries: config.maxCacheEntries ?? Constants.DEFAULT_AUTH_CACHE_MAX_ENTRIES,
     })
   }
index 306ebcb5cff5530ae8d04fd6259d6897d11e9593..1dbde98f0077ed802ae7131097a50d957b3756cf 100644 (file)
@@ -22,8 +22,16 @@ export class Constants {
 
   static readonly DEFAULT_ATG_WAIT_TIME = 1000 // Ms
 
+  static readonly DEFAULT_AUTH_CACHE_CLEANUP_INTERVAL_SECONDS = 300
+
   static readonly DEFAULT_AUTH_CACHE_MAX_ENTRIES = 1000
 
+  static readonly DEFAULT_AUTH_CACHE_RATE_LIMIT_MAX_REQUESTS = 10
+
+  static readonly DEFAULT_AUTH_CACHE_RATE_LIMIT_WINDOW_MS = 60000
+
+  static readonly DEFAULT_AUTH_CACHE_TTL_SECONDS = 3600
+
   static readonly DEFAULT_BOOT_NOTIFICATION_INTERVAL = 60000 // Ms
 
   static readonly DEFAULT_CIRCULAR_BUFFER_CAPACITY = 386
index f48b91eeaf53e5a1634387d78134b6f1ed721cd4..4807c88dce374c7ba78afb7081a3eeb687ff3d78 100644 (file)
@@ -2,12 +2,12 @@ import { isMainThread } from 'node:worker_threads'
 
 import type { WorkerAbstract } from './WorkerAbstract.js'
 
-import { mergeDeepRight } from '../utils/index.js'
 import { DEFAULT_WORKER_OPTIONS } from './WorkerConstants.js'
 import { WorkerDynamicPool } from './WorkerDynamicPool.js'
 import { WorkerFixedPool } from './WorkerFixedPool.js'
 import { WorkerSet } from './WorkerSet.js'
 import { type WorkerData, type WorkerOptions, WorkerProcessType } from './WorkerTypes.js'
+import { mergeDeepRight } from './WorkerUtils.js'
 
 // eslint-disable-next-line @typescript-eslint/no-extraneous-class
 export class WorkerFactory {
index a5a06908dcbffa2d18ad3cef65bd6e3d2766f092..f10811e181a2be8a36bd089fd9862714c0910f81 100644 (file)
@@ -3,6 +3,29 @@ import { getRandomValues } from 'node:crypto'
 
 import { WorkerProcessType } from './WorkerTypes.js'
 
+const isPlainObject = (value: unknown): value is Record<string, unknown> => {
+  if (typeof value !== 'object' || value === null) return false
+  return Object.prototype.toString.call(value).slice(8, -1) === 'Object'
+}
+
+export const mergeDeepRight = <T extends object>(target: T, source: object): T => {
+  const output: Record<string, unknown> = { ...(target as Record<string, unknown>) }
+
+  if (isPlainObject(target) && isPlainObject(source)) {
+    Object.keys(source).forEach(key => {
+      const sourceValue = source[key]
+      const targetValue = (target as Record<string, unknown>)[key]
+      if (isPlainObject(sourceValue) && isPlainObject(targetValue)) {
+        output[key] = mergeDeepRight(targetValue, sourceValue)
+      } else {
+        output[key] = sourceValue
+      }
+    })
+  }
+
+  return output as T
+}
+
 export const sleep = async (milliSeconds: number): Promise<NodeJS.Timeout> => {
   return await new Promise<NodeJS.Timeout>(resolve => {
     const timeout = setTimeout(() => {
index 66ec8c3460683e37c0c2114bc800f32a175d59e0..d6d5c7980f36a35ab09e34ee4671c81445219029 100644 (file)
@@ -10,7 +10,11 @@ import type { ChargingStation } from '../../src/charging-station/index.js'
 import { OCPP16ServiceUtils } from '../../src/charging-station/ocpp/1.6/OCPP16ServiceUtils.js'
 import { OCPP20ServiceUtils } from '../../src/charging-station/ocpp/2.0/OCPP20ServiceUtils.js'
 import { OCPPVersion } from '../../src/types/index.js'
-import { standardCleanup, withMockTimers } from '../helpers/TestLifecycleHelpers.js'
+import {
+  setupConnectorWithTransaction,
+  standardCleanup,
+  withMockTimers,
+} from '../helpers/TestLifecycleHelpers.js'
 import { TEST_HEARTBEAT_INTERVAL_MS, TEST_ID_TAG } from './ChargingStationTestConstants.js'
 import { cleanupChargingStation, createMockChargingStation } from './ChargingStationTestUtils.js'
 
@@ -44,12 +48,7 @@ await describe('ChargingStation Transaction Management', async () => {
       // Arrange
       const result = createMockChargingStation({ connectorsCount: 2 })
       station = result.station
-      const connector1 = station.getConnectorStatus(1)
-      if (connector1 != null) {
-        connector1.transactionStarted = true
-        connector1.transactionId = 100
-        connector1.transactionIdTag = TEST_ID_TAG
-      }
+      setupConnectorWithTransaction(station, 1, { idTag: TEST_ID_TAG, transactionId: 100 })
 
       // Act
       const connectorId = station.getConnectorIdByTransactionId(100)
@@ -98,12 +97,7 @@ await describe('ChargingStation Transaction Management', async () => {
       })
       station = result.station
       // Get connector in EVSE 1
-      const connector1 = station.getConnectorStatus(1)
-      if (connector1 != null) {
-        connector1.transactionStarted = true
-        connector1.transactionId = 200
-        connector1.transactionIdTag = TEST_ID_TAG
-      }
+      setupConnectorWithTransaction(station, 1, { idTag: TEST_ID_TAG, transactionId: 200 })
 
       // Act
       const evseId = station.getEvseIdByTransactionId(200)
@@ -116,12 +110,7 @@ await describe('ChargingStation Transaction Management', async () => {
       // Arrange
       const result = createMockChargingStation({ connectorsCount: 2 })
       station = result.station
-      const connector1 = station.getConnectorStatus(1)
-      if (connector1 != null) {
-        connector1.transactionStarted = true
-        connector1.transactionId = 300
-        connector1.transactionIdTag = 'MY-TAG-123'
-      }
+      setupConnectorWithTransaction(station, 1, { idTag: 'MY-TAG-123', transactionId: 300 })
 
       // Act
       const idTag = station.getTransactionIdTag(300)
@@ -301,28 +290,21 @@ await describe('ChargingStation Transaction Management', async () => {
       station = result.station
 
       // Set up transactions on connectors 1, 2, and 3
-      const connector1 = station.getConnectorStatus(1)
-      const connector2 = station.getConnectorStatus(2)
-      const connector3 = station.getConnectorStatus(3)
-
-      if (connector1 != null) {
-        connector1.transactionStarted = true
-        connector1.transactionId = 100
-        connector1.transactionIdTag = 'TAG-A'
-        connector1.transactionEnergyActiveImportRegisterValue = 10000
-      }
-      if (connector2 != null) {
-        connector2.transactionStarted = true
-        connector2.transactionId = 101
-        connector2.transactionIdTag = 'TAG-B'
-        connector2.transactionEnergyActiveImportRegisterValue = 20000
-      }
-      if (connector3 != null) {
-        connector3.transactionStarted = true
-        connector3.transactionId = 102
-        connector3.transactionIdTag = 'TAG-C'
-        connector3.transactionEnergyActiveImportRegisterValue = 30000
-      }
+      setupConnectorWithTransaction(station, 1, {
+        energyImport: 10000,
+        idTag: 'TAG-A',
+        transactionId: 100,
+      })
+      setupConnectorWithTransaction(station, 2, {
+        energyImport: 20000,
+        idTag: 'TAG-B',
+        transactionId: 101,
+      })
+      setupConnectorWithTransaction(station, 3, {
+        energyImport: 30000,
+        idTag: 'TAG-C',
+        transactionId: 102,
+      })
 
       // Act & Assert - Running transactions count
       assert.strictEqual(station.getNumberOfRunningTransactions(), 3)
@@ -352,21 +334,16 @@ await describe('ChargingStation Transaction Management', async () => {
       station = result.station
 
       // Set up transaction on connector 1 (EVSE 1) and connector 3 (EVSE 2)
-      const connector1 = station.getConnectorStatus(1)
-      const connector3 = station.getConnectorStatus(3)
-
-      if (connector1 != null) {
-        connector1.transactionStarted = true
-        connector1.transactionId = 500
-        connector1.transactionIdTag = 'EVSE1-TAG'
-        connector1.transactionEnergyActiveImportRegisterValue = 15000
-      }
-      if (connector3 != null) {
-        connector3.transactionStarted = true
-        connector3.transactionId = 501
-        connector3.transactionIdTag = 'EVSE2-TAG'
-        connector3.transactionEnergyActiveImportRegisterValue = 18000
-      }
+      setupConnectorWithTransaction(station, 1, {
+        energyImport: 15000,
+        idTag: 'EVSE1-TAG',
+        transactionId: 500,
+      })
+      setupConnectorWithTransaction(station, 3, {
+        energyImport: 18000,
+        idTag: 'EVSE2-TAG',
+        transactionId: 501,
+      })
 
       // Act & Assert - Running transactions count
       assert.strictEqual(station.getNumberOfRunningTransactions(), 2)
@@ -418,12 +395,7 @@ await describe('ChargingStation Transaction Management', async () => {
       })
       station = result.station
 
-      const connector2 = station.getConnectorStatus(2)
-      if (connector2 != null) {
-        connector2.transactionStarted = true
-        connector2.transactionId = 600
-        connector2.transactionIdTag = 'EVSE-MODE-TAG'
-      }
+      setupConnectorWithTransaction(station, 2, { idTag: 'EVSE-MODE-TAG', transactionId: 600 })
 
       // Act
       const idTag = station.getTransactionIdTag(600)
diff --git a/tests/helpers/OCPPAuthIntegrationTest.ts b/tests/helpers/OCPPAuthIntegrationTest.ts
deleted file mode 100644 (file)
index 78949fa..0000000
+++ /dev/null
@@ -1,382 +0,0 @@
-import type { ChargingStation } from '../../src/charging-station/index.js'
-import type {
-  AuthConfiguration,
-  AuthorizationResult,
-  AuthRequest,
-  Identifier,
-} from '../../src/charging-station/ocpp/auth/index.js'
-
-import {
-  AuthContext,
-  AuthenticationMethod,
-  AuthorizationStatus,
-  IdentifierType,
-  OCPPAuthServiceImpl,
-} from '../../src/charging-station/ocpp/auth/index.js'
-import { logger } from '../../src/utils/index.js'
-
-const KNOWN_STRATEGIES = ['local', 'remote', 'certificate'] as const
-
-export class OCPPAuthIntegrationTest {
-  private authService: OCPPAuthServiceImpl
-  private chargingStation: ChargingStation
-
-  constructor (chargingStation: ChargingStation) {
-    this.chargingStation = chargingStation
-    this.authService = new OCPPAuthServiceImpl(chargingStation)
-  }
-
-  public async runTests (): Promise<{ failed: number; passed: number; results: string[] }> {
-    const results: string[] = []
-    let passed = 0
-    let failed = 0
-
-    logger.info(
-      `${this.chargingStation.logPrefix()} Starting OCPP Authentication Integration Tests`
-    )
-
-    try {
-      this.testServiceInitialization()
-      results.push('✅ Service Initialization - PASSED')
-      passed++
-    } catch (error) {
-      results.push(`❌ Service Initialization - FAILED: ${(error as Error).message}`)
-      failed++
-    }
-
-    try {
-      this.testConfigurationManagement()
-      results.push('✅ Configuration Management - PASSED')
-      passed++
-    } catch (error) {
-      results.push(`❌ Configuration Management - FAILED: ${(error as Error).message}`)
-      failed++
-    }
-
-    try {
-      this.testStrategySelection()
-      results.push('✅ Strategy Selection Logic - PASSED')
-      passed++
-    } catch (error) {
-      results.push(`❌ Strategy Selection Logic - FAILED: ${(error as Error).message}`)
-      failed++
-    }
-
-    try {
-      await this.testOCPP16AuthFlow()
-      results.push('✅ OCPP 1.6 Authentication Flow - PASSED')
-      passed++
-    } catch (error) {
-      results.push(`❌ OCPP 1.6 Authentication Flow - FAILED: ${(error as Error).message}`)
-      failed++
-    }
-
-    try {
-      await this.testOCPP20AuthFlow()
-      results.push('✅ OCPP 2.0 Authentication Flow - PASSED')
-      passed++
-    } catch (error) {
-      results.push(`❌ OCPP 2.0 Authentication Flow - FAILED: ${(error as Error).message}`)
-      failed++
-    }
-
-    try {
-      await this.testErrorHandling()
-      results.push('✅ Error Handling - PASSED')
-      passed++
-    } catch (error) {
-      results.push(`❌ Error Handling - FAILED: ${(error as Error).message}`)
-      failed++
-    }
-
-    try {
-      await this.testCacheOperations()
-      results.push('✅ Cache Operations - PASSED')
-      passed++
-    } catch (error) {
-      results.push(`❌ Cache Operations - FAILED: ${(error as Error).message}`)
-      failed++
-    }
-
-    try {
-      await this.testPerformanceAndStats()
-      results.push('✅ Performance and Statistics - PASSED')
-      passed++
-    } catch (error) {
-      results.push(`❌ Performance and Statistics - FAILED: ${(error as Error).message}`)
-      failed++
-    }
-
-    logger.info(
-      `${this.chargingStation.logPrefix()} Integration Tests Complete: ${String(passed)} passed, ${String(failed)} failed`
-    )
-
-    return { failed, passed, results }
-  }
-
-  private async testCacheOperations (): Promise<void> {
-    const testIdentifier: Identifier = {
-      type: IdentifierType.LOCAL,
-      value: 'CACHE_TEST_ID',
-    }
-
-    this.authService.invalidateCache(testIdentifier)
-    this.authService.clearCache()
-    await this.authService.isLocallyAuthorized(testIdentifier)
-
-    logger.debug(`${this.chargingStation.logPrefix()} Cache operations tested`)
-  }
-
-  private testConfigurationManagement (): void {
-    const originalConfiguration = this.authService.getConfiguration()
-
-    const updates: Partial<AuthConfiguration> = {
-      authorizationTimeout: 60,
-      localAuthListEnabled: false,
-      maxCacheEntries: 2000,
-    }
-
-    this.authService.updateConfiguration(updates)
-
-    const updatedConfiguration = this.authService.getConfiguration()
-
-    if (updatedConfiguration.authorizationTimeout !== 60) {
-      throw new Error('Configuration update failed: authorizationTimeout')
-    }
-
-    if (updatedConfiguration.localAuthListEnabled) {
-      throw new Error('Configuration update failed: localAuthListEnabled')
-    }
-
-    if (updatedConfiguration.maxCacheEntries !== 2000) {
-      throw new Error('Configuration update failed: maxCacheEntries')
-    }
-
-    this.authService.updateConfiguration(originalConfiguration)
-
-    logger.debug(`${this.chargingStation.logPrefix()} Configuration management test completed`)
-  }
-
-  private async testErrorHandling (): Promise<void> {
-    const invalidIdentifier: Identifier = {
-      type: IdentifierType.ISO14443,
-      value: '',
-    }
-
-    const invalidRequest: AuthRequest = {
-      allowOffline: false,
-      connectorId: 999,
-      context: AuthContext.TRANSACTION_START,
-      identifier: invalidIdentifier,
-      timestamp: new Date(),
-    }
-
-    const result = await this.authService.authenticate(invalidRequest)
-
-    if (result.status === AuthorizationStatus.ACCEPTED) {
-      throw new Error('Expected INVALID status for invalid identifier, got ACCEPTED')
-    }
-
-    try {
-      await this.authService.authorizeWithStrategy('non-existent', invalidRequest)
-      throw new Error('Expected error for non-existent strategy')
-    } catch (error) {
-      if (!(error as Error).message.includes('not found')) {
-        throw new Error('Unexpected error message for non-existent strategy')
-      }
-    }
-
-    logger.debug(`${this.chargingStation.logPrefix()} Error handling verified`)
-  }
-
-  private async testOCPP16AuthFlow (): Promise<void> {
-    const identifier: Identifier = {
-      type: IdentifierType.ISO14443,
-      value: 'VALID_ID_123',
-    }
-
-    const request: AuthRequest = {
-      allowOffline: true,
-      connectorId: 1,
-      context: AuthContext.TRANSACTION_START,
-      identifier,
-      timestamp: new Date(),
-    }
-
-    const result = await this.authService.authenticate(request)
-    this.validateAuthenticationResult(result)
-
-    const authResult = await this.authService.authorize(request)
-    this.validateAuthenticationResult(authResult)
-
-    const localResult = await this.authService.isLocallyAuthorized(identifier, 1)
-    if (localResult) {
-      this.validateAuthenticationResult(localResult)
-    }
-
-    logger.debug(`${this.chargingStation.logPrefix()} OCPP 1.6 authentication flow tested`)
-  }
-
-  private async testOCPP20AuthFlow (): Promise<void> {
-    const identifier: Identifier = {
-      type: IdentifierType.ISO15693,
-      value: 'VALID_ID_456',
-    }
-
-    const request: AuthRequest = {
-      allowOffline: false,
-      connectorId: 2,
-      context: AuthContext.TRANSACTION_START,
-      identifier,
-      timestamp: new Date(),
-    }
-
-    const contexts = [
-      AuthContext.TRANSACTION_START,
-      AuthContext.TRANSACTION_STOP,
-      AuthContext.REMOTE_START,
-      AuthContext.REMOTE_STOP,
-    ]
-
-    for (const context of contexts) {
-      const contextRequest = { ...request, context }
-      const result = await this.authService.authenticate(contextRequest)
-      this.validateAuthenticationResult(result)
-    }
-
-    logger.debug(`${this.chargingStation.logPrefix()} OCPP 2.0 authentication flow tested`)
-  }
-
-  private async testPerformanceAndStats (): Promise<void> {
-    const connectivity = this.authService.testConnectivity()
-    if (typeof connectivity !== 'boolean') {
-      throw new Error('Invalid connectivity test result')
-    }
-
-    const stats = this.authService.getStats()
-    if (typeof stats.totalRequests !== 'number') {
-      throw new Error('Invalid statistics object')
-    }
-
-    const identifier: Identifier = {
-      type: IdentifierType.ISO14443,
-      value: 'PERF_TEST_ID',
-    }
-
-    const startTime = Date.now()
-    const promises = []
-
-    for (let i = 0; i < 10; i++) {
-      const request: AuthRequest = {
-        allowOffline: true,
-        connectorId: 1,
-        context: AuthContext.TRANSACTION_START,
-        identifier: { ...identifier, value: `PERF_TEST_${String(i)}` },
-        timestamp: new Date(),
-      }
-      promises.push(this.authService.authenticate(request))
-    }
-
-    const results = await Promise.all(promises)
-    const duration = Date.now() - startTime
-
-    if (results.length !== 10) {
-      throw new Error('Not all performance test requests completed')
-    }
-
-    if (duration > 5000) {
-      throw new Error(`Performance test too slow: ${String(duration)}ms for 10 requests`)
-    }
-
-    logger.debug(
-      `${this.chargingStation.logPrefix()} Performance test: ${String(duration)}ms for 10 requests`
-    )
-  }
-
-  private testServiceInitialization (): void {
-    const availableStrategies = KNOWN_STRATEGIES.filter(
-      name => this.authService.getStrategy(name) != null
-    )
-    if (availableStrategies.length === 0) {
-      throw new Error('No authentication strategies available')
-    }
-
-    const config = this.authService.getConfiguration()
-    if (typeof config !== 'object') {
-      throw new Error('Invalid configuration object')
-    }
-
-    const stats = this.authService.getStats()
-    if (typeof stats.totalRequests !== 'number') {
-      throw new Error('Invalid authentication statistics')
-    }
-
-    logger.debug(
-      `${this.chargingStation.logPrefix()} Service initialized with ${String(availableStrategies.length)} strategies`
-    )
-  }
-
-  private testStrategySelection (): void {
-    const availableStrategies = KNOWN_STRATEGIES.filter(
-      name => this.authService.getStrategy(name) != null
-    )
-
-    if (availableStrategies.length === 0) {
-      throw new Error('No authentication strategies available')
-    }
-
-    const testIdentifier: Identifier = {
-      type: IdentifierType.ISO14443,
-      value: 'TEST123',
-    }
-
-    const isSupported = this.authService.isSupported(testIdentifier)
-    if (typeof isSupported !== 'boolean') {
-      throw new Error('Invalid support detection result')
-    }
-
-    logger.debug(`${this.chargingStation.logPrefix()} Strategy selection logic verified`)
-  }
-
-  private validateAuthenticationResult (result: AuthorizationResult): void {
-    if (typeof result.isOffline !== 'boolean') {
-      throw new Error('Authentication result missing or invalid isOffline flag')
-    }
-
-    const validStatuses = Object.values(AuthorizationStatus)
-    if (!validStatuses.includes(result.status)) {
-      throw new Error(`Invalid authorization status: ${result.status}`)
-    }
-
-    const validMethods = Object.values(AuthenticationMethod)
-    if (!validMethods.includes(result.method)) {
-      throw new Error(`Invalid authentication method: ${result.method}`)
-    }
-
-    const now = new Date()
-    const diff = now.getTime() - result.timestamp.getTime()
-    if (diff > 60000) {
-      throw new Error(`Authentication timestamp too old: ${String(diff)}ms`)
-    }
-
-    if (result.additionalInfo) {
-      if (typeof result.additionalInfo !== 'object') {
-        throw new Error('Invalid additionalInfo structure')
-      }
-    }
-  }
-}
-
-/**
- * Create and run integration tests for a charging station.
- * @param chargingStation - Charging station instance to test
- * @returns Test results with pass/fail counts and outcome messages
- */
-export async function runOCPPAuthIntegrationTests (chargingStation: ChargingStation): Promise<{
-  failed: number
-  passed: number
-  results: string[]
-}> {
-  const tester = new OCPPAuthIntegrationTest(chargingStation)
-  return await tester.runTests()
-}