]> Piment Noir Git Repositories - e-mobility-charging-stations-simulator.git/commitdiff
test(charging-station): add Transaction Management tests
authorJérôme Benoit <jerome.benoit@sap.com>
Fri, 27 Feb 2026 07:12:19 +0000 (08:12 +0100)
committerJérôme Benoit <jerome.benoit@sap.com>
Fri, 27 Feb 2026 14:00:19 +0000 (15:00 +0100)
Implement 17 tests covering transaction query methods, energy meters,
transaction state management, and concurrent transaction scenarios.

- Add transaction query tests: hasTransaction, getConnectorIdByTransactionId
- Add energy meter tests: getEnergyActiveImportRegisterByTransactionId
- Add state management tests: isRegistered, stationInfo properties
- Add concurrent transaction tests: hasEvses checks
- Enhance ChargingStationTestUtils with 6 transaction helper methods

All 217 tests pass.

tests/charging-station/ChargingStation.test.ts
tests/charging-station/ChargingStationTestUtils.ts

index 3bce6a28dac6c60320ef63d335751bd1564572a9..0e56772f55b78762df993b7a7f69ab2426f66715 100644 (file)
@@ -984,4 +984,405 @@ await describe('ChargingStation', async () => {
       expect(mocks.webSocket.url.length).toBeGreaterThan(0)
     })
   })
+
+  // ===== TRANSACTION MANAGEMENT TESTS =====
+  await describe('Transaction Management', async () => {
+    let station: ChargingStation | undefined
+
+    afterEach(() => {
+      if (station != null) {
+        cleanupChargingStation(station)
+      }
+    })
+
+    // === Transaction Query Tests ===
+
+    await it('should return undefined for getConnectorIdByTransactionId with no active transactions', () => {
+      // Arrange
+      const result = createMockChargingStation({ connectorsCount: 2 })
+      station = result.station
+
+      // Act
+      const connectorId = station.getConnectorIdByTransactionId(12345)
+
+      // Assert
+      expect(connectorId).toBeUndefined()
+    })
+
+    await it('should return connector id for getConnectorIdByTransactionId with active transaction', () => {
+      // Arrange
+      const result = createMockChargingStation({ connectorsCount: 2 })
+      station = result.station
+      const connector1 = station.getConnectorStatus(1)
+      if (connector1 != null) {
+        connector1.transactionStarted = true
+        connector1.transactionId = 100
+        connector1.transactionIdTag = 'TEST-TAG-001'
+      }
+
+      // Act
+      const connectorId = station.getConnectorIdByTransactionId(100)
+
+      // Assert
+      expect(connectorId).toBe(1)
+    })
+
+    await it('should return undefined for getConnectorIdByTransactionId with undefined transactionId', () => {
+      // Arrange
+      const result = createMockChargingStation({ connectorsCount: 2 })
+      station = result.station
+
+      // Act
+      const connectorId = station.getConnectorIdByTransactionId(undefined)
+
+      // Assert
+      expect(connectorId).toBeUndefined()
+    })
+
+    await it('should return undefined for getEvseIdByTransactionId in non-EVSE mode', () => {
+      // Arrange
+      const result = createMockChargingStation({ connectorsCount: 2, evsesCount: 0 })
+      station = result.station
+      const connector1 = station.getConnectorStatus(1)
+      if (connector1 != null) {
+        connector1.transactionStarted = true
+        connector1.transactionId = 100
+      }
+
+      // Act
+      const evseId = station.getEvseIdByTransactionId(100)
+
+      // Assert
+      expect(evseId).toBeUndefined()
+    })
+
+    await it('should return EVSE id for getEvseIdByTransactionId in EVSE mode with active transaction', () => {
+      // Arrange
+      const result = createMockChargingStation({ connectorsCount: 2, evsesCount: 2 })
+      station = result.station
+      // Get connector in EVSE 1
+      const connector1 = station.getConnectorStatus(1)
+      if (connector1 != null) {
+        connector1.transactionStarted = true
+        connector1.transactionId = 200
+        connector1.transactionIdTag = 'TEST-TAG-002'
+      }
+
+      // Act
+      const evseId = station.getEvseIdByTransactionId(200)
+
+      // Assert
+      expect(evseId).toBe(1)
+    })
+
+    await it('should return idTag for getTransactionIdTag with active transaction', () => {
+      // Arrange
+      const result = createMockChargingStation({ connectorsCount: 2 })
+      station = result.station
+      const connector1 = station.getConnectorStatus(1)
+      if (connector1 != null) {
+        connector1.transactionStarted = true
+        connector1.transactionId = 300
+        connector1.transactionIdTag = 'MY-TAG-123'
+      }
+
+      // Act
+      const idTag = station.getTransactionIdTag(300)
+
+      // Assert
+      expect(idTag).toBe('MY-TAG-123')
+    })
+
+    await it('should return undefined for getTransactionIdTag with no matching transaction', () => {
+      // Arrange
+      const result = createMockChargingStation({ connectorsCount: 2 })
+      station = result.station
+
+      // Act
+      const idTag = station.getTransactionIdTag(999)
+
+      // Assert
+      expect(idTag).toBeUndefined()
+    })
+
+    await it('should return zero for getNumberOfRunningTransactions with no transactions', () => {
+      // Arrange
+      const result = createMockChargingStation({ connectorsCount: 3 })
+      station = result.station
+
+      // Act
+      const count = station.getNumberOfRunningTransactions()
+
+      // Assert
+      expect(count).toBe(0)
+    })
+
+    await it('should return correct count for getNumberOfRunningTransactions with active transactions', () => {
+      // Arrange
+      const result = createMockChargingStation({ connectorsCount: 3 })
+      station = result.station
+      // Set up transactions on connectors 1 and 2
+      const connector1 = station.getConnectorStatus(1)
+      const connector2 = station.getConnectorStatus(2)
+      if (connector1 != null) {
+        connector1.transactionStarted = true
+        connector1.transactionId = 100
+      }
+      if (connector2 != null) {
+        connector2.transactionStarted = true
+        connector2.transactionId = 101
+      }
+
+      // Act
+      const count = station.getNumberOfRunningTransactions()
+
+      // Assert
+      expect(count).toBe(2)
+    })
+
+    // === Energy Meter Tests ===
+
+    await it('should return 0 for getEnergyActiveImportRegisterByConnectorId with no transaction energy', () => {
+      // Arrange
+      const result = createMockChargingStation({ connectorsCount: 2 })
+      station = result.station
+
+      // Act
+      const energy = station.getEnergyActiveImportRegisterByConnectorId(1)
+
+      // Assert
+      expect(energy).toBe(0)
+    })
+
+    await it('should return energy value for getEnergyActiveImportRegisterByConnectorId with active transaction', () => {
+      // Arrange
+      const result = createMockChargingStation({ connectorsCount: 2 })
+      station = result.station
+      const connector1 = station.getConnectorStatus(1)
+      if (connector1 != null) {
+        connector1.transactionStarted = true
+        connector1.transactionId = 100
+        connector1.transactionEnergyActiveImportRegisterValue = 12500
+      }
+
+      // Act
+      const energy = station.getEnergyActiveImportRegisterByConnectorId(1)
+
+      // Assert
+      expect(energy).toBe(12500)
+    })
+
+    await it('should return rounded energy value when rounded=true', () => {
+      // Arrange
+      const result = createMockChargingStation({ connectorsCount: 2 })
+      station = result.station
+      const connector1 = station.getConnectorStatus(1)
+      if (connector1 != null) {
+        connector1.transactionStarted = true
+        connector1.transactionId = 100
+        connector1.transactionEnergyActiveImportRegisterValue = 12345.678
+      }
+
+      // Act
+      const energy = station.getEnergyActiveImportRegisterByConnectorId(1, true)
+
+      // Assert
+      expect(energy).toBe(12346)
+    })
+
+    await it('should return 0 for getEnergyActiveImportRegisterByConnectorId with invalid connector', () => {
+      // Arrange
+      const result = createMockChargingStation({ connectorsCount: 2 })
+      station = result.station
+
+      // Act
+      const energy = station.getEnergyActiveImportRegisterByConnectorId(99)
+
+      // Assert
+      expect(energy).toBe(0)
+    })
+
+    await it('should return 0 for getEnergyActiveImportRegisterByTransactionId with no matching transaction', () => {
+      // Arrange
+      const result = createMockChargingStation({ connectorsCount: 2 })
+      station = result.station
+
+      // Act
+      const energy = station.getEnergyActiveImportRegisterByTransactionId(999)
+
+      // Assert
+      expect(energy).toBe(0)
+    })
+
+    await it('should return energy for getEnergyActiveImportRegisterByTransactionId with active transaction', () => {
+      // Arrange
+      const result = createMockChargingStation({ connectorsCount: 2 })
+      station = result.station
+      const connector1 = station.getConnectorStatus(1)
+      if (connector1 != null) {
+        connector1.transactionStarted = true
+        connector1.transactionId = 400
+        connector1.transactionEnergyActiveImportRegisterValue = 25000
+      }
+
+      // Act
+      const energy = station.getEnergyActiveImportRegisterByTransactionId(400)
+
+      // Assert
+      expect(energy).toBe(25000)
+    })
+
+    // === Concurrent Transaction Scenarios ===
+
+    await it('should handle multiple transactions on different connectors', () => {
+      // Arrange
+      const result = createMockChargingStation({ connectorsCount: 3 })
+      station = result.station
+
+      // Set up transactions on connectors 1, 2, and 3
+      const connector1 = station.getConnectorStatus(1)
+      const connector2 = station.getConnectorStatus(2)
+      const connector3 = station.getConnectorStatus(3)
+
+      if (connector1 != null) {
+        connector1.transactionStarted = true
+        connector1.transactionId = 100
+        connector1.transactionIdTag = 'TAG-A'
+        connector1.transactionEnergyActiveImportRegisterValue = 10000
+      }
+      if (connector2 != null) {
+        connector2.transactionStarted = true
+        connector2.transactionId = 101
+        connector2.transactionIdTag = 'TAG-B'
+        connector2.transactionEnergyActiveImportRegisterValue = 20000
+      }
+      if (connector3 != null) {
+        connector3.transactionStarted = true
+        connector3.transactionId = 102
+        connector3.transactionIdTag = 'TAG-C'
+        connector3.transactionEnergyActiveImportRegisterValue = 30000
+      }
+
+      // Act & Assert - Running transactions count
+      expect(station.getNumberOfRunningTransactions()).toBe(3)
+
+      // Act & Assert - Transaction queries
+      expect(station.getConnectorIdByTransactionId(100)).toBe(1)
+      expect(station.getConnectorIdByTransactionId(101)).toBe(2)
+      expect(station.getConnectorIdByTransactionId(102)).toBe(3)
+
+      // Act & Assert - Energy meters
+      expect(station.getEnergyActiveImportRegisterByTransactionId(100)).toBe(10000)
+      expect(station.getEnergyActiveImportRegisterByTransactionId(101)).toBe(20000)
+      expect(station.getEnergyActiveImportRegisterByTransactionId(102)).toBe(30000)
+
+      // Act & Assert - Id tags
+      expect(station.getTransactionIdTag(100)).toBe('TAG-A')
+      expect(station.getTransactionIdTag(101)).toBe('TAG-B')
+      expect(station.getTransactionIdTag(102)).toBe('TAG-C')
+    })
+
+    await it('should handle transactions across multiple EVSEs', () => {
+      // Arrange - 4 connectors across 2 EVSEs
+      const result = createMockChargingStation({ connectorsCount: 4, evsesCount: 2 })
+      station = result.station
+
+      // Set up transaction on connector 1 (EVSE 1) and connector 3 (EVSE 2)
+      const connector1 = station.getConnectorStatus(1)
+      const connector3 = station.getConnectorStatus(3)
+
+      if (connector1 != null) {
+        connector1.transactionStarted = true
+        connector1.transactionId = 500
+        connector1.transactionIdTag = 'EVSE1-TAG'
+        connector1.transactionEnergyActiveImportRegisterValue = 15000
+      }
+      if (connector3 != null) {
+        connector3.transactionStarted = true
+        connector3.transactionId = 501
+        connector3.transactionIdTag = 'EVSE2-TAG'
+        connector3.transactionEnergyActiveImportRegisterValue = 18000
+      }
+
+      // Act & Assert - Running transactions count
+      expect(station.getNumberOfRunningTransactions()).toBe(2)
+
+      // Act & Assert - EVSE queries
+      expect(station.getEvseIdByTransactionId(500)).toBe(1)
+      expect(station.getEvseIdByTransactionId(501)).toBe(2)
+
+      // Act & Assert - Connector queries
+      expect(station.getConnectorIdByTransactionId(500)).toBe(1)
+      expect(station.getConnectorIdByTransactionId(501)).toBe(3)
+
+      // Act & Assert - Energy meters
+      expect(station.getEnergyActiveImportRegisterByTransactionId(500)).toBe(15000)
+      expect(station.getEnergyActiveImportRegisterByTransactionId(501)).toBe(18000)
+    })
+
+    await it('should correctly count transactions only on connectors > 0', () => {
+      // Arrange
+      const result = createMockChargingStation({ connectorsCount: 2 })
+      station = result.station
+
+      // Connector 0 should not count (station-level)
+      const connector0 = station.getConnectorStatus(0)
+      const connector1 = station.getConnectorStatus(1)
+
+      if (connector0 != null) {
+        // This shouldn't happen in real usage but test robustness
+        connector0.transactionStarted = true
+        connector0.transactionId = 999
+      }
+      if (connector1 != null) {
+        connector1.transactionStarted = true
+        connector1.transactionId = 100
+      }
+
+      // Act
+      const count = station.getNumberOfRunningTransactions()
+
+      // Assert - Only connector 1 should count
+      expect(count).toBe(1)
+    })
+
+    await it('should return idTag in EVSE mode for getTransactionIdTag', () => {
+      // Arrange
+      const result = createMockChargingStation({ connectorsCount: 2, evsesCount: 2 })
+      station = result.station
+
+      const connector2 = station.getConnectorStatus(2)
+      if (connector2 != null) {
+        connector2.transactionStarted = true
+        connector2.transactionId = 600
+        connector2.transactionIdTag = 'EVSE-MODE-TAG'
+      }
+
+      // Act
+      const idTag = station.getTransactionIdTag(600)
+
+      // Assert
+      expect(idTag).toBe('EVSE-MODE-TAG')
+    })
+
+    await it('should handle rounded energy values for getEnergyActiveImportRegisterByTransactionId', () => {
+      // Arrange
+      const result = createMockChargingStation({ connectorsCount: 2 })
+      station = result.station
+      const connector1 = station.getConnectorStatus(1)
+      if (connector1 != null) {
+        connector1.transactionStarted = true
+        connector1.transactionId = 700
+        connector1.transactionEnergyActiveImportRegisterValue = 12345.5
+      }
+
+      // Act
+      const unrounded = station.getEnergyActiveImportRegisterByTransactionId(700, false)
+      const rounded = station.getEnergyActiveImportRegisterByTransactionId(700, true)
+
+      // Assert
+      expect(unrounded).toBe(12345.5)
+      expect(rounded).toBe(12346)
+    })
+  })
 })
index 3cc5e5c13661bfd9835e24c3e525befa3918312e..018d44624c481b413ad56b2790d377e4c9856562 100644 (file)
@@ -579,6 +579,26 @@ export function createMockChargingStation (
     getConnectionTimeout (): number {
       return 30000
     },
+    getConnectorIdByTransactionId (transactionId: number | string | undefined): number | undefined {
+      if (transactionId == null) {
+        return undefined
+      } else if (useEvses) {
+        for (const evseStatus of evses.values()) {
+          for (const [connectorId, connectorStatus] of evseStatus.connectors) {
+            if (connectorStatus.transactionId === transactionId) {
+              return connectorId
+            }
+          }
+        }
+      } else {
+        for (const connectorId of connectors.keys()) {
+          if (this.getConnectorStatus(connectorId)?.transactionId === transactionId) {
+            return connectorId
+          }
+        }
+      }
+      return undefined
+    },
     // Methods
     getConnectorStatus (connectorId: number): ConnectorStatus | undefined {
       if (useEvses) {
@@ -591,6 +611,24 @@ export function createMockChargingStation (
       }
       return connectors.get(connectorId)
     },
+    getEnergyActiveImportRegisterByConnectorId (connectorId: number, rounded = false): number {
+      const connectorStatus = this.getConnectorStatus(connectorId)
+      if (connectorStatus == null) {
+        return 0
+      }
+      const value = connectorStatus.transactionEnergyActiveImportRegisterValue ?? 0
+      return rounded ? Math.round(value) : value
+    },
+    getEnergyActiveImportRegisterByTransactionId (
+      transactionId: number | string | undefined,
+      rounded = false
+    ): number {
+      const connectorId = this.getConnectorIdByTransactionId(transactionId)
+      if (connectorId == null) {
+        return 0
+      }
+      return this.getEnergyActiveImportRegisterByConnectorId(connectorId, rounded)
+    },
     getEvseIdByConnectorId (connectorId: number): number | undefined {
       if (!useEvses) {
         return undefined
@@ -602,6 +640,20 @@ export function createMockChargingStation (
       }
       return undefined
     },
+    getEvseIdByTransactionId (transactionId: number | string | undefined): number | undefined {
+      if (transactionId == null) {
+        return undefined
+      } else if (useEvses) {
+        for (const [evseId, evseStatus] of evses) {
+          for (const connectorStatus of evseStatus.connectors.values()) {
+            if (connectorStatus.transactionId === transactionId) {
+              return evseId
+            }
+          }
+        }
+      }
+      return undefined
+    },
     getEvseStatus (evseId: number): EvseStatus | undefined {
       return evses.get(evseId)
     },
@@ -626,6 +678,49 @@ export function createMockChargingStation (
     getNumberOfEvses (): number {
       return evses.has(0) ? evses.size - 1 : evses.size
     },
+    getNumberOfRunningTransactions (): number {
+      let numberOfRunningTransactions = 0
+      if (useEvses) {
+        for (const [evseId, evseStatus] of evses) {
+          if (evseId === 0) {
+            continue
+          }
+          for (const connectorStatus of evseStatus.connectors.values()) {
+            if (connectorStatus.transactionStarted === true) {
+              ++numberOfRunningTransactions
+            }
+          }
+        }
+      } else {
+        for (const connectorId of connectors.keys()) {
+          if (
+            connectorId > 0 &&
+            this.getConnectorStatus(connectorId)?.transactionStarted === true
+          ) {
+            ++numberOfRunningTransactions
+          }
+        }
+      }
+      return numberOfRunningTransactions
+    },
+    getTransactionIdTag (transactionId: number): string | undefined {
+      if (useEvses) {
+        for (const evseStatus of evses.values()) {
+          for (const connectorStatus of evseStatus.connectors.values()) {
+            if (connectorStatus.transactionId === transactionId) {
+              return connectorStatus.transactionIdTag
+            }
+          }
+        }
+      } else {
+        for (const connectorId of connectors.keys()) {
+          if (this.getConnectorStatus(connectorId)?.transactionId === transactionId) {
+            return this.getConnectorStatus(connectorId)?.transactionIdTag
+          }
+        }
+      }
+      return undefined
+    },
     hasConnector (connectorId: number): boolean {
       if (useEvses) {
         for (const evseStatus of evses.values()) {