]> Piment Noir Git Repositories - e-mobility-charging-stations-simulator.git/commitdiff
refactor(tests): remove aliases, extract time constants, fix naming and lint issues
authorJérôme Benoit <jerome.benoit@sap.com>
Sat, 28 Feb 2026 23:25:52 +0000 (00:25 +0100)
committerJérôme Benoit <jerome.benoit@sap.com>
Sat, 28 Feb 2026 23:25:52 +0000 (00:25 +0100)
20 files changed:
tests/charging-station/ChargingStation-Configuration.test.ts
tests/charging-station/ChargingStation-Connectors.test.ts
tests/charging-station/ChargingStation-Resilience.test.ts
tests/charging-station/ChargingStation-Transactions.test.ts
tests/charging-station/ChargingStation.test.ts
tests/charging-station/ChargingStationTestConstants.ts
tests/charging-station/ConfigurationKeyUtils.test.ts
tests/charging-station/Helpers.test.ts
tests/charging-station/ocpp/2.0/OCPP20CertificateManager.test.ts
tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-ClearCache.test.ts
tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-Reset.test.ts
tests/charging-station/ocpp/2.0/OCPP20RequestService-ISO15118.test.ts
tests/charging-station/ocpp/2.0/OCPP20ServiceUtils-TransactionEvent.test.ts
tests/charging-station/ocpp/2.0/OCPP20VariableManager.test.ts
tests/charging-station/ocpp/auth/OCPPAuthIntegration.test.ts
tests/charging-station/ocpp/auth/adapters/OCPP20AuthAdapter.test.ts
tests/charging-station/ocpp/auth/factories/AuthComponentFactory.test.ts
tests/charging-station/ocpp/auth/strategies/LocalAuthStrategy.test.ts
tests/charging-station/ui-server/UIHttpServer.test.ts
tests/charging-station/ui-server/UIServerSecurity.test.ts

index 83de22598865bb1c1aae7677c5380a690d854fe8..089715ac3e8ebc77c25f65cfd7e5ea6a84a6d248 100644 (file)
@@ -9,11 +9,9 @@ import type { ChargingStation } from '../../src/charging-station/ChargingStation
 
 import { AvailabilityType, RegistrationStatusEnumType } from '../../src/types/index.js'
 import { standardCleanup, withMockTimers } from '../helpers/TestLifecycleHelpers.js'
+import { TEST_HEARTBEAT_INTERVAL_MS, TEST_ONE_HOUR_MS } from './ChargingStationTestConstants.js'
 import { cleanupChargingStation, createMockChargingStation } from './ChargingStationTestUtils.js'
 
-// Alias for tests that reference createRealChargingStation
-const createRealChargingStation = createMockChargingStation
-
 await describe('ChargingStation Configuration Management', async () => {
   // ===== B02/B03 BOOT NOTIFICATION BEHAVIOR TESTS =====
   // These tests verify behavioral requirements, not just state detection
@@ -214,7 +212,7 @@ await describe('ChargingStation Configuration Management', async () => {
       expect(pendingStation.station.inPendingState()).toBe(true)
       expect(rejectedStation.station.inRejectedState()).toBe(true)
       expect(pendingStation.station.getHeartbeatInterval()).toBe(60000)
-      expect(rejectedStation.station.getHeartbeatInterval()).toBe(3600000)
+      expect(rejectedStation.station.getHeartbeatInterval()).toBe(TEST_ONE_HOUR_MS)
 
       // Cleanup
       cleanupChargingStation(pendingStation.station)
@@ -259,7 +257,7 @@ await describe('ChargingStation Configuration Management', async () => {
 
     await it('should return heartbeat interval in milliseconds', () => {
       // Arrange - create station with 60 second heartbeat
-      const result = createRealChargingStation({ heartbeatInterval: 60 })
+      const result = createMockChargingStation({ heartbeatInterval: 60 })
       station = result.station
 
       // Act & Assert - should convert seconds to milliseconds
@@ -268,7 +266,7 @@ await describe('ChargingStation Configuration Management', async () => {
 
     await it('should return default heartbeat interval when not explicitly configured', () => {
       // Arrange - use default heartbeat interval (TEST_HEARTBEAT_INTERVAL_SECONDS = 60)
-      const result = createRealChargingStation()
+      const result = createMockChargingStation()
       station = result.station
 
       // Act & Assert - default 60s * 1000 = 60000ms
@@ -277,16 +275,16 @@ await describe('ChargingStation Configuration Management', async () => {
 
     await it('should return connection timeout in milliseconds', () => {
       // Arrange
-      const result = createRealChargingStation()
+      const result = createMockChargingStation()
       station = result.station
 
       // Act & Assert - default connection timeout is 30 seconds
-      expect(station.getConnectionTimeout()).toBe(30000)
+      expect(station.getConnectionTimeout()).toBe(TEST_HEARTBEAT_INTERVAL_MS)
     })
 
     await it('should return authorize remote TX requests as boolean', () => {
       // Arrange - create station which defaults to false for AuthorizeRemoteTxRequests
-      const result = createRealChargingStation()
+      const result = createMockChargingStation()
       station = result.station
 
       // Act & Assert - getAuthorizeRemoteTxRequests returns boolean
@@ -296,7 +294,7 @@ await describe('ChargingStation Configuration Management', async () => {
 
     await it('should return local auth list enabled as boolean', () => {
       // Arrange
-      const result = createRealChargingStation()
+      const result = createMockChargingStation()
       station = result.station
 
       // Act & Assert - getLocalAuthListEnabled returns boolean
@@ -308,7 +306,7 @@ await describe('ChargingStation Configuration Management', async () => {
 
     await it('should call saveOcppConfiguration without throwing', () => {
       // Arrange
-      const result = createRealChargingStation()
+      const result = createMockChargingStation()
       station = result.station
 
       // Act & Assert - should not throw
@@ -317,7 +315,7 @@ await describe('ChargingStation Configuration Management', async () => {
 
     await it('should have ocppConfiguration object with configurationKey array', () => {
       // Arrange
-      const result = createRealChargingStation()
+      const result = createMockChargingStation()
       station = result.station
 
       // Act & Assert - configuration structure should be present
@@ -330,13 +328,13 @@ await describe('ChargingStation Configuration Management', async () => {
 
     await it('should allow updating heartbeat interval', () => {
       // Arrange - create with 60 second interval
-      const result = createRealChargingStation({ heartbeatInterval: 60 })
+      const result = createMockChargingStation({ heartbeatInterval: 60 })
       station = result.station
       const initialInterval = station.getHeartbeatInterval()
       expect(initialInterval).toBe(60000)
 
       // Act - simulate configuration change by creating new station with different interval
-      const result2 = createRealChargingStation({ heartbeatInterval: 120 })
+      const result2 = createMockChargingStation({ heartbeatInterval: 120 })
       const station2 = result2.station
 
       // Assert - different configurations have different intervals
@@ -349,7 +347,7 @@ await describe('ChargingStation Configuration Management', async () => {
 
     await it('should support setSupervisionUrl method if available', () => {
       // Arrange
-      const result = createRealChargingStation()
+      const result = createMockChargingStation()
       station = result.station
 
       // Act & Assert - setSupervisionUrl should be a function if available
@@ -365,7 +363,7 @@ await describe('ChargingStation Configuration Management', async () => {
 
     await it('should have template file reference', () => {
       // Arrange
-      const result = createRealChargingStation({ templateFile: 'custom-template.json' })
+      const result = createMockChargingStation({ templateFile: 'custom-template.json' })
       station = result.station
 
       // Act & Assert - station info should have template reference
@@ -374,7 +372,7 @@ await describe('ChargingStation Configuration Management', async () => {
 
     await it('should have hashId for configuration persistence', () => {
       // Arrange
-      const result = createRealChargingStation()
+      const result = createMockChargingStation()
       station = result.station
 
       // Act & Assert - hashId is used for configuration file naming
@@ -384,7 +382,7 @@ await describe('ChargingStation Configuration Management', async () => {
 
     await it('should preserve station info properties for persistence', () => {
       // Arrange
-      const result = createRealChargingStation({
+      const result = createMockChargingStation({
         baseName: 'PERSIST-CS',
         index: 5,
       })
@@ -399,7 +397,7 @@ await describe('ChargingStation Configuration Management', async () => {
 
     await it('should track configuration file path via templateFile', () => {
       // Arrange
-      const result = createRealChargingStation()
+      const result = createMockChargingStation()
       station = result.station
 
       // Act & Assert - templateFile is used to track configuration source
@@ -409,7 +407,7 @@ await describe('ChargingStation Configuration Management', async () => {
 
     await it('should use mocked file system without real file writes', () => {
       // Arrange
-      const result = createRealChargingStation()
+      const result = createMockChargingStation()
       station = result.station
       const mocks = result.mocks
 
index 354eb6b6a3fb3d48209501cd804feaf97e239903..4ea1112fed5cb713d7959e71b66acf431f173c04 100644 (file)
@@ -9,13 +9,11 @@ import type { ChargingStation } from '../../src/charging-station/ChargingStation
 
 import { RegistrationStatusEnumType } from '../../src/types/index.js'
 import { standardCleanup } from '../helpers/TestLifecycleHelpers.js'
+import { TEST_ONE_HOUR_MS } from './ChargingStationTestConstants.js'
 import { cleanupChargingStation, createMockChargingStation } from './ChargingStationTestUtils.js'
 
-// Alias for tests that reference createRealChargingStation
-const createRealChargingStation = createMockChargingStation
-
 await describe('ChargingStation Connector and EVSE State', async () => {
-  await describe('Connector Query Tests', async () => {
+  await describe('Connector Query', async () => {
     let station: ChargingStation | undefined
 
     beforeEach(() => {
@@ -98,7 +96,7 @@ await describe('ChargingStation Connector and EVSE State', async () => {
     })
   })
 
-  await describe('Connector 0 (Shared Power) Tests', async () => {
+  await describe('Connector 0 (Shared Power)', async () => {
     let station: ChargingStation | undefined
 
     beforeEach(() => {
@@ -129,7 +127,7 @@ await describe('ChargingStation Connector and EVSE State', async () => {
     })
   })
 
-  await describe('EVSE Query Tests (non-EVSE mode)', async () => {
+  await describe('EVSE Query (non-EVSE mode)', async () => {
     let station: ChargingStation | undefined
 
     beforeEach(() => {
@@ -165,7 +163,7 @@ await describe('ChargingStation Connector and EVSE State', async () => {
     })
   })
 
-  await describe('EVSE Mode Tests', async () => {
+  await describe('EVSE Mode', async () => {
     let station: ChargingStation | undefined
 
     beforeEach(() => {
@@ -307,7 +305,7 @@ await describe('ChargingStation Connector and EVSE State', async () => {
 
     await it('should return true for inAcceptedState when boot status is ACCEPTED', () => {
       // Arrange
-      const result = createRealChargingStation({
+      const result = createMockChargingStation({
         bootNotificationStatus: RegistrationStatusEnumType.ACCEPTED,
       })
       station = result.station
@@ -321,7 +319,7 @@ await describe('ChargingStation Connector and EVSE State', async () => {
 
     await it('should return true for inPendingState when boot status is PENDING', () => {
       // Arrange
-      const result = createRealChargingStation({
+      const result = createMockChargingStation({
         bootNotificationStatus: RegistrationStatusEnumType.PENDING,
       })
       station = result.station
@@ -335,7 +333,7 @@ await describe('ChargingStation Connector and EVSE State', async () => {
 
     await it('should return true for inRejectedState when boot status is REJECTED', () => {
       // Arrange
-      const result = createRealChargingStation({
+      const result = createMockChargingStation({
         bootNotificationStatus: RegistrationStatusEnumType.REJECTED,
       })
       station = result.station
@@ -349,7 +347,7 @@ await describe('ChargingStation Connector and EVSE State', async () => {
 
     await it('should return true for inUnknownState when boot notification response is null', () => {
       // Arrange - create station with default accepted status, then delete the response
-      const result = createRealChargingStation({ connectorsCount: 1 })
+      const result = createMockChargingStation({ connectorsCount: 1 })
       station = result.station
 
       // Act - simulate unknown state by clearing boot notification response
@@ -364,7 +362,7 @@ await describe('ChargingStation Connector and EVSE State', async () => {
 
     await it('should allow state transitions from PENDING to ACCEPTED', () => {
       // Arrange
-      const result = createRealChargingStation({
+      const result = createMockChargingStation({
         bootNotificationStatus: RegistrationStatusEnumType.PENDING,
       })
       station = result.station
@@ -381,7 +379,7 @@ await describe('ChargingStation Connector and EVSE State', async () => {
 
     await it('should allow state transitions from PENDING to REJECTED', () => {
       // Arrange
-      const result = createRealChargingStation({
+      const result = createMockChargingStation({
         bootNotificationStatus: RegistrationStatusEnumType.PENDING,
       })
       station = result.station
@@ -416,7 +414,7 @@ await describe('ChargingStation Connector and EVSE State', async () => {
       station = result.station
       const reservation = {
         connectorId: 1,
-        expiryDate: new Date(Date.now() + 3600000), // 1 hour from now
+        expiryDate: new Date(Date.now() + TEST_ONE_HOUR_MS), // 1 hour from now
         idTag: 'test-tag-1',
         reservationId: 101,
       }
@@ -437,13 +435,13 @@ await describe('ChargingStation Connector and EVSE State', async () => {
       station = result.station
       const firstReservation = {
         connectorId: 1,
-        expiryDate: new Date(Date.now() + 3600000),
+        expiryDate: new Date(Date.now() + TEST_ONE_HOUR_MS),
         idTag: 'tag-1',
         reservationId: 201,
       }
       const secondReservation = {
         connectorId: 2,
-        expiryDate: new Date(Date.now() + 3600000),
+        expiryDate: new Date(Date.now() + TEST_ONE_HOUR_MS),
         idTag: 'tag-2',
         reservationId: 201, // Same ID
       }
@@ -465,7 +463,7 @@ await describe('ChargingStation Connector and EVSE State', async () => {
       station = result.station
       const reservation = {
         connectorId: 1,
-        expiryDate: new Date(Date.now() + 3600000),
+        expiryDate: new Date(Date.now() + TEST_ONE_HOUR_MS),
         idTag: 'test-tag-expired',
         reservationId: 301,
       }
@@ -486,7 +484,7 @@ await describe('ChargingStation Connector and EVSE State', async () => {
       station = result.station
       const reservation = {
         connectorId: 1,
-        expiryDate: new Date(Date.now() + 3600000),
+        expiryDate: new Date(Date.now() + TEST_ONE_HOUR_MS),
         idTag: 'test-tag-replace',
         reservationId: 401,
       }
@@ -507,7 +505,7 @@ await describe('ChargingStation Connector and EVSE State', async () => {
       station = result.station
       const reservation = {
         connectorId: 2,
-        expiryDate: new Date(Date.now() + 3600000),
+        expiryDate: new Date(Date.now() + TEST_ONE_HOUR_MS),
         idTag: 'query-test-id',
         reservationId: 501,
       }
@@ -528,7 +526,7 @@ await describe('ChargingStation Connector and EVSE State', async () => {
       station = result.station
       const reservation = {
         connectorId: 1,
-        expiryDate: new Date(Date.now() + 3600000),
+        expiryDate: new Date(Date.now() + TEST_ONE_HOUR_MS),
         idTag: 'search-by-tag',
         reservationId: 601,
       }
@@ -549,7 +547,7 @@ await describe('ChargingStation Connector and EVSE State', async () => {
       station = result.station
       const reservation = {
         connectorId: 2,
-        expiryDate: new Date(Date.now() + 3600000),
+        expiryDate: new Date(Date.now() + TEST_ONE_HOUR_MS),
         idTag: 'connector-search',
         reservationId: 701,
       }
@@ -570,7 +568,7 @@ await describe('ChargingStation Connector and EVSE State', async () => {
       station = result.station
       const reservation = {
         connectorId: 1,
-        expiryDate: new Date(Date.now() + 3600000),
+        expiryDate: new Date(Date.now() + TEST_ONE_HOUR_MS),
         idTag: 'reservable-check',
         reservationId: 801,
       }
@@ -613,13 +611,13 @@ await describe('ChargingStation Connector and EVSE State', async () => {
       station = result.station
       const reservation1 = {
         connectorId: 1,
-        expiryDate: new Date(Date.now() + 3600000),
+        expiryDate: new Date(Date.now() + TEST_ONE_HOUR_MS),
         idTag: 'multi-test-1',
         reservationId: 1001,
       }
       const reservation2 = {
         connectorId: 2,
-        expiryDate: new Date(Date.now() + 3600000),
+        expiryDate: new Date(Date.now() + TEST_ONE_HOUR_MS),
         idTag: 'multi-test-2',
         reservationId: 1002,
       }
index e4756de0cb23a5a2f52343b3776f67d9785197f4..c36966d21c29f90c19101d22797a008731a93585 100644 (file)
@@ -9,6 +9,7 @@ import type { ChargingStation } from '../../src/charging-station/ChargingStation
 
 import { RegistrationStatusEnumType } from '../../src/types/index.js'
 import { standardCleanup } from '../helpers/TestLifecycleHelpers.js'
+import { TEST_HEARTBEAT_INTERVAL_MS } from './ChargingStationTestConstants.js'
 import { cleanupChargingStation, createMockChargingStation } from './ChargingStationTestUtils.js'
 
 await describe('ChargingStation Error Recovery and Resilience', async () => {
@@ -251,7 +252,7 @@ await describe('ChargingStation Error Recovery and Resilience', async () => {
     // Set up a heartbeat timer (simulated)
     station.heartbeatSetInterval = setInterval(() => {
       /* empty */
-    }, 30000) as unknown as NodeJS.Timeout
+    }, TEST_HEARTBEAT_INTERVAL_MS) as unknown as NodeJS.Timeout
 
     // Act - Cleanup station
     cleanupChargingStation(station)
index 20e2ae575149cd7457549b3ee5d063a64fcd5554..e7b6915df4fe7a44be6f056034fb3b367d7b8e67 100644 (file)
@@ -8,11 +8,11 @@ import { afterEach, beforeEach, describe, it } from 'node:test'
 import type { ChargingStation } from '../../src/charging-station/ChargingStation.js'
 
 import { standardCleanup, withMockTimers } from '../helpers/TestLifecycleHelpers.js'
-import { TEST_ID_TAG } from './ChargingStationTestConstants.js'
+import { TEST_HEARTBEAT_INTERVAL_MS, TEST_ID_TAG } from './ChargingStationTestConstants.js'
 import { cleanupChargingStation, createMockChargingStation } from './ChargingStationTestUtils.js'
 
 await describe('ChargingStation Transaction Management', async () => {
-  await describe('Transaction Query Tests', async () => {
+  await describe('Transaction Query', async () => {
     let station: ChargingStation | undefined
 
     beforeEach(() => {
@@ -175,7 +175,7 @@ await describe('ChargingStation Transaction Management', async () => {
     })
   })
 
-  await describe('Energy Meter Tests', async () => {
+  await describe('Energy Meter', async () => {
     let station: ChargingStation | undefined
 
     beforeEach(() => {
@@ -466,7 +466,10 @@ await describe('ChargingStation Transaction Management', async () => {
     await it('should create interval when startHeartbeat() is called with valid interval', async t => {
       await withMockTimers(t, ['setInterval'], () => {
         // Arrange
-        const result = createMockChargingStation({ connectorsCount: 1, heartbeatInterval: 30000 })
+        const result = createMockChargingStation({
+          connectorsCount: 1,
+          heartbeatInterval: TEST_HEARTBEAT_INTERVAL_MS,
+        })
         station = result.station
 
         // Act
@@ -481,7 +484,10 @@ await describe('ChargingStation Transaction Management', async () => {
     await it('should restart heartbeat interval when restartHeartbeat() is called', async t => {
       await withMockTimers(t, ['setInterval'], () => {
         // Arrange
-        const result = createMockChargingStation({ connectorsCount: 1, heartbeatInterval: 30000 })
+        const result = createMockChargingStation({
+          connectorsCount: 1,
+          heartbeatInterval: TEST_HEARTBEAT_INTERVAL_MS,
+        })
         station = result.station
         station.startHeartbeat()
         const firstInterval = station.heartbeatSetInterval
@@ -500,7 +506,10 @@ await describe('ChargingStation Transaction Management', async () => {
     await it('should not create heartbeat interval if already started', async t => {
       await withMockTimers(t, ['setInterval'], () => {
         // Arrange
-        const result = createMockChargingStation({ connectorsCount: 1, heartbeatInterval: 30000 })
+        const result = createMockChargingStation({
+          connectorsCount: 1,
+          heartbeatInterval: TEST_HEARTBEAT_INTERVAL_MS,
+        })
         station = result.station
         station.startHeartbeat()
         const firstInterval = station.heartbeatSetInterval
index 47f09ac95874e88c6d0ede8f026e420bf13f2312..9f86f080698f97a6be9382c8398f6ba349fc27d7 100644 (file)
@@ -9,7 +9,7 @@
  * - ChargingStation-Configuration.test.ts: boot notification, config persistence, WebSocket, error handling
  */
 import { expect } from '@std/expect'
-import { afterEach, describe, it } from 'node:test'
+import { afterEach, beforeEach, describe, it } from 'node:test'
 
 import type { ChargingStation } from '../../src/charging-station/ChargingStation.js'
 
@@ -17,6 +17,7 @@ import { RegistrationStatusEnumType } from '../../src/types/index.js'
 import { standardCleanup } from '../helpers/TestLifecycleHelpers.js'
 import {
   TEST_ID_TAG,
+  TEST_ONE_HOUR_MS,
   TEST_TRANSACTION_ENERGY_WH,
   TEST_TRANSACTION_ID,
 } from './ChargingStationTestConstants.js'
@@ -29,8 +30,8 @@ import {
   WebSocketReadyState,
 } from './ChargingStationTestUtils.js'
 
-await describe('ChargingStation Integration Tests', async () => {
-  await describe('Test Utilities Verification', async () => {
+await describe('ChargingStation', async () => {
+  await describe('Test Utilities', async () => {
     afterEach(() => {
       standardCleanup()
     })
@@ -116,15 +117,19 @@ await describe('ChargingStation Integration Tests', async () => {
     })
   })
 
-  await describe('Cross-Domain Integration', async () => {
+  await describe('Cross-Domain', async () => {
+    let station: ChargingStation | undefined
+
+    beforeEach(() => {
+      station = undefined
+    })
+
     afterEach(() => {
       standardCleanup()
       if (station != null) {
         cleanupChargingStation(station)
       }
     })
-    let station: ChargingStation | undefined
-
     await it('should support full lifecycle with transactions', async () => {
       // Create station
       const result = createMockChargingStation({ connectorsCount: 2 })
@@ -198,7 +203,7 @@ await describe('ChargingStation Integration Tests', async () => {
       // Add reservation
       const reservation = {
         connectorId: 1,
-        expiryDate: new Date(Date.now() + 3600000),
+        expiryDate: new Date(Date.now() + TEST_ONE_HOUR_MS),
         idTag: 'RESERVATION-TAG',
         reservationId: 1,
       }
@@ -246,7 +251,7 @@ await describe('ChargingStation Integration Tests', async () => {
     })
   })
 
-  await describe('Mock Reset Verification', async () => {
+  await describe('Mock Reset', async () => {
     await it('should reset singleton mocks between tests', () => {
       // First test - create and use mocks
       const result1 = createMockChargingStation()
index 08608e4464e7f17333c75d4fbdf1f3a5911f0758..d69ec4e8204a7e1285322253a230236e876d4543 100644 (file)
@@ -20,6 +20,9 @@ export const TEST_CHARGING_STATION_HASH_ID = 'cs-test-hash-001'
  * Test values for timing-related configuration and expectations
  */
 export const TEST_HEARTBEAT_INTERVAL_SECONDS = 60
+export const TEST_HEARTBEAT_INTERVAL_MS = 30000
+export const TEST_AUTHORIZATION_TIMEOUT_MS = 30000
+export const TEST_ONE_HOUR_MS = 3600000
 
 /**
  * Charging Station Information
index f0b6191a50e676e81825be90dbdf825b1008b1c7..b41f6fac5828e377ccbd52af1425efc03bdc574e 100644 (file)
@@ -27,49 +27,75 @@ await describe('ConfigurationKeyUtils', async () => {
     standardCleanup()
     mock.restoreAll()
   })
-  await describe('getConfigurationKey()', async () => {
+  await describe('GetConfigurationKey', async () => {
     await it('should return undefined when configurationKey array is missing', () => {
+      // Arrange
       const { station: cs } = createMockChargingStation()
       // Simulate missing configurationKey array
       cs.ocppConfiguration = {} as Partial<ChargingStationOcppConfiguration>
+
+      // Act & Assert
       expect(getConfigurationKey(cs, TEST_KEY_1)).toBeUndefined()
     })
 
     await it('should find existing key (case-sensitive)', () => {
+      // Arrange
       const { station: cs } = createMockChargingStation()
       addConfigurationKey(cs, TEST_KEY_1, VALUE_A, undefined, { save: false })
+
+      // Act
       const k = getConfigurationKey(cs, TEST_KEY_1)
+
+      // Assert
       expect(k?.key).toBe(TEST_KEY_1)
       expect(k?.value).toBe(VALUE_A)
     })
 
     await it('should respect case sensitivity (no match)', () => {
+      // Arrange
       const { station: cs } = createMockChargingStation()
       addConfigurationKey(cs, MIXED_CASE_KEY, VALUE_A, undefined, { save: false })
+
+      // Act & Assert
       expect(getConfigurationKey(cs, MIXED_CASE_KEY.toLowerCase())).toBeUndefined()
     })
 
     await it('should support caseInsensitive lookup', () => {
+      // Arrange
       const { station: cs } = createMockChargingStation()
       addConfigurationKey(cs, MIXED_CASE_KEY, VALUE_A, undefined, { save: false })
+
+      // Act
       const k = getConfigurationKey(cs, MIXED_CASE_KEY.toLowerCase(), true)
+
+      // Assert
       expect(k?.key).toBe(MIXED_CASE_KEY)
     })
   })
 
-  await describe('addConfigurationKey()', async () => {
+  await describe('AddConfigurationKey', async () => {
     await it('should no-op when configurationKey array missing', () => {
+      // Arrange
       const { station: cs } = createMockChargingStation()
       // Simulate missing configurationKey array
       cs.ocppConfiguration = {} as Partial<ChargingStationOcppConfiguration>
+
+      // Act
       addConfigurationKey(cs, TEST_KEY_1, VALUE_A)
+
+      // Assert
       expect(getConfigurationKey(cs, TEST_KEY_1)).toBeUndefined()
     })
 
     await it('should add new key with default options', () => {
+      // Arrange
       const { station: cs } = createMockChargingStation()
+
+      // Act
       addConfigurationKey(cs, TEST_KEY_1, VALUE_A, undefined, { save: false })
       const k = getConfigurationKey(cs, TEST_KEY_1)
+
+      // Assert
       expect(k).toBeDefined()
       expect(k?.value).toBe(VALUE_A)
       // defaults
@@ -79,7 +105,10 @@ await describe('ConfigurationKeyUtils', async () => {
     })
 
     await it('should add new key with custom options', () => {
+      // Arrange
       const { station: cs } = createMockChargingStation()
+
+      // Act
       addConfigurationKey(
         cs,
         TEST_KEY_1,
@@ -88,15 +117,20 @@ await describe('ConfigurationKeyUtils', async () => {
         { save: false }
       )
       const k = getConfigurationKey(cs, TEST_KEY_1)
+
+      // Assert
       expect(k?.readonly).toBe(true)
       expect(k?.reboot).toBe(true)
       expect(k?.visible).toBe(false)
     })
 
     await it('should log error and not overwrite value when key exists and overwrite=false', t => {
+      // Arrange
       const { station: cs } = createMockChargingStation()
       addConfigurationKey(cs, TEST_KEY_1, VALUE_A, { readonly: false }, { save: false })
       const errorMock = t.mock.method(logger, 'error')
+
+      // Act
       // Attempt to add same key with different value and option change
       addConfigurationKey(
         cs,
@@ -106,6 +140,8 @@ await describe('ConfigurationKeyUtils', async () => {
         { overwrite: false, save: false }
       )
       const k = getConfigurationKey(cs, TEST_KEY_1)
+
+      // Assert
       // value unchanged
       expect(k?.value).toBe(VALUE_A)
       // options updated only where differing (all provided differ)
@@ -116,6 +152,7 @@ await describe('ConfigurationKeyUtils', async () => {
     })
 
     await it('should log error and leave key untouched when identical options & value attempted (overwrite=false)', t => {
+      // Arrange
       const { station: cs } = createMockChargingStation()
       addConfigurationKey(
         cs,
@@ -125,6 +162,8 @@ await describe('ConfigurationKeyUtils', async () => {
         { save: false }
       )
       const errorMock = t.mock.method(logger, 'error')
+
+      // Act
       // Attempt to add same key with identical value and options
       addConfigurationKey(
         cs,
@@ -134,6 +173,8 @@ await describe('ConfigurationKeyUtils', async () => {
         { overwrite: false, save: false }
       )
       const k = getConfigurationKey(cs, TEST_KEY_1)
+
+      // Assert
       expect(k?.value).toBe(VALUE_A)
       expect(k?.readonly).toBe(true)
       expect(k?.reboot).toBe(false)
@@ -142,8 +183,11 @@ await describe('ConfigurationKeyUtils', async () => {
     })
 
     await it('should overwrite existing key value and options when overwrite=true', () => {
+      // Arrange
       const { station: cs } = createMockChargingStation()
       addConfigurationKey(cs, TEST_KEY_1, VALUE_A, { readonly: false }, { save: false })
+
+      // Act
       addConfigurationKey(
         cs,
         TEST_KEY_1,
@@ -152,6 +196,8 @@ await describe('ConfigurationKeyUtils', async () => {
         { overwrite: true, save: false }
       )
       const k = getConfigurationKey(cs, TEST_KEY_1)
+
+      // Assert
       expect(k?.value).toBe(VALUE_B)
       expect(k?.readonly).toBe(true)
       expect(k?.reboot).toBe(true)
@@ -159,8 +205,11 @@ await describe('ConfigurationKeyUtils', async () => {
     })
 
     await it('should caseInsensitive overwrite update existing differently cased key', () => {
+      // Arrange
       const { station: cs } = createMockChargingStation()
       addConfigurationKey(cs, MIXED_CASE_KEY, VALUE_A, undefined, { save: false })
+
+      // Act
       addConfigurationKey(
         cs,
         MIXED_CASE_KEY.toLowerCase(),
@@ -169,35 +218,50 @@ await describe('ConfigurationKeyUtils', async () => {
         { caseInsensitive: true, overwrite: true, save: false }
       )
       const k = getConfigurationKey(cs, MIXED_CASE_KEY)
+
+      // Assert
       expect(k?.value).toBe(VALUE_B)
       expect(k?.readonly).toBe(true)
     })
 
     await it('should case-insensitive false create separate key with different case', () => {
+      // Arrange
       const { station: cs } = createMockChargingStation()
       addConfigurationKey(cs, MIXED_CASE_KEY, VALUE_A, undefined, { save: false })
+
+      // Act
       addConfigurationKey(cs, MIXED_CASE_KEY.toLowerCase(), VALUE_B, undefined, {
         overwrite: true,
         save: false,
       })
       const orig = getConfigurationKey(cs, MIXED_CASE_KEY)
       const second = getConfigurationKey(cs, MIXED_CASE_KEY.toLowerCase())
+
+      // Assert
       expect(orig).toBeDefined()
       expect(second).toBeDefined()
       expect(orig).not.toBe(second)
     })
 
     await it('should call saveOcppConfiguration when params.save=true (new key)', t => {
+      // Arrange
       const { station: cs } = createMockChargingStation()
       const saveMock = t.mock.method(cs, 'saveOcppConfiguration')
+
+      // Act
       addConfigurationKey(cs, TEST_KEY_1, VALUE_A, undefined, { save: true })
+
+      // Assert
       expect(saveMock.mock.calls.length).toBe(1)
     })
 
     await it('should call saveOcppConfiguration when overwriting existing key and save=true', t => {
+      // Arrange
       const { station: cs } = createMockChargingStation()
       addConfigurationKey(cs, TEST_KEY_1, VALUE_A, undefined, { save: false })
       const saveMock = t.mock.method(cs, 'saveOcppConfiguration')
+
+      // Act
       addConfigurationKey(
         cs,
         TEST_KEY_1,
@@ -205,66 +269,103 @@ await describe('ConfigurationKeyUtils', async () => {
         { readonly: true },
         { overwrite: true, save: true }
       )
+
+      // Assert
       expect(saveMock.mock.calls.length).toBe(1)
     })
   })
 
-  await describe('setConfigurationKeyValue()', async () => {
+  await describe('SetConfigurationKeyValue', async () => {
     await it('should return undefined and log error for non-existing key', t => {
+      // Arrange
       const { station: cs } = createMockChargingStation()
       const errorMock = t.mock.method(logger, 'error')
+
+      // Act
       const res = setConfigurationKeyValue(cs, TEST_KEY_1, VALUE_A)
+
+      // Assert
       expect(res).toBeUndefined()
       expect(errorMock.mock.calls.length).toBe(1)
     })
 
     await it('should return undefined without logging when configurationKey array missing', t => {
+      // Arrange
       const { station: cs } = createMockChargingStation()
       // Simulate missing configurationKey array
       cs.ocppConfiguration = {} as Partial<ChargingStationOcppConfiguration>
       const errorMock = t.mock.method(logger, 'error')
+
+      // Act
       const res = setConfigurationKeyValue(cs, TEST_KEY_1, VALUE_A)
+
+      // Assert
       expect(res).toBeUndefined()
       expect(errorMock.mock.calls.length).toBe(0)
     })
 
     await it('should update existing key value and save', t => {
+      // Arrange
       const { station: cs } = createMockChargingStation()
       addConfigurationKey(cs, TEST_KEY_1, VALUE_A, undefined, { save: false })
       const saveMock = t.mock.method(cs, 'saveOcppConfiguration')
+
+      // Act
       const updated = setConfigurationKeyValue(cs, TEST_KEY_1, VALUE_B)
+
+      // Assert
       expect(updated?.value).toBe(VALUE_B)
       expect(saveMock.mock.calls.length).toBe(1)
     })
 
     await it('should caseInsensitive value update work', () => {
+      // Arrange
       const { station: cs } = createMockChargingStation()
       addConfigurationKey(cs, MIXED_CASE_KEY, VALUE_A, undefined, { save: false })
+
+      // Act
       const updated = setConfigurationKeyValue(cs, MIXED_CASE_KEY.toLowerCase(), VALUE_B, true)
+
+      // Assert
       expect(updated?.value).toBe(VALUE_B)
     })
   })
 
-  await describe('deleteConfigurationKey()', async () => {
+  await describe('DeleteConfigurationKey', async () => {
     await it('should return undefined when configurationKey array missing', () => {
+      // Arrange
       const { station: cs } = createMockChargingStation()
       // Simulate missing configurationKey array
       cs.ocppConfiguration = {} as Partial<ChargingStationOcppConfiguration>
+
+      // Act
       const res = deleteConfigurationKey(cs, TEST_KEY_1)
+
+      // Assert
       expect(res).toBeUndefined()
     })
 
     await it('should return undefined when key does not exist', () => {
+      // Arrange
       const { station: cs } = createMockChargingStation()
+
+      // Act
       const res = deleteConfigurationKey(cs, TEST_KEY_1)
+
+      // Assert
       expect(res).toBeUndefined()
     })
 
     await it('should delete existing key and save by default', t => {
+      // Arrange
       const { station: cs } = createMockChargingStation()
       addConfigurationKey(cs, TEST_KEY_1, VALUE_A, undefined, { save: false })
       const saveMock = t.mock.method(cs, 'saveOcppConfiguration')
+
+      // Act
       const deleted = deleteConfigurationKey(cs, TEST_KEY_1)
+
+      // Assert
       expect(Array.isArray(deleted)).toBe(true)
       expect(deleted).toHaveLength(1)
       expect(deleted?.[0].key).toBe(TEST_KEY_1)
@@ -273,21 +374,31 @@ await describe('ConfigurationKeyUtils', async () => {
     })
 
     await it('should not save when params.save=false', t => {
+      // Arrange
       const { station: cs } = createMockChargingStation()
       addConfigurationKey(cs, TEST_KEY_1, VALUE_A, undefined, { save: false })
       const saveMock = t.mock.method(cs, 'saveOcppConfiguration')
+
+      // Act
       const deleted = deleteConfigurationKey(cs, TEST_KEY_1, { save: false })
+
+      // Assert
       expect(deleted).toHaveLength(1)
       expect(saveMock.mock.calls.length).toBe(0)
     })
 
     await it('should caseInsensitive deletion remove key with different case', () => {
+      // Arrange
       const { station: cs } = createMockChargingStation()
       addConfigurationKey(cs, MIXED_CASE_KEY, VALUE_A, undefined, { save: false })
+
+      // Act
       const deleted = deleteConfigurationKey(cs, MIXED_CASE_KEY.toLowerCase(), {
         caseInsensitive: true,
         save: false,
       })
+
+      // Assert
       expect(deleted).toHaveLength(1)
       expect(getConfigurationKey(cs, MIXED_CASE_KEY)).toBeUndefined()
     })
@@ -295,11 +406,16 @@ await describe('ConfigurationKeyUtils', async () => {
 
   await describe('Combined scenarios', async () => {
     await it('should add then set then delete lifecycle', () => {
+      // Arrange
       const { station: cs } = createMockChargingStation()
       addConfigurationKey(cs, TEST_KEY_1, VALUE_A, { readonly: false }, { save: false })
+
+      // Act
       const setRes = setConfigurationKeyValue(cs, TEST_KEY_1, VALUE_B)
-      expect(setRes?.value).toBe(VALUE_B)
       const delRes = deleteConfigurationKey(cs, TEST_KEY_1, { save: false })
+
+      // Assert
+      expect(setRes?.value).toBe(VALUE_B)
       expect(delRes).toHaveLength(1)
       expect(getConfigurationKey(cs, TEST_KEY_1)).toBeUndefined()
     })
index 8e9cd75f05b598f1d0e2b5b0fa0627f7b84c2b5d..115da9bd194d74f2b82e853d98a3790f079d5866 100644 (file)
@@ -4,7 +4,7 @@
  */
 
 import { expect } from '@std/expect'
-import { afterEach, describe, it } from 'node:test'
+import { afterEach, beforeEach, describe, it } from 'node:test'
 
 import {
   checkChargingStationState,
@@ -40,8 +40,13 @@ import {
 } from './ChargingStationTestUtils.js'
 
 await describe('Helpers', async () => {
-  const baseName = 'CS-TEST'
-  const chargingStationTemplate = createMockChargingStationTemplate(baseName)
+  let baseName: string
+  let chargingStationTemplate: ChargingStationTemplate
+
+  beforeEach(() => {
+    baseName = 'CS-TEST'
+    chargingStationTemplate = createMockChargingStationTemplate(baseName)
+  })
 
   afterEach(() => {
     standardCleanup()
@@ -57,55 +62,70 @@ await describe('Helpers', async () => {
     }) as Reservation
 
   await it('should return formatted charging station ID with index', () => {
+    // Arrange & Act & Assert
     expect(getChargingStationId(1, chargingStationTemplate)).toBe(`${baseName}-00001`)
   })
 
   await it('should return consistent hash ID for same template and index', () => {
+    // Arrange & Act & Assert
     expect(getHashId(1, chargingStationTemplate)).toBe(
       'b4b1e8ec4fca79091d99ea9a7ea5901548010e6c0e98be9296f604b9d68734444dfdae73d7d406b6124b42815214d088'
     )
   })
 
   await it('should throw when stationInfo is missing', () => {
+    // Arrange
     // For validation edge cases, we need to manually create invalid states
     // since the factory is designed to create valid configurations
     const { station: stationNoInfo } = createMockChargingStation({ baseName })
     stationNoInfo.stationInfo = undefined
+
+    // Act & Assert
     expect(() => {
       validateStationInfo(stationNoInfo)
     }).toThrow(new BaseError('Missing charging station information'))
   })
 
   await it('should throw when stationInfo is empty object', () => {
+    // Arrange
     // For validation edge cases, manually create empty stationInfo
     const { station: stationEmptyInfo } = createMockChargingStation({ baseName })
     stationEmptyInfo.stationInfo = {} as ChargingStationInfo
+
+    // Act & Assert
     expect(() => {
       validateStationInfo(stationEmptyInfo)
     }).toThrow(new BaseError('Missing charging station information'))
   })
 
   await it('should throw when chargingStationId is undefined', () => {
+    // Arrange
     const { station: stationMissingId } = createMockChargingStation({
       baseName,
       stationInfo: { baseName, chargingStationId: undefined },
     })
+
+    // Act & Assert
     expect(() => {
       validateStationInfo(stationMissingId)
     }).toThrow(new BaseError('Missing chargingStationId in stationInfo properties'))
   })
 
   await it('should throw when chargingStationId is empty string', () => {
+    // Arrange
     const { station: stationEmptyId } = createMockChargingStation({
       baseName,
       stationInfo: { baseName, chargingStationId: '' },
     })
+
+    // Act & Assert
     expect(() => {
       validateStationInfo(stationEmptyId)
     }).toThrow(new BaseError('Missing chargingStationId in stationInfo properties'))
   })
 
   await it('should throw when hashId is undefined', () => {
+    // Arrange
     const { station: stationMissingHash } = createMockChargingStation({
       baseName,
       stationInfo: {
@@ -114,12 +134,15 @@ await describe('Helpers', async () => {
         hashId: undefined,
       },
     })
+
+    // Act & Assert
     expect(() => {
       validateStationInfo(stationMissingHash)
     }).toThrow(new BaseError(`${baseName}-00001: Missing hashId in stationInfo properties`))
   })
 
   await it('should throw when hashId is empty string', () => {
+    // Arrange
     const { station: stationEmptyHash } = createMockChargingStation({
       baseName,
       stationInfo: {
@@ -128,12 +151,15 @@ await describe('Helpers', async () => {
         hashId: '',
       },
     })
+
+    // Act & Assert
     expect(() => {
       validateStationInfo(stationEmptyHash)
     }).toThrow(new BaseError(`${baseName}-00001: Missing hashId in stationInfo properties`))
   })
 
   await it('should throw when templateIndex is undefined', () => {
+    // Arrange
     const { station: stationMissingTemplate } = createMockChargingStation({
       baseName,
       stationInfo: {
@@ -143,12 +169,15 @@ await describe('Helpers', async () => {
         templateIndex: undefined,
       },
     })
+
+    // Act & Assert
     expect(() => {
       validateStationInfo(stationMissingTemplate)
     }).toThrow(new BaseError(`${baseName}-00001: Missing templateIndex in stationInfo properties`))
   })
 
   await it('should throw when templateIndex is zero', () => {
+    // Arrange
     const { station: stationInvalidTemplate } = createMockChargingStation({
       baseName,
       stationInfo: {
@@ -158,6 +187,8 @@ await describe('Helpers', async () => {
         templateIndex: 0,
       },
     })
+
+    // Act & Assert
     expect(() => {
       validateStationInfo(stationInvalidTemplate)
     }).toThrow(
@@ -166,6 +197,7 @@ await describe('Helpers', async () => {
   })
 
   await it('should throw when templateName is undefined', () => {
+    // Arrange
     const { station: stationMissingName } = createMockChargingStation({
       baseName,
       stationInfo: {
@@ -176,12 +208,15 @@ await describe('Helpers', async () => {
         templateName: undefined,
       },
     })
+
+    // Act & Assert
     expect(() => {
       validateStationInfo(stationMissingName)
     }).toThrow(new BaseError(`${baseName}-00001: Missing templateName in stationInfo properties`))
   })
 
   await it('should throw when templateName is empty string', () => {
+    // Arrange
     const { station: stationEmptyName } = createMockChargingStation({
       baseName,
       stationInfo: {
@@ -192,12 +227,15 @@ await describe('Helpers', async () => {
         templateName: '',
       },
     })
+
+    // Act & Assert
     expect(() => {
       validateStationInfo(stationEmptyName)
     }).toThrow(new BaseError(`${baseName}-00001: Missing templateName in stationInfo properties`))
   })
 
   await it('should throw when maximumPower is undefined', () => {
+    // Arrange
     const { station: stationMissingPower } = createMockChargingStation({
       baseName,
       stationInfo: {
@@ -209,12 +247,15 @@ await describe('Helpers', async () => {
         templateName: 'test-template.json',
       },
     })
+
+    // Act & Assert
     expect(() => {
       validateStationInfo(stationMissingPower)
     }).toThrow(new BaseError(`${baseName}-00001: Missing maximumPower in stationInfo properties`))
   })
 
   await it('should throw when maximumPower is zero', () => {
+    // Arrange
     const { station: stationInvalidPower } = createMockChargingStation({
       baseName,
       stationInfo: {
@@ -226,6 +267,8 @@ await describe('Helpers', async () => {
         templateName: 'test-template.json',
       },
     })
+
+    // Act & Assert
     expect(() => {
       validateStationInfo(stationInvalidPower)
     }).toThrow(
@@ -234,6 +277,7 @@ await describe('Helpers', async () => {
   })
 
   await it('should throw when maximumAmperage is undefined', () => {
+    // Arrange
     const { station: stationMissingAmperage } = createMockChargingStation({
       baseName,
       stationInfo: {
@@ -246,6 +290,8 @@ await describe('Helpers', async () => {
         templateName: 'test-template.json',
       },
     })
+
+    // Act & Assert
     expect(() => {
       validateStationInfo(stationMissingAmperage)
     }).toThrow(
@@ -254,6 +300,7 @@ await describe('Helpers', async () => {
   })
 
   await it('should throw when maximumAmperage is zero', () => {
+    // Arrange
     const { station: stationInvalidAmperage } = createMockChargingStation({
       baseName,
       stationInfo: {
@@ -266,6 +313,8 @@ await describe('Helpers', async () => {
         templateName: 'test-template.json',
       },
     })
+
+    // Act & Assert
     expect(() => {
       validateStationInfo(stationInvalidAmperage)
     }).toThrow(
@@ -274,6 +323,7 @@ await describe('Helpers', async () => {
   })
 
   await it('should pass validation with complete valid configuration', () => {
+    // Arrange
     const { station: validStation } = createMockChargingStation({
       baseName,
       stationInfo: {
@@ -286,12 +336,15 @@ await describe('Helpers', async () => {
         templateName: 'test-template.json',
       },
     })
+
+    // Act & Assert
     expect(() => {
       validateStationInfo(validStation)
     }).not.toThrow()
   })
 
   await it('should throw for OCPP 2.0 without EVSE configuration', () => {
+    // Arrange
     const { station: stationOcpp20 } = createMockChargingStation({
       baseName,
       connectorsCount: 0, // Ensure no EVSEs are created
@@ -307,6 +360,8 @@ await describe('Helpers', async () => {
         templateName: 'test-template.json',
       },
     })
+
+    // Act & Assert
     expect(() => {
       validateStationInfo(stationOcpp20)
     }).toThrow(
@@ -317,6 +372,7 @@ await describe('Helpers', async () => {
   })
 
   await it('should throw for OCPP 2.0.1 without EVSE configuration', () => {
+    // Arrange
     const { station: stationOcpp201 } = createMockChargingStation({
       baseName,
       connectorsCount: 0, // Ensure no EVSEs are created
@@ -332,6 +388,8 @@ await describe('Helpers', async () => {
         templateName: 'test-template.json',
       },
     })
+
+    // Act & Assert
     expect(() => {
       validateStationInfo(stationOcpp201)
     }).toThrow(
@@ -342,39 +400,58 @@ await describe('Helpers', async () => {
   })
 
   await it('should return false and warn when station is not started or starting', t => {
+    // Arrange
     const warnMock = t.mock.method(logger, 'warn')
     const { station: stationNotStarted } = createMockChargingStation({
       baseName,
       started: false,
       starting: false,
     })
-    expect(checkChargingStationState(stationNotStarted, 'log prefix |')).toBe(false)
+
+    // Act
+    const result = checkChargingStationState(stationNotStarted, 'log prefix |')
+
+    // Assert
+    expect(result).toBe(false)
     expect(warnMock.mock.calls.length).toBe(1)
   })
 
   await it('should return true when station is starting', t => {
+    // Arrange
     const warnMock = t.mock.method(logger, 'warn')
     const { station: stationStarting } = createMockChargingStation({
       baseName,
       started: false,
       starting: true,
     })
-    expect(checkChargingStationState(stationStarting, 'log prefix |')).toBe(true)
+
+    // Act
+    const result = checkChargingStationState(stationStarting, 'log prefix |')
+
+    // Assert
+    expect(result).toBe(true)
     expect(warnMock.mock.calls.length).toBe(0)
   })
 
   await it('should return true when station is started', t => {
+    // Arrange
     const warnMock = t.mock.method(logger, 'warn')
     const { station: stationStarted } = createMockChargingStation({
       baseName,
       started: true,
       starting: false,
     })
-    expect(checkChargingStationState(stationStarted, 'log prefix |')).toBe(true)
+
+    // Act
+    const result = checkChargingStationState(stationStarted, 'log prefix |')
+
+    // Assert
+    expect(result).toBe(true)
     expect(warnMock.mock.calls.length).toBe(0)
   })
 
   await it('should return correct phase rotation value for connector and phase count', () => {
+    // Arrange & Act & Assert
     expect(getPhaseRotationValue(0, 0)).toBe('0.RST')
     expect(getPhaseRotationValue(1, 0)).toBe('1.NotApplicable')
     expect(getPhaseRotationValue(2, 0)).toBe('2.NotApplicable')
@@ -390,13 +467,17 @@ await describe('Helpers', async () => {
   })
 
   await it('should return -1 for undefined EVSEs and 0 for empty object', () => {
+    // Arrange & Act & Assert
     expect(getMaxNumberOfEvses(undefined)).toBe(-1)
     expect(getMaxNumberOfEvses({})).toBe(0)
   })
 
   await it('should throw for undefined or empty template', t => {
+    // Arrange
     const warnMock = t.mock.method(logger, 'warn')
     const errorMock = t.mock.method(logger, 'error')
+
+    // Act & Assert
     expect(() => {
       checkTemplate(undefined, 'log prefix |', 'test-template.json')
     }).toThrow(new BaseError('Failed to read charging station template file test-template.json'))
@@ -412,7 +493,10 @@ await describe('Helpers', async () => {
   })
 
   await it('should throw for undefined or empty configuration', t => {
+    // Arrange
     const errorMock = t.mock.method(logger, 'error')
+
+    // Act & Assert
     expect(() => {
       checkConfiguration(undefined, 'log prefix |', 'configuration.json')
     }).toThrow(
@@ -426,8 +510,11 @@ await describe('Helpers', async () => {
   })
 
   await it('should warn and clear status when connector has predefined status', t => {
+    // Arrange
     const warnMock = t.mock.method(logger, 'warn')
     checkStationInfoConnectorStatus(1, {} as ConnectorStatus, 'log prefix |', 'test-template.json')
+
+    // Act & Assert
     expect(warnMock.mock.calls.length).toBe(0)
     const connectorStatus = {
       status: ConnectorStatusEnum.Available,
@@ -438,24 +525,31 @@ await describe('Helpers', async () => {
   })
 
   await it('should return Available when no bootStatus is defined', () => {
+    // Arrange
     const { station: chargingStation } = createMockChargingStation({ baseName, connectorsCount: 2 })
     const connectorStatus = {} as ConnectorStatus
+
+    // Act & Assert
     expect(getBootConnectorStatus(chargingStation, 1, connectorStatus)).toBe(
       ConnectorStatusEnum.Available
     )
   })
 
   await it('should return bootStatus from template when defined', () => {
+    // Arrange
     const { station: chargingStation } = createMockChargingStation({ baseName, connectorsCount: 2 })
     const connectorStatus = {
       bootStatus: ConnectorStatusEnum.Unavailable,
     } as ConnectorStatus
+
+    // Act & Assert
     expect(getBootConnectorStatus(chargingStation, 1, connectorStatus)).toBe(
       ConnectorStatusEnum.Unavailable
     )
   })
 
   await it('should return Unavailable when charging station is inoperative', () => {
+    // Arrange
     const { station: chargingStation } = createMockChargingStation({
       baseName,
       connectorDefaults: { availability: AvailabilityType.Inoperative },
@@ -464,12 +558,15 @@ await describe('Helpers', async () => {
     const connectorStatus = {
       bootStatus: ConnectorStatusEnum.Available,
     } as ConnectorStatus
+
+    // Act & Assert
     expect(getBootConnectorStatus(chargingStation, 1, connectorStatus)).toBe(
       ConnectorStatusEnum.Unavailable
     )
   })
 
   await it('should return Unavailable when connector is inoperative', () => {
+    // Arrange
     const { station: chargingStation } = createMockChargingStation({
       baseName,
       connectorDefaults: { availability: AvailabilityType.Inoperative },
@@ -479,30 +576,38 @@ await describe('Helpers', async () => {
       availability: AvailabilityType.Inoperative,
       bootStatus: ConnectorStatusEnum.Available,
     } as ConnectorStatus
+
+    // Act & Assert
     expect(getBootConnectorStatus(chargingStation, 1, connectorStatus)).toBe(
       ConnectorStatusEnum.Unavailable
     )
   })
 
   await it('should restore previous status when transaction is in progress', () => {
+    // Arrange
     const { station: chargingStation } = createMockChargingStation({ baseName, connectorsCount: 2 })
     const connectorStatus = {
       bootStatus: ConnectorStatusEnum.Available,
       status: ConnectorStatusEnum.Charging,
       transactionStarted: true,
     } as ConnectorStatus
+
+    // Act & Assert
     expect(getBootConnectorStatus(chargingStation, 1, connectorStatus)).toBe(
       ConnectorStatusEnum.Charging
     )
   })
 
   await it('should use bootStatus over previous status when no transaction', () => {
+    // Arrange
     const { station: chargingStation } = createMockChargingStation({ baseName, connectorsCount: 2 })
     const connectorStatus = {
       bootStatus: ConnectorStatusEnum.Available,
       status: ConnectorStatusEnum.Charging,
       transactionStarted: false,
     } as ConnectorStatus
+
+    // Act & Assert
     expect(getBootConnectorStatus(chargingStation, 1, connectorStatus)).toBe(
       ConnectorStatusEnum.Available
     )
@@ -510,52 +615,65 @@ await describe('Helpers', async () => {
 
   // Tests for reservation helper functions
   await it('should return true when reservation has expired', () => {
+    // Arrange & Act & Assert
     expect(hasReservationExpired(createTestReservation(true))).toBe(true)
   })
 
   await it('should return false when reservation is still valid', () => {
+    // Arrange & Act & Assert
     expect(hasReservationExpired(createTestReservation(false))).toBe(false)
   })
 
   await it('should return false when connector has no reservation', () => {
+    // Arrange & Act & Assert
     const connectorStatus = {} as ConnectorStatus
     expect(hasPendingReservation(connectorStatus)).toBe(false)
   })
 
   await it('should return true when connector has valid pending reservation', () => {
+    // Arrange & Act & Assert
     const connectorStatus = { reservation: createTestReservation(false) } as ConnectorStatus
     expect(hasPendingReservation(connectorStatus)).toBe(true)
   })
 
   await it('should return false when connector reservation has expired', () => {
+    // Arrange & Act & Assert
     const connectorStatus = { reservation: createTestReservation(true) } as ConnectorStatus
     expect(hasPendingReservation(connectorStatus)).toBe(false)
   })
 
   await it('should return false when no reservations exist (connector mode)', () => {
+    // Arrange & Act & Assert
     const { station: chargingStation } = createMockChargingStation({ baseName, connectorsCount: 2 })
     expect(hasPendingReservations(chargingStation)).toBe(false)
   })
 
   await it('should return true when pending reservation exists (connector mode)', () => {
+    // Arrange
     const { station: chargingStation } = createMockChargingStation({ baseName, connectorsCount: 2 })
     const connectorStatus = chargingStation.connectors.get(1)
     if (connectorStatus != null) {
       connectorStatus.reservation = createTestReservation(false)
     }
+
+    // Act & Assert
     expect(hasPendingReservations(chargingStation)).toBe(true)
   })
 
   await it('should return false when no reservations exist (EVSE mode)', () => {
+    // Arrange
     const { station: chargingStation } = createMockChargingStation({
       baseName,
       connectorsCount: 2,
       stationInfo: { ocppVersion: OCPPVersion.VERSION_201 },
     })
+
+    // Act & Assert
     expect(hasPendingReservations(chargingStation)).toBe(false)
   })
 
   await it('should return true when pending reservation exists (EVSE mode)', () => {
+    // Arrange
     const { station: chargingStation } = createMockChargingStation({
       baseName,
       connectorsCount: 2,
@@ -566,10 +684,13 @@ await describe('Helpers', async () => {
     if (firstConnector != null) {
       firstConnector.reservation = createTestReservation(false)
     }
+
+    // Act & Assert
     expect(hasPendingReservations(chargingStation)).toBe(true)
   })
 
   await it('should return false when only expired reservations exist (EVSE mode)', () => {
+    // Arrange
     const { station: chargingStation } = createMockChargingStation({
       baseName,
       connectorsCount: 2,
@@ -580,6 +701,8 @@ await describe('Helpers', async () => {
     if (firstConnector != null) {
       firstConnector.reservation = createTestReservation(true)
     }
+
+    // Act & Assert
     expect(hasPendingReservations(chargingStation)).toBe(false)
   })
 })
index 9e01913c32d3a3c456df5528dbb682a15e361604..483e16567958b78c850baff7e4d801ac1d6083ef 100644 (file)
@@ -5,7 +5,7 @@
 
 import { expect } from '@std/expect'
 import { rm } from 'node:fs/promises'
-import { afterEach, describe, it } from 'node:test'
+import { afterEach, beforeEach, describe, it } from 'node:test'
 
 import { OCPP20CertificateManager } from '../../../../src/charging-station/ocpp/2.0/OCPP20CertificateManager.js'
 import {
@@ -66,9 +66,13 @@ await describe('I02-I04 - ISO15118 Certificate Management', async () => {
   })
 
   await describe('storeCertificate', async () => {
-    await it('should store a valid PEM certificate to the correct path', async () => {
-      const manager = new OCPP20CertificateManager()
+    let manager: OCPP20CertificateManager
+
+    beforeEach(() => {
+      manager = new OCPP20CertificateManager()
+    })
 
+    await it('should store a valid PEM certificate to the correct path', async () => {
       const result = await manager.storeCertificate(
         TEST_STATION_HASH_ID,
         TEST_CERT_TYPE,
@@ -83,8 +87,6 @@ await describe('I02-I04 - ISO15118 Certificate Management', async () => {
     })
 
     await it('should reject invalid PEM certificate without BEGIN/END markers', async () => {
-      const manager = new OCPP20CertificateManager()
-
       const result = await manager.storeCertificate(
         TEST_STATION_HASH_ID,
         TEST_CERT_TYPE,
@@ -97,8 +99,6 @@ await describe('I02-I04 - ISO15118 Certificate Management', async () => {
     })
 
     await it('should reject empty certificate data', async () => {
-      const manager = new OCPP20CertificateManager()
-
       const result = await manager.storeCertificate(
         TEST_STATION_HASH_ID,
         TEST_CERT_TYPE,
@@ -111,8 +111,6 @@ await describe('I02-I04 - ISO15118 Certificate Management', async () => {
     })
 
     await it('should create certificate directory structure if not exists', async () => {
-      const manager = new OCPP20CertificateManager()
-
       const result = await manager.storeCertificate(
         TEST_STATION_HASH_ID,
         InstallCertificateUseEnumType.V2GRootCertificate,
@@ -126,9 +124,12 @@ await describe('I02-I04 - ISO15118 Certificate Management', async () => {
   })
 
   await describe('deleteCertificate', async () => {
-    await it('should delete certificate by hash data', async () => {
-      const manager = new OCPP20CertificateManager()
+    let manager: OCPP20CertificateManager
 
+    beforeEach(() => {
+      manager = new OCPP20CertificateManager()
+    })
+    await it('should delete certificate by hash data', async () => {
       const hashData: CertificateHashDataType = {
         hashAlgorithm: HashAlgorithmEnumType.SHA256,
         issuerKeyHash: 'abc123def456',
@@ -144,8 +145,6 @@ await describe('I02-I04 - ISO15118 Certificate Management', async () => {
     })
 
     await it('should return NotFound for non-existent certificate', async () => {
-      const manager = new OCPP20CertificateManager()
-
       const hashData: CertificateHashDataType = {
         hashAlgorithm: HashAlgorithmEnumType.SHA256,
         issuerKeyHash: 'non-existent-hash',
@@ -160,8 +159,6 @@ await describe('I02-I04 - ISO15118 Certificate Management', async () => {
     })
 
     await it('should handle filesystem errors gracefully', async () => {
-      const manager = new OCPP20CertificateManager()
-
       const hashData: CertificateHashDataType = {
         hashAlgorithm: HashAlgorithmEnumType.SHA256,
         issuerKeyHash: 'valid-hash',
@@ -177,9 +174,12 @@ await describe('I02-I04 - ISO15118 Certificate Management', async () => {
   })
 
   await describe('getInstalledCertificates', async () => {
-    await it('should return list of installed certificates for station', async () => {
-      const manager = new OCPP20CertificateManager()
+    let manager: OCPP20CertificateManager
 
+    beforeEach(() => {
+      manager = new OCPP20CertificateManager()
+    })
+    await it('should return list of installed certificates for station', async () => {
       const result = await manager.getInstalledCertificates(TEST_STATION_HASH_ID)
 
       expect(result).toBeDefined()
@@ -187,8 +187,6 @@ await describe('I02-I04 - ISO15118 Certificate Management', async () => {
     })
 
     await it('should filter certificates by type when filter provided', async () => {
-      const manager = new OCPP20CertificateManager()
-
       const filterTypes = [InstallCertificateUseEnumType.CSMSRootCertificate]
       const result = await manager.getInstalledCertificates(TEST_STATION_HASH_ID, filterTypes)
 
@@ -197,8 +195,6 @@ await describe('I02-I04 - ISO15118 Certificate Management', async () => {
     })
 
     await it('should return empty list when no certificates installed', async () => {
-      const manager = new OCPP20CertificateManager()
-
       const result = await manager.getInstalledCertificates('empty-station-hash-id')
 
       expect(result).toBeDefined()
@@ -206,8 +202,6 @@ await describe('I02-I04 - ISO15118 Certificate Management', async () => {
     })
 
     await it('should support multiple certificate type filters', async () => {
-      const manager = new OCPP20CertificateManager()
-
       const filterTypes = [
         InstallCertificateUseEnumType.CSMSRootCertificate,
         InstallCertificateUseEnumType.V2GRootCertificate,
@@ -221,9 +215,12 @@ await describe('I02-I04 - ISO15118 Certificate Management', async () => {
   })
 
   await describe('computeCertificateHash', async () => {
-    await it('should compute hash data for valid PEM certificate', () => {
-      const manager = new OCPP20CertificateManager()
+    let manager: OCPP20CertificateManager
 
+    beforeEach(() => {
+      manager = new OCPP20CertificateManager()
+    })
+    await it('should compute hash data for valid PEM certificate', () => {
       const hashData = manager.computeCertificateHash(VALID_PEM_CERTIFICATE)
 
       expect(hashData).toBeDefined()
@@ -237,8 +234,6 @@ await describe('I02-I04 - ISO15118 Certificate Management', async () => {
     })
 
     await it('should return hex-encoded hash values', () => {
-      const manager = new OCPP20CertificateManager()
-
       const hashData = manager.computeCertificateHash(VALID_PEM_CERTIFICATE)
 
       const hexPattern = /^[a-fA-F0-9]+$/
@@ -247,24 +242,18 @@ await describe('I02-I04 - ISO15118 Certificate Management', async () => {
     })
 
     await it('should throw error for invalid PEM certificate', () => {
-      const manager = new OCPP20CertificateManager()
-
       expect(() => {
         manager.computeCertificateHash(INVALID_PEM_NO_MARKERS)
       }).toThrow()
     })
 
     await it('should throw error for empty certificate', () => {
-      const manager = new OCPP20CertificateManager()
-
       expect(() => {
         manager.computeCertificateHash(EMPTY_PEM_CERTIFICATE)
       }).toThrow()
     })
 
     await it('should support SHA384 hash algorithm', () => {
-      const manager = new OCPP20CertificateManager()
-
       const hashData = manager.computeCertificateHash(
         VALID_PEM_CERTIFICATE,
         HashAlgorithmEnumType.SHA384
@@ -275,8 +264,6 @@ await describe('I02-I04 - ISO15118 Certificate Management', async () => {
     })
 
     await it('should support SHA512 hash algorithm', () => {
-      const manager = new OCPP20CertificateManager()
-
       const hashData = manager.computeCertificateHash(
         VALID_PEM_CERTIFICATE,
         HashAlgorithmEnumType.SHA512
@@ -288,41 +275,36 @@ await describe('I02-I04 - ISO15118 Certificate Management', async () => {
   })
 
   await describe('validateCertificateFormat', async () => {
-    await it('should return true for valid PEM certificate', () => {
-      const manager = new OCPP20CertificateManager()
+    let manager: OCPP20CertificateManager
 
+    beforeEach(() => {
+      manager = new OCPP20CertificateManager()
+    })
+    await it('should return true for valid PEM certificate', () => {
       const isValid = manager.validateCertificateFormat(VALID_PEM_CERTIFICATE)
 
       expect(isValid).toBe(true)
     })
 
     await it('should return false for certificate without BEGIN marker', () => {
-      const manager = new OCPP20CertificateManager()
-
       const isValid = manager.validateCertificateFormat(INVALID_PEM_NO_MARKERS)
 
       expect(isValid).toBe(false)
     })
 
     await it('should return false for certificate with wrong markers', () => {
-      const manager = new OCPP20CertificateManager()
-
       const isValid = manager.validateCertificateFormat(INVALID_PEM_WRONG_MARKERS)
 
       expect(isValid).toBe(false)
     })
 
     await it('should return false for empty string', () => {
-      const manager = new OCPP20CertificateManager()
-
       const isValid = manager.validateCertificateFormat(EMPTY_PEM_CERTIFICATE)
 
       expect(isValid).toBe(false)
     })
 
     await it('should return false for null/undefined input', () => {
-      const manager = new OCPP20CertificateManager()
-
       // eslint-disable-next-line @typescript-eslint/no-explicit-any -- testing invalid null input
       expect(manager.validateCertificateFormat(null as any)).toBe(false)
       // eslint-disable-next-line @typescript-eslint/no-explicit-any -- testing invalid undefined input
@@ -330,8 +312,6 @@ await describe('I02-I04 - ISO15118 Certificate Management', async () => {
     })
 
     await it('should return true for certificate with extra whitespace', () => {
-      const manager = new OCPP20CertificateManager()
-
       const pemWithWhitespace = `
         
         -----BEGIN CERTIFICATE-----
@@ -347,9 +327,12 @@ await describe('I02-I04 - ISO15118 Certificate Management', async () => {
   })
 
   await describe('getCertificatePath', async () => {
-    await it('should return correct file path for certificate', () => {
-      const manager = new OCPP20CertificateManager()
+    let manager: OCPP20CertificateManager
 
+    beforeEach(() => {
+      manager = new OCPP20CertificateManager()
+    })
+    await it('should return correct file path for certificate', () => {
       const path = manager.getCertificatePath(TEST_STATION_HASH_ID, TEST_CERT_TYPE, 'SERIAL-12345')
 
       expect(path).toBeDefined()
@@ -361,8 +344,6 @@ await describe('I02-I04 - ISO15118 Certificate Management', async () => {
     })
 
     await it('should handle special characters in serial number', () => {
-      const manager = new OCPP20CertificateManager()
-
       const path = manager.getCertificatePath(
         TEST_STATION_HASH_ID,
         TEST_CERT_TYPE,
@@ -376,8 +357,6 @@ await describe('I02-I04 - ISO15118 Certificate Management', async () => {
     })
 
     await it('should return different paths for different certificate types', () => {
-      const manager = new OCPP20CertificateManager()
-
       const csmsPath = manager.getCertificatePath(
         TEST_STATION_HASH_ID,
         InstallCertificateUseEnumType.CSMSRootCertificate,
@@ -396,8 +375,6 @@ await describe('I02-I04 - ISO15118 Certificate Management', async () => {
     })
 
     await it('should return path following project convention', () => {
-      const manager = new OCPP20CertificateManager()
-
       const path = manager.getCertificatePath(TEST_STATION_HASH_ID, TEST_CERT_TYPE, 'SERIAL-12345')
 
       expect(path).toMatch(/configurations/)
@@ -407,9 +384,12 @@ await describe('I02-I04 - ISO15118 Certificate Management', async () => {
   })
 
   await describe('Edge cases and error handling', async () => {
-    await it('should handle concurrent certificate operations', async () => {
-      const manager = new OCPP20CertificateManager()
+    let manager: OCPP20CertificateManager
 
+    beforeEach(() => {
+      manager = new OCPP20CertificateManager()
+    })
+    await it('should handle concurrent certificate operations', async () => {
       const results = await Promise.all([
         manager.storeCertificate(
           TEST_STATION_HASH_ID,
@@ -431,8 +411,6 @@ await describe('I02-I04 - ISO15118 Certificate Management', async () => {
     })
 
     await it('should handle very long certificate chains', async () => {
-      const manager = new OCPP20CertificateManager()
-
       const longChain = Array(5).fill(VALID_PEM_CERTIFICATE).join('\n')
 
       const result = await manager.storeCertificate(TEST_STATION_HASH_ID, TEST_CERT_TYPE, longChain)
@@ -441,8 +419,6 @@ await describe('I02-I04 - ISO15118 Certificate Management', async () => {
     })
 
     await it('should sanitize station hash ID for filesystem safety', () => {
-      const manager = new OCPP20CertificateManager()
-
       const maliciousHashId = '../../../etc/passwd'
 
       const path = manager.getCertificatePath(maliciousHashId, TEST_CERT_TYPE, 'SERIAL-001')
index df3c9a011ba5c657b0f2c9dc085d97f22e80f9c0..d4ad89d77fd11029c0d02cffafa6db7468e1787c 100644 (file)
@@ -97,7 +97,7 @@ await describe('C11 - Clear Authorization Data in Authorization Cache', async ()
       }
     })
 
-    await it('should NOT call idTagsCache.deleteIdTags() on ClearCache request', async () => {
+    await it('should not call idTagsCache.deleteIdTags() on ClearCache request', async () => {
       // Verify that IdTagsCache is not touched
       let deleteIdTagsCalled = false
       const originalDeleteIdTags = station.idTagsCache.deleteIdTags.bind(station.idTagsCache)
index d16fc9423c92bf6e1d3160100212fe3d0181957b..8596daf5a9de32c75af1e36910111fb35aa5b550 100644 (file)
@@ -25,7 +25,10 @@ import {
 } from '../../../../src/types/index.js'
 import { Constants } from '../../../../src/utils/index.js'
 import { standardCleanup } from '../../../helpers/TestLifecycleHelpers.js'
-import { TEST_CHARGING_STATION_BASE_NAME } from '../../ChargingStationTestConstants.js'
+import {
+  TEST_CHARGING_STATION_BASE_NAME,
+  TEST_ONE_HOUR_MS,
+} from '../../ChargingStationTestConstants.js'
 import { createMockChargingStation } from '../../ChargingStationTestUtils.js'
 
 await describe('B11 & B12 - Reset', async () => {
@@ -72,6 +75,40 @@ await describe('B11 & B12 - Reset', async () => {
   })
 
   await describe('B11 - Reset - Without Ongoing Transaction', async () => {
+    let b11MockChargingStation: ChargingStation
+    let b11MockStation: ChargingStation & {
+      getNumberOfRunningTransactions: () => number
+      reset: () => Promise<void>
+    }
+
+    beforeEach(() => {
+      const { station } = createMockChargingStation({
+        baseName: TEST_CHARGING_STATION_BASE_NAME,
+        connectorsCount: 3,
+        evseConfiguration: { evsesCount: 3 },
+        heartbeatInterval: Constants.DEFAULT_HEARTBEAT_INTERVAL,
+        stationInfo: {
+          ocppStrictCompliance: false,
+          ocppVersion: OCPPVersion.VERSION_201,
+          resetTime: 5000,
+        },
+        websocketPingInterval: Constants.DEFAULT_WEBSOCKET_PING_INTERVAL,
+      })
+      b11MockChargingStation = station
+
+      interface MockChargingStation extends ChargingStation {
+        getNumberOfRunningTransactions: () => number
+        reset: () => Promise<void>
+      }
+      b11MockStation = b11MockChargingStation as MockChargingStation
+      b11MockStation.getNumberOfRunningTransactions = () => 0
+      b11MockStation.reset = () => Promise.resolve()
+    })
+
+    afterEach(() => {
+      standardCleanup()
+    })
+
     // FR: B11.FR.01
     await it('should handle Reset request with Immediate type when no transactions', async () => {
       const resetRequest: OCPP20ResetRequest = {
@@ -79,7 +116,7 @@ await describe('B11 & B12 - Reset', async () => {
       }
 
       const response: OCPP20ResetResponse = await testableService.handleRequestReset(
-        mockChargingStation,
+        b11MockChargingStation,
         resetRequest
       )
 
@@ -100,7 +137,7 @@ await describe('B11 & B12 - Reset', async () => {
       }
 
       const response: OCPP20ResetResponse = await testableService.handleRequestReset(
-        mockChargingStation,
+        b11MockChargingStation,
         resetRequest
       )
 
@@ -121,7 +158,7 @@ await describe('B11 & B12 - Reset', async () => {
       }
 
       const response: OCPP20ResetResponse = await testableService.handleRequestReset(
-        mockChargingStation,
+        b11MockChargingStation,
         resetRequest
       )
 
@@ -141,7 +178,7 @@ await describe('B11 & B12 - Reset', async () => {
       }
 
       const response: OCPP20ResetResponse = await testableService.handleRequestReset(
-        mockChargingStation,
+        b11MockChargingStation,
         resetRequest
       )
 
@@ -158,7 +195,7 @@ await describe('B11 & B12 - Reset', async () => {
       }
 
       const response: OCPP20ResetResponse = await testableService.handleRequestReset(
-        mockChargingStation,
+        b11MockChargingStation,
         resetRequest
       )
 
@@ -167,7 +204,7 @@ await describe('B11 & B12 - Reset', async () => {
       expect(typeof response.status).toBe('string')
 
       // For immediate reset without active transactions, should be accepted
-      if (mockStation.getNumberOfRunningTransactions() === 0) {
+      if (b11MockStation.getNumberOfRunningTransactions() === 0) {
         expect(response.status).toBe(ResetStatusEnumType.Accepted)
       }
     })
@@ -178,7 +215,7 @@ await describe('B11 & B12 - Reset', async () => {
       }
 
       const response: OCPP20ResetResponse = await testableService.handleRequestReset(
-        mockChargingStation,
+        b11MockChargingStation,
         resetRequest
       )
 
@@ -186,31 +223,36 @@ await describe('B11 & B12 - Reset', async () => {
       expect(response.status).toBe(ResetStatusEnumType.Accepted)
     })
 
-    await it('should reject EVSE reset when not supported and no transactions', async () => {
-      // Mock charging station without EVSE support
-      const originalHasEvses = mockChargingStation.hasEvses
-      ;(mockChargingStation as { hasEvses: boolean }).hasEvses = false
-
-      const resetRequest: OCPP20ResetRequest = {
-        evseId: 1,
-        type: ResetEnumType.Immediate,
-      }
-
-      const response: OCPP20ResetResponse = await testableService.handleRequestReset(
-        mockChargingStation,
-        resetRequest
-      )
+    // Mock charging station without EVSE support
+    Object.defineProperty(b11MockChargingStation, 'hasEvses', {
+      configurable: true,
+      value: false,
+      writable: true,
+    })
 
-      expect(response).toBeDefined()
-      expect(response.status).toBe(ResetStatusEnumType.Rejected)
-      expect(response.statusInfo).toBeDefined()
-      expect(response.statusInfo?.reasonCode).toBe(ReasonCodeEnumType.UnsupportedRequest)
-      expect(response.statusInfo?.additionalInfo).toContain(
-        'does not support resetting individual EVSE'
-      )
+    const resetRequest: OCPP20ResetRequest = {
+      evseId: 1,
+      type: ResetEnumType.Immediate,
+    }
 
-      // Restore original state
-      ;(mockChargingStation as { hasEvses: boolean }).hasEvses = originalHasEvses
+    const response: OCPP20ResetResponse = await testableService.handleRequestReset(
+      b11MockChargingStation,
+      resetRequest
+    )
+
+    expect(response).toBeDefined()
+    expect(response.status).toBe(ResetStatusEnumType.Rejected)
+    expect(response.statusInfo).toBeDefined()
+    expect(response.statusInfo?.reasonCode).toBe(ReasonCodeEnumType.UnsupportedRequest)
+    expect(response.statusInfo?.additionalInfo).toContain(
+      'does not support resetting individual EVSE'
+    )
+
+    // Restore original state
+    Object.defineProperty(b11MockChargingStation, 'hasEvses', {
+      configurable: false,
+      value: true,
+      writable: false,
     })
 
     await it('should handle EVSE-specific reset without transactions', async () => {
@@ -220,7 +262,7 @@ await describe('B11 & B12 - Reset', async () => {
       }
 
       const response: OCPP20ResetResponse = await testableService.handleRequestReset(
-        mockChargingStation,
+        b11MockChargingStation,
         resetRequest
       )
 
@@ -231,54 +273,81 @@ await describe('B11 & B12 - Reset', async () => {
   })
 
   await describe('B12 - Reset - With Ongoing Transaction', async () => {
+    let b12MockChargingStation: ChargingStation
+    let b12MockStation: ChargingStation & {
+      getNumberOfRunningTransactions: () => number
+      reset: () => Promise<void>
+    }
+
+    beforeEach(() => {
+      const { station } = createMockChargingStation({
+        baseName: TEST_CHARGING_STATION_BASE_NAME,
+        connectorsCount: 3,
+        evseConfiguration: { evsesCount: 3 },
+        heartbeatInterval: Constants.DEFAULT_HEARTBEAT_INTERVAL,
+        stationInfo: {
+          ocppStrictCompliance: false,
+          ocppVersion: OCPPVersion.VERSION_201,
+          resetTime: 5000,
+        },
+        websocketPingInterval: Constants.DEFAULT_WEBSOCKET_PING_INTERVAL,
+      })
+      b12MockChargingStation = station
+
+      interface MockChargingStation extends ChargingStation {
+        getNumberOfRunningTransactions: () => number
+        reset: () => Promise<void>
+      }
+      b12MockStation = b12MockChargingStation as MockChargingStation
+      b12MockStation.getNumberOfRunningTransactions = () => 0
+      b12MockStation.reset = () => Promise.resolve()
+    })
+
+    afterEach(() => {
+      standardCleanup()
+    })
+
     // FR: B12.FR.02
     await it('should handle immediate reset with active transactions', async () => {
       // Mock active transactions
-      mockStation.getNumberOfRunningTransactions = () => 1
+      b12MockStation.getNumberOfRunningTransactions = () => 1
 
       const resetRequest: OCPP20ResetRequest = {
         type: ResetEnumType.Immediate,
       }
 
       const response: OCPP20ResetResponse = await testableService.handleRequestReset(
-        mockChargingStation,
+        b12MockChargingStation,
         resetRequest
       )
 
       expect(response).toBeDefined()
       expect(response.status).toBe(ResetStatusEnumType.Accepted) // Should accept immediate reset
       expect(response.statusInfo).toBeUndefined()
-
-      // Reset mock
-      mockStation.getNumberOfRunningTransactions = () => 0
     })
-
     // FR: B12.FR.01
     await it('should handle OnIdle reset with active transactions', async () => {
       // Mock active transactions
-      mockStation.getNumberOfRunningTransactions = () => 1
+      b12MockStation.getNumberOfRunningTransactions = () => 1
 
       const resetRequest: OCPP20ResetRequest = {
         type: ResetEnumType.OnIdle,
       }
 
       const response: OCPP20ResetResponse = await testableService.handleRequestReset(
-        mockChargingStation,
+        b12MockChargingStation,
         resetRequest
       )
 
       expect(response).toBeDefined()
       expect(response.status).toBe(ResetStatusEnumType.Scheduled) // Should schedule OnIdle reset
       expect(response.statusInfo).toBeUndefined()
-
-      // Reset mock
-      mockStation.getNumberOfRunningTransactions = () => 0
     })
 
     // FR: B12.FR.03
     await it('should handle EVSE-specific reset with active transactions', async () => {
       // Mock active transactions
-      mockStation.getNumberOfRunningTransactions = () => 1
+      b12MockStation.getNumberOfRunningTransactions = () => 1
 
       const resetRequest: OCPP20ResetRequest = {
         evseId: 1,
@@ -286,7 +355,7 @@ await describe('B11 & B12 - Reset', async () => {
       }
 
       const response: OCPP20ResetResponse = await testableService.handleRequestReset(
-        mockChargingStation,
+        b12MockChargingStation,
         resetRequest
       )
 
@@ -295,16 +364,16 @@ await describe('B11 & B12 - Reset', async () => {
       expect([ResetStatusEnumType.Accepted, ResetStatusEnumType.Scheduled]).toContain(
         response.status
       )
-
-      // Reset mock
-      mockStation.getNumberOfRunningTransactions = () => 0
     })
 
     await it('should reject EVSE reset when not supported with active transactions', async () => {
       // Mock charging station without EVSE support and active transactions
-      const originalHasEvses = mockChargingStation.hasEvses
-      ;(mockChargingStation as { hasEvses: boolean }).hasEvses = false
-      mockStation.getNumberOfRunningTransactions = () => 1
+      Object.defineProperty(b12MockChargingStation, 'hasEvses', {
+        configurable: true,
+        value: false,
+        writable: true,
+      })
+      b12MockStation.getNumberOfRunningTransactions = () => 1
 
       const resetRequest: OCPP20ResetRequest = {
         evseId: 1,
@@ -312,7 +381,7 @@ await describe('B11 & B12 - Reset', async () => {
       }
 
       const response: OCPP20ResetResponse = await testableService.handleRequestReset(
-        mockChargingStation,
+        b12MockChargingStation,
         resetRequest
       )
 
@@ -325,8 +394,11 @@ await describe('B11 & B12 - Reset', async () => {
       )
 
       // Restore original state
-      ;(mockChargingStation as { hasEvses: boolean }).hasEvses = originalHasEvses
-      mockStation.getNumberOfRunningTransactions = () => 0
+      Object.defineProperty(b12MockChargingStation, 'hasEvses', {
+        configurable: false,
+        value: true,
+        writable: false,
+      })
     })
 
     // RST-001: Reset OnIdle Errata 2.14 Compliance Tests
@@ -471,7 +543,7 @@ await describe('B11 & B12 - Reset', async () => {
         await it('should return Scheduled when connector has non-expired reservation', async () => {
           const station = createTestStation()
           // Create a reservation that expires in 1 hour (future)
-          const futureExpiryDate = new Date(Date.now() + 3600000)
+          const futureExpiryDate = new Date(Date.now() + TEST_ONE_HOUR_MS)
           const mockReservation: Partial<Reservation> = {
             expiryDate: futureExpiryDate,
             id: 1,
@@ -504,7 +576,7 @@ await describe('B11 & B12 - Reset', async () => {
         await it('should return Accepted when reservation is expired', async () => {
           const station = createTestStation()
           // Create a reservation that expired 1 hour ago (past)
-          const pastExpiryDate = new Date(Date.now() - 3600000)
+          const pastExpiryDate = new Date(Date.now() - TEST_ONE_HOUR_MS)
           const mockReservation: Partial<Reservation> = {
             expiryDate: pastExpiryDate,
             id: 1,
@@ -588,7 +660,7 @@ await describe('B11 & B12 - Reset', async () => {
             firmwareStatus: FirmwareStatus.Downloading,
           })
           // Active reservation
-          const futureExpiryDate = new Date(Date.now() + 3600000)
+          const futureExpiryDate = new Date(Date.now() + TEST_ONE_HOUR_MS)
           const mockReservation: Partial<Reservation> = {
             expiryDate: futureExpiryDate,
             id: 1,
index 00c3902654247a10c2625c921c01f906eb25ebbd..dae44e14f108a8dc516102f72334407954e911e5 100644 (file)
@@ -230,6 +230,7 @@ await describe('M03 - GetCertificateStatus Request', async () => {
   })
 
   afterEach(() => {
+    standardCleanup()
     mock.restoreAll()
   })
 
@@ -348,6 +349,7 @@ await describe('Request Command Names', async () => {
   })
 
   afterEach(() => {
+    standardCleanup()
     mock.restoreAll()
   })
 
index 58c7acda09102509baf04c1b8815f34d6fa31716..6bb7d539b0e08735dc37ed076cc114484e5a641e 100644 (file)
@@ -1118,7 +1118,7 @@ await describe('E01-E04 - OCPP 2.0.1 TransactionEvent Implementation', async ()
   // ==========================================================================
   // E02 Cable-First Specific Tests
   // ==========================================================================
-  await describe('E02 - Cable-First Transaction Flow', async () => {
+  await describe('E02 - Cable-First Transaction', async () => {
     beforeEach(() => {
       resetConnectorTransactionState(mockChargingStation)
     })
@@ -1207,7 +1207,7 @@ await describe('E01-E04 - OCPP 2.0.1 TransactionEvent Implementation', async ()
       })
     })
 
-    await describe('EV Detection Flow', async () => {
+    await describe('EV Detection', async () => {
       await it('should include EVDetected between cable plug and charging start', () => {
         const connectorId = 1
         const transactionId = generateUUID()
@@ -1421,7 +1421,7 @@ await describe('E01-E04 - OCPP 2.0.1 TransactionEvent Implementation', async ()
   // ==========================================================================
   // E03 IdToken-First Specific Tests
   // ==========================================================================
-  await describe('E03 - IdToken-First Pre-Authorization Flow', async () => {
+  await describe('E03 - IdToken-First Pre-Authorization', async () => {
     beforeEach(() => {
       resetConnectorTransactionState(mockChargingStation)
     })
@@ -1748,7 +1748,7 @@ await describe('E01-E04 - OCPP 2.0.1 TransactionEvent Implementation', async ()
       })
     })
 
-    await describe('Authorization Status in E03 Flow', async () => {
+    await describe('Authorization Status in E03', async () => {
       await it('should support Deauthorized trigger for rejected authorization', () => {
         const context: OCPP20TransactionContext = {
           authorizationMethod: 'idToken',
index 94001aa9f445a782b26b8e728179fec1b69d763f..3c2a0ef306d27ab55a450daea4bc50807f708005 100644 (file)
@@ -98,8 +98,11 @@ await describe('B05 - OCPP20VariableManager', async () => {
   })
 
   await describe('getVariables method tests', async () => {
-    const manager = OCPP20VariableManager.getInstance()
+    let manager: OCPP20VariableManager
 
+    beforeEach(() => {
+      manager = OCPP20VariableManager.getInstance()
+    })
     await it('should handle valid OCPPCommCtrlr and TxCtrlr component requests', () => {
       const request: OCPP20GetVariableDataType[] = [
         {
@@ -349,9 +352,13 @@ await describe('B05 - OCPP20VariableManager', async () => {
   })
 
   await describe('Component validation tests', async () => {
-    const manager = OCPP20VariableManager.getInstance()
-    const testable = createTestableVariableManager(manager)
+    let manager: OCPP20VariableManager
+    let testable: ReturnType<typeof createTestableVariableManager>
 
+    beforeEach(() => {
+      manager = OCPP20VariableManager.getInstance()
+      testable = createTestableVariableManager(manager)
+    })
     await it('should validate OCPPCommCtrlr component as always valid', () => {
       // Behavior: Connector components are unsupported and isComponentValid returns false.
       // Scope: Per-connector variable validation not implemented; tests assert current behavior.
@@ -380,9 +387,13 @@ await describe('B05 - OCPP20VariableManager', async () => {
   })
 
   await describe('Variable support validation tests', async () => {
-    const manager = OCPP20VariableManager.getInstance()
-    const testable = createTestableVariableManager(manager)
+    let manager: OCPP20VariableManager
+    let testable: ReturnType<typeof createTestableVariableManager>
 
+    beforeEach(() => {
+      manager = OCPP20VariableManager.getInstance()
+      testable = createTestableVariableManager(manager)
+    })
     await it('should support standard HeartbeatInterval variable', () => {
       const component: ComponentType = { name: OCPP20ComponentName.OCPPCommCtrlr }
       const variable: VariableType = { name: OCPP20OptionalVariableName.HeartbeatInterval }
@@ -411,8 +422,11 @@ await describe('B05 - OCPP20VariableManager', async () => {
   })
 
   await describe('setVariables method tests', async () => {
-    const manager = OCPP20VariableManager.getInstance()
+    let manager: OCPP20VariableManager
 
+    beforeEach(() => {
+      manager = OCPP20VariableManager.getInstance()
+    })
     await it('should accept setting writable variables (Actual default)', () => {
       const request: OCPP20SetVariableDataType[] = [
         {
index 849995973af4068582490802338150bfc2d89e22..8f57ff78d6eff82521c0815d0bfb45e3a2bfffaf 100644 (file)
@@ -19,7 +19,7 @@ import { standardCleanup } from '../../../helpers/TestLifecycleHelpers.js'
 import { createMockChargingStation } from '../../ChargingStationTestUtils.js'
 import { createMockAuthRequest, createMockIdentifier } from './helpers/MockFactories.js'
 
-await describe('OCPP Authentication Integration Tests', async () => {
+await describe('OCPP Authentication', async () => {
   let mockChargingStation16: ChargingStation
   let mockChargingStation20: ChargingStation
 
@@ -54,16 +54,21 @@ await describe('OCPP Authentication Integration Tests', async () => {
     mock.restoreAll()
   })
 
-  await describe('OCPP 1.6 Authentication Flow', async () => {
+  await describe('OCPP 1.6 Authentication', async () => {
+    let authService16: OCPPAuthServiceImpl
+
+    beforeEach(() => {
+      authService16 = new OCPPAuthServiceImpl(mockChargingStation16)
+    })
+
     await it('should authenticate with valid identifier', async () => {
-      const authService = new OCPPAuthServiceImpl(mockChargingStation16)
       const request = createMockAuthRequest({
         connectorId: 1,
         context: AuthContext.TRANSACTION_START,
         identifier: createMockIdentifier(OCPPVersion.VERSION_16, 'VALID_ID_123'),
       })
 
-      const result = await authService.authenticate(request)
+      const result = await authService16.authenticate(request)
 
       expect(result).toBeDefined()
       expect(result.timestamp).toBeInstanceOf(Date)
@@ -73,7 +78,6 @@ await describe('OCPP Authentication Integration Tests', async () => {
     })
 
     await it('should handle multiple auth contexts', async () => {
-      const authService = new OCPPAuthServiceImpl(mockChargingStation16)
       const contexts = [
         AuthContext.TRANSACTION_START,
         AuthContext.TRANSACTION_STOP,
@@ -88,35 +92,39 @@ await describe('OCPP Authentication Integration Tests', async () => {
           identifier: createMockIdentifier(OCPPVersion.VERSION_16, `CONTEXT_TEST_${context}`),
         })
 
-        const result = await authService.authenticate(request)
+        const result = await authService16.authenticate(request)
         expect(result).toBeDefined()
         expect(result.timestamp).toBeInstanceOf(Date)
       }
     })
 
     await it('should authorize request directly', async () => {
-      const authService = new OCPPAuthServiceImpl(mockChargingStation16)
       const request = createMockAuthRequest({
         connectorId: 1,
         identifier: createMockIdentifier(OCPPVersion.VERSION_16, 'AUTH_DIRECT_TEST'),
       })
 
-      const result = await authService.authorize(request)
+      const result = await authService16.authorize(request)
       expect(result).toBeDefined()
       expect(result.timestamp).toBeInstanceOf(Date)
     })
   })
 
-  await describe('OCPP 2.0 Authentication Flow', async () => {
+  await describe('OCPP 2.0 Authentication', async () => {
+    let authService20: OCPPAuthServiceImpl
+
+    beforeEach(() => {
+      authService20 = new OCPPAuthServiceImpl(mockChargingStation20)
+    })
+
     await it('should authenticate with valid identifier', async () => {
-      const authService = new OCPPAuthServiceImpl(mockChargingStation20)
       const request = createMockAuthRequest({
         connectorId: 2,
         context: AuthContext.TRANSACTION_START,
         identifier: createMockIdentifier(OCPPVersion.VERSION_20, 'VALID_ID_456'),
       })
 
-      const result = await authService.authenticate(request)
+      const result = await authService20.authenticate(request)
 
       expect(result).toBeDefined()
       expect(result.timestamp).toBeInstanceOf(Date)
@@ -125,7 +133,6 @@ await describe('OCPP Authentication Integration Tests', async () => {
     })
 
     await it('should handle all auth contexts', async () => {
-      const authService = new OCPPAuthServiceImpl(mockChargingStation20)
       const contexts = [
         AuthContext.TRANSACTION_START,
         AuthContext.TRANSACTION_STOP,
@@ -140,7 +147,7 @@ await describe('OCPP Authentication Integration Tests', async () => {
           identifier: createMockIdentifier(OCPPVersion.VERSION_20, `V20_CONTEXT_${context}`),
         })
 
-        const result = await authService.authenticate(request)
+        const result = await authService20.authenticate(request)
         expect(result).toBeDefined()
         expect(result.timestamp).toBeInstanceOf(Date)
       }
@@ -148,8 +155,13 @@ await describe('OCPP Authentication Integration Tests', async () => {
   })
 
   await describe('Integration Error Scenarios', async () => {
+    let authServiceError: OCPPAuthServiceImpl
+
+    beforeEach(() => {
+      authServiceError = new OCPPAuthServiceImpl(mockChargingStation16)
+    })
+
     await it('should handle invalid identifier gracefully during auth flow', async () => {
-      const authService = new OCPPAuthServiceImpl(mockChargingStation16)
       const request = createMockAuthRequest({
         connectorId: 999, // Invalid connector
         context: AuthContext.TRANSACTION_START,
@@ -160,7 +172,7 @@ await describe('OCPP Authentication Integration Tests', async () => {
         },
       })
 
-      const result = await authService.authenticate(request)
+      const result = await authServiceError.authenticate(request)
 
       // Should return a result (not throw) with non-ACCEPTED status
       expect(result).toBeDefined()
@@ -169,8 +181,13 @@ await describe('OCPP Authentication Integration Tests', async () => {
   })
 
   await describe('Concurrent Operations', async () => {
+    let authServiceConcurrent: OCPPAuthServiceImpl
+
+    beforeEach(() => {
+      authServiceConcurrent = new OCPPAuthServiceImpl(mockChargingStation16)
+    })
+
     await it('should handle concurrent authentication requests with mixed contexts', async () => {
-      const authService = new OCPPAuthServiceImpl(mockChargingStation16)
       const requestCount = 10
       const promises = []
 
@@ -180,7 +197,7 @@ await describe('OCPP Authentication Integration Tests', async () => {
           context: i % 2 === 0 ? AuthContext.TRANSACTION_START : AuthContext.TRANSACTION_STOP,
           identifier: createMockIdentifier(OCPPVersion.VERSION_16, `CONCURRENT_${String(i)}`),
         })
-        promises.push(authService.authenticate(request))
+        promises.push(authServiceConcurrent.authenticate(request))
       }
 
       const results = await Promise.all(promises)
index 0dfb917dbe8500edc32f74217a438b2277f8eace..28b8566d64244d9f723809668572eb1ed6e87038 100644 (file)
@@ -407,6 +407,7 @@ await describe('OCPP20AuthAdapter', async () => {
     })
 
     afterEach(() => {
+      standardCleanup()
       mock.reset()
     })
 
index 8fb6c63f170fece47d4ee852d8d50cd522102c82..fd9c993dcbc47eb129d16d3ea4f1c867904ea783 100644 (file)
@@ -11,6 +11,7 @@ import type { AuthConfiguration } from '../../../../../src/charging-station/ocpp
 import { AuthComponentFactory } from '../../../../../src/charging-station/ocpp/auth/factories/AuthComponentFactory.js'
 import { OCPPVersion } from '../../../../../src/types/ocpp/OCPPVersion.js'
 import { standardCleanup } from '../../../../helpers/TestLifecycleHelpers.js'
+import { TEST_AUTHORIZATION_TIMEOUT_MS } from '../../../ChargingStationTestConstants.js'
 import { createMockChargingStation } from '../../../ChargingStationTestUtils.js'
 
 await describe('AuthComponentFactory', async () => {
@@ -74,7 +75,7 @@ await describe('AuthComponentFactory', async () => {
       const config: AuthConfiguration = {
         allowOfflineTxForUnknownId: false,
         authorizationCacheEnabled: true,
-        authorizationTimeout: 30000,
+        authorizationTimeout: TEST_AUTHORIZATION_TIMEOUT_MS,
         certificateAuthEnabled: false,
         localAuthListEnabled: false,
         localPreAuthorize: false,
@@ -97,7 +98,7 @@ await describe('AuthComponentFactory', async () => {
       const config: AuthConfiguration = {
         allowOfflineTxForUnknownId: false,
         authorizationCacheEnabled: false,
-        authorizationTimeout: 30000,
+        authorizationTimeout: TEST_AUTHORIZATION_TIMEOUT_MS,
         certificateAuthEnabled: false,
         localAuthListEnabled: true,
         localPreAuthorize: false,
@@ -115,7 +116,7 @@ await describe('AuthComponentFactory', async () => {
       const config: AuthConfiguration = {
         allowOfflineTxForUnknownId: false,
         authorizationCacheEnabled: false,
-        authorizationTimeout: 30000,
+        authorizationTimeout: TEST_AUTHORIZATION_TIMEOUT_MS,
         certificateAuthEnabled: false,
         localAuthListEnabled: false,
         localPreAuthorize: false,
@@ -131,7 +132,7 @@ await describe('AuthComponentFactory', async () => {
       const config: AuthConfiguration = {
         allowOfflineTxForUnknownId: false,
         authorizationCacheEnabled: false,
-        authorizationTimeout: 30000,
+        authorizationTimeout: TEST_AUTHORIZATION_TIMEOUT_MS,
         certificateAuthEnabled: false,
         localAuthListEnabled: true,
         localPreAuthorize: false,
@@ -156,7 +157,7 @@ await describe('AuthComponentFactory', async () => {
       const config: AuthConfiguration = {
         allowOfflineTxForUnknownId: false,
         authorizationCacheEnabled: false,
-        authorizationTimeout: 30000,
+        authorizationTimeout: TEST_AUTHORIZATION_TIMEOUT_MS,
         certificateAuthEnabled: false,
         localAuthListEnabled: false,
         localPreAuthorize: false,
@@ -177,7 +178,7 @@ await describe('AuthComponentFactory', async () => {
       const config: AuthConfiguration = {
         allowOfflineTxForUnknownId: false,
         authorizationCacheEnabled: false,
-        authorizationTimeout: 30000,
+        authorizationTimeout: TEST_AUTHORIZATION_TIMEOUT_MS,
         certificateAuthEnabled: false,
         localAuthListEnabled: false,
         localPreAuthorize: false,
@@ -203,7 +204,7 @@ await describe('AuthComponentFactory', async () => {
       const config: AuthConfiguration = {
         allowOfflineTxForUnknownId: false,
         authorizationCacheEnabled: false,
-        authorizationTimeout: 30000,
+        authorizationTimeout: TEST_AUTHORIZATION_TIMEOUT_MS,
         certificateAuthEnabled: true,
         localAuthListEnabled: false,
         localPreAuthorize: false,
@@ -230,7 +231,7 @@ await describe('AuthComponentFactory', async () => {
       const config: AuthConfiguration = {
         allowOfflineTxForUnknownId: false,
         authorizationCacheEnabled: false,
-        authorizationTimeout: 30000,
+        authorizationTimeout: TEST_AUTHORIZATION_TIMEOUT_MS,
         certificateAuthEnabled: false,
         localAuthListEnabled: false,
         localPreAuthorize: false,
@@ -257,7 +258,7 @@ await describe('AuthComponentFactory', async () => {
       const config: AuthConfiguration = {
         allowOfflineTxForUnknownId: false,
         authorizationCacheEnabled: false,
-        authorizationTimeout: 30000,
+        authorizationTimeout: TEST_AUTHORIZATION_TIMEOUT_MS,
         certificateAuthEnabled: false,
         localAuthListEnabled: true,
         localPreAuthorize: false,
@@ -286,7 +287,7 @@ await describe('AuthComponentFactory', async () => {
         allowOfflineTxForUnknownId: false,
         authorizationCacheEnabled: true,
         authorizationCacheLifetime: 600,
-        authorizationTimeout: 30000,
+        authorizationTimeout: TEST_AUTHORIZATION_TIMEOUT_MS,
         certificateAuthEnabled: false,
         localAuthListEnabled: true,
         localPreAuthorize: false,
@@ -304,7 +305,7 @@ await describe('AuthComponentFactory', async () => {
         allowOfflineTxForUnknownId: false,
         authorizationCacheEnabled: true,
         authorizationCacheLifetime: -1, // Invalid
-        authorizationTimeout: 30000,
+        authorizationTimeout: TEST_AUTHORIZATION_TIMEOUT_MS,
         certificateAuthEnabled: false,
         localAuthListEnabled: false,
         localPreAuthorize: false,
index f34eb837057c65b1ec9a9ac98d43b602860dc29c..acdecb624091a077ef3aa9552b6c6c43ad96c167 100644 (file)
@@ -102,6 +102,19 @@ await describe('LocalAuthStrategy', async () => {
       await strategy.initialize(config)
     })
 
+    afterEach(() => {
+      // Reset mock properties to prevent state pollution between tests
+      // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
+      delete (mockLocalAuthListManager as any).getEntry
+      // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
+      delete (mockAuthCache as any).get
+      // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
+      delete (mockAuthCache as any).set
+      // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
+      delete (mockAuthCache as any).remove
+      mock.restoreAll()
+    })
+
     await it('should authenticate using local auth list', async () => {
       mockLocalAuthListManager.getEntry = async () =>
         await Promise.resolve({
index 9e81b9e27934e19e8e0986906ea60e9da4f452a5..209e21f0fa722eaeaef7cb9e833e566a0fa41ccc 100644 (file)
@@ -5,7 +5,7 @@
 // Copyright Jerome Benoit. 2024-2025. All Rights Reserved.
 
 import { expect } from '@std/expect'
-import { afterEach, describe, it, mock } from 'node:test'
+import { afterEach, beforeEach, describe, it, mock } from 'node:test'
 import { gunzipSync } from 'node:zlib'
 
 import type { UUIDv4 } from '../../../src/types/index.js'
@@ -48,12 +48,18 @@ const createLargePayload = (status: ResponseStatus = ResponseStatus.SUCCESS) =>
 })
 
 await describe('UIHttpServer', async () => {
+  let server: TestableUIHttpServer
+
+  beforeEach(() => {
+    server = new TestableUIHttpServer(createHttpServerConfig())
+  })
+
   afterEach(() => {
     mock.restoreAll()
     standardCleanup()
   })
+
   await it('should delete response handler after successful send', () => {
-    const server = new TestableUIHttpServer(createHttpServerConfig())
     const res = new MockServerResponse()
 
     server.addResponseHandler(TEST_UUID, res)
@@ -67,15 +73,12 @@ await describe('UIHttpServer', async () => {
   })
 
   await it('should log error when response handler not found', () => {
-    const server = new TestableUIHttpServer(createHttpServerConfig())
-
     server.sendResponse([TEST_UUID, { status: ResponseStatus.SUCCESS }])
 
     expect(server.hasResponseHandler(TEST_UUID)).toBe(false)
   })
 
   await it('should set status code 400 for failure responses', () => {
-    const server = new TestableUIHttpServer(createHttpServerConfig())
     const res = new MockServerResponse()
 
     server.addResponseHandler(TEST_UUID, res)
@@ -87,7 +90,6 @@ await describe('UIHttpServer', async () => {
   })
 
   await it('should handle send errors gracefully without throwing', () => {
-    const server = new TestableUIHttpServer(createHttpServerConfig())
     const res = new MockServerResponse()
     res.end = (): never => {
       throw new Error('HTTP response end error')
@@ -100,7 +102,6 @@ await describe('UIHttpServer', async () => {
   })
 
   await it('should set application/json Content-Type header', () => {
-    const server = new TestableUIHttpServer(createHttpServerConfig())
     const res = new MockServerResponse()
 
     server.addResponseHandler(TEST_UUID, res)
@@ -110,7 +111,6 @@ await describe('UIHttpServer', async () => {
   })
 
   await it('should clean up response handlers after each response', () => {
-    const server = new TestableUIHttpServer(createHttpServerConfig())
     const res1 = new MockServerResponse()
     const res2 = new MockServerResponse()
 
@@ -126,7 +126,6 @@ await describe('UIHttpServer', async () => {
   })
 
   await it('should clear all handlers on server stop', () => {
-    const server = new TestableUIHttpServer(createHttpServerConfig())
     const res = new MockServerResponse()
 
     server.addResponseHandler(TEST_UUID, res)
@@ -138,7 +137,6 @@ await describe('UIHttpServer', async () => {
   })
 
   await it('should serialize response payload to JSON correctly', () => {
-    const server = new TestableUIHttpServer(createHttpServerConfig())
     const res = new MockServerResponse()
     const payload = {
       hashIdsSucceeded: ['station-1', 'station-2'],
@@ -155,7 +153,6 @@ await describe('UIHttpServer', async () => {
   })
 
   await it('should include error details in failure response', () => {
-    const server = new TestableUIHttpServer(createHttpServerConfig())
     const res = new MockServerResponse()
     const payload = {
       errorMessage: 'Test error',
@@ -174,71 +171,77 @@ await describe('UIHttpServer', async () => {
   })
 
   await it('should create server with valid HTTP configuration', () => {
-    const server = new UIHttpServer(createHttpServerConfig())
-
     expect(server).toBeDefined()
   })
 
   await it('should create server with custom host and port', () => {
-    const config = createMockUIServerConfiguration({
-      options: {
-        host: 'localhost',
-        port: 9090,
-      },
-      type: ApplicationProtocol.HTTP,
-    })
-
-    const server = new UIHttpServer(config)
-    expect(server).toBeDefined()
+    const serverCustom = new UIHttpServer(
+      createMockUIServerConfiguration({
+        options: {
+          host: 'localhost',
+          port: 9090,
+        },
+        type: ApplicationProtocol.HTTP,
+      })
+    )
+
+    expect(serverCustom).toBeDefined()
   })
 
   await describe('Gzip compression', async () => {
+    let gzipServer: TestableUIHttpServer
+
+    beforeEach(() => {
+      gzipServer = new TestableUIHttpServer(createHttpServerConfig())
+    })
+
+    afterEach(() => {
+      mock.restoreAll()
+      standardCleanup()
+    })
+
     await it('should skip compression when acceptsGzip is false', () => {
-      const server = new TestableUIHttpServer(createHttpServerConfig())
       const res = new MockServerResponse()
 
-      server.addResponseHandler(TEST_UUID, res)
-      server.setAcceptsGzip(TEST_UUID, false)
-      server.sendResponse([TEST_UUID, createLargePayload()])
+      gzipServer.addResponseHandler(TEST_UUID, res)
+      gzipServer.setAcceptsGzip(TEST_UUID, false)
+      gzipServer.sendResponse([TEST_UUID, createLargePayload()])
 
       expect(res.headers['Content-Encoding']).toBeUndefined()
       expect(res.headers['Content-Type']).toBe('application/json')
     })
 
     await it('should skip compression for small response payloads', () => {
-      const server = new TestableUIHttpServer(createHttpServerConfig())
       const res = new MockServerResponse()
 
-      server.addResponseHandler(TEST_UUID, res)
-      server.setAcceptsGzip(TEST_UUID, true)
-      server.sendResponse([TEST_UUID, { status: ResponseStatus.SUCCESS }])
+      gzipServer.addResponseHandler(TEST_UUID, res)
+      gzipServer.setAcceptsGzip(TEST_UUID, true)
+      gzipServer.sendResponse([TEST_UUID, { status: ResponseStatus.SUCCESS }])
 
       expect(res.headers['Content-Encoding']).toBeUndefined()
       expect(res.headers['Content-Type']).toBe('application/json')
     })
 
     await it('should skip compression when payload below threshold', () => {
-      const server = new TestableUIHttpServer(createHttpServerConfig())
       const res = new MockServerResponse()
       const smallPayload = {
         data: 'x'.repeat(100),
         status: ResponseStatus.SUCCESS,
       }
 
-      server.addResponseHandler(TEST_UUID, res)
-      server.setAcceptsGzip(TEST_UUID, true)
-      server.sendResponse([TEST_UUID, smallPayload])
+      gzipServer.addResponseHandler(TEST_UUID, res)
+      gzipServer.setAcceptsGzip(TEST_UUID, true)
+      gzipServer.sendResponse([TEST_UUID, smallPayload])
 
       expect(res.headers['Content-Encoding']).toBeUndefined()
     })
 
     await it('should set gzip Content-Encoding header for large responses', async () => {
-      const server = new TestableUIHttpServer(createHttpServerConfig())
       const res = new MockServerResponse()
 
-      server.addResponseHandler(TEST_UUID, res)
-      server.setAcceptsGzip(TEST_UUID, true)
-      server.sendResponse([TEST_UUID, createLargePayload()])
+      gzipServer.addResponseHandler(TEST_UUID, res)
+      gzipServer.setAcceptsGzip(TEST_UUID, true)
+      gzipServer.sendResponse([TEST_UUID, createLargePayload()])
 
       await waitForStreamFlush(GZIP_STREAM_FLUSH_DELAY_MS)
 
@@ -248,13 +251,12 @@ await describe('UIHttpServer', async () => {
     })
 
     await it('should decompress gzip response to original payload', async () => {
-      const server = new TestableUIHttpServer(createHttpServerConfig())
       const res = new MockServerResponse()
       const payload = createLargePayload()
 
-      server.addResponseHandler(TEST_UUID, res)
-      server.setAcceptsGzip(TEST_UUID, true)
-      server.sendResponse([TEST_UUID, payload])
+      gzipServer.addResponseHandler(TEST_UUID, res)
+      gzipServer.setAcceptsGzip(TEST_UUID, true)
+      gzipServer.sendResponse([TEST_UUID, payload])
 
       await waitForStreamFlush(GZIP_STREAM_FLUSH_DELAY_MS)
 
@@ -269,29 +271,27 @@ await describe('UIHttpServer', async () => {
     })
 
     await it('should skip compression when acceptsGzip context missing', () => {
-      const server = new TestableUIHttpServer(createHttpServerConfig())
       const res = new MockServerResponse()
 
-      server.addResponseHandler(TEST_UUID, res)
-      server.sendResponse([TEST_UUID, createLargePayload()])
+      gzipServer.addResponseHandler(TEST_UUID, res)
+      gzipServer.sendResponse([TEST_UUID, createLargePayload()])
 
       expect(res.headers['Content-Encoding']).toBeUndefined()
       expect(res.headers['Content-Type']).toBe('application/json')
     })
 
     await it('should cleanup acceptsGzip context after response sent', async () => {
-      const server = new TestableUIHttpServer(createHttpServerConfig())
       const res = new MockServerResponse()
 
-      server.addResponseHandler(TEST_UUID, res)
-      server.setAcceptsGzip(TEST_UUID, true)
-      expect(server.getAcceptsGzip().has(TEST_UUID)).toBe(true)
+      gzipServer.addResponseHandler(TEST_UUID, res)
+      gzipServer.setAcceptsGzip(TEST_UUID, true)
+      expect(gzipServer.getAcceptsGzip().has(TEST_UUID)).toBe(true)
 
-      server.sendResponse([TEST_UUID, createLargePayload()])
+      gzipServer.sendResponse([TEST_UUID, createLargePayload()])
 
       await waitForStreamFlush(GZIP_STREAM_FLUSH_DELAY_MS)
 
-      expect(server.getAcceptsGzip().has(TEST_UUID)).toBe(false)
+      expect(gzipServer.getAcceptsGzip().has(TEST_UUID)).toBe(false)
     })
   })
 })
index ba1a062ebe548d9fd8210223694e0bd4d4e5c171..cd07d2380ff52d299c257e96ef1e79b1082b4dbe 100644 (file)
@@ -24,7 +24,7 @@ await describe('UIServerSecurity', async () => {
     mock.restoreAll()
     standardCleanup()
   })
-  await describe('isValidCredential()', async () => {
+  await describe('IsValidCredential', async () => {
     await it('should return true for matching credentials', () => {
       expect(isValidCredential('myPassword123', 'myPassword123')).toBe(true)
     })
@@ -43,30 +43,33 @@ await describe('UIServerSecurity', async () => {
     })
   })
 
-  await describe('createBodySizeLimiter()', async () => {
+  await describe('CreateBodySizeLimiter', async () => {
+    let limiter: ReturnType<typeof createBodySizeLimiter>
+
     await it('should return true when bytes under limit', () => {
-      const limiter = createBodySizeLimiter(1000)
+      limiter = createBodySizeLimiter(1000)
 
       expect(limiter(500)).toBe(true)
     })
 
     await it('should return false when accumulated bytes exceed limit', () => {
-      const limiter = createBodySizeLimiter(1000)
+      limiter = createBodySizeLimiter(1000)
       limiter(600)
-
       expect(limiter(500)).toBe(false)
     })
 
     await it('should return true at exact limit boundary', () => {
-      const limiter = createBodySizeLimiter(1000)
+      limiter = createBodySizeLimiter(1000)
 
       expect(limiter(1000)).toBe(true)
     })
   })
 
-  await describe('createRateLimiter()', async () => {
+  await describe('CreateRateLimiter', async () => {
+    let limiter: ReturnType<typeof createRateLimiter>
+
     await it('should allow requests under rate limit', () => {
-      const limiter = createRateLimiter(5, 1000)
+      limiter = createRateLimiter(5, 1000)
 
       for (let i = 0; i < 5; i++) {
         expect(limiter('192.168.1.1')).toBe(true)
@@ -74,16 +77,15 @@ await describe('UIServerSecurity', async () => {
     })
 
     await it('should block requests exceeding rate limit', () => {
-      const limiter = createRateLimiter(3, 1000)
+      limiter = createRateLimiter(3, 1000)
       limiter('192.168.1.1')
       limiter('192.168.1.1')
       limiter('192.168.1.1')
-
       expect(limiter('192.168.1.1')).toBe(false)
     })
 
     await it('should reset window after time expires', async () => {
-      const limiter = createRateLimiter(2, 100)
+      limiter = createRateLimiter(2, 100)
       limiter('10.0.0.1')
       limiter('10.0.0.1')
       expect(limiter('10.0.0.1')).toBe(false)
@@ -94,7 +96,7 @@ await describe('UIServerSecurity', async () => {
     })
 
     await it('should reject new IPs when at max tracked capacity', () => {
-      const limiter = createRateLimiter(10, 60000, 3)
+      limiter = createRateLimiter(10, 60000, 3)
 
       expect(limiter('192.168.1.1')).toBe(true)
       expect(limiter('192.168.1.2')).toBe(true)
@@ -103,7 +105,7 @@ await describe('UIServerSecurity', async () => {
     })
 
     await it('should allow existing IPs when at max capacity', () => {
-      const limiter = createRateLimiter(10, 60000, 2)
+      limiter = createRateLimiter(10, 60000, 2)
 
       expect(limiter('192.168.1.1')).toBe(true)
       expect(limiter('192.168.1.2')).toBe(true)
@@ -112,7 +114,7 @@ await describe('UIServerSecurity', async () => {
     })
 
     await it('should cleanup expired entries when at capacity', async () => {
-      const limiter = createRateLimiter(10, 50, 2)
+      limiter = createRateLimiter(10, 50, 2)
       expect(limiter('192.168.1.1')).toBe(true)
       expect(limiter('192.168.1.2')).toBe(true)
 
@@ -122,7 +124,7 @@ await describe('UIServerSecurity', async () => {
     })
   })
 
-  await describe('isValidNumberOfStations()', async () => {
+  await describe('IsValidNumberOfStations', async () => {
     await it('should return true for valid number within limit', () => {
       expect(isValidNumberOfStations(50, DEFAULT_MAX_STATIONS)).toBe(true)
     })