From eada3ac236a9df54ae6d62d67eafd74c61ea1756 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Sun, 5 Apr 2026 17:46:39 +0200 Subject: [PATCH] test: harmonize RemoteStartTransaction tests across OCPP stacks OCPP 1.6: - Remove fabricated TC_0XX_CS codes (no official 1.6 test case spec) - Reorder tests by category: happy path, input validation, availability, transaction state, event listeners OCPP 2.0: - Add 2 EVSE availability tests (skip inoperative, all inoperative) - Add official TC refs where tests match Part 6 spec (TC_F_02_CS, TC_F_03_CS, TC_K_37_CS) - Harmonize FR comment format on new tests --- ...uestService-RemoteStartTransaction.test.ts | 58 ++++++++++--------- ...estService-RequestStartTransaction.test.ts | 51 +++++++++++++++- 2 files changed, 81 insertions(+), 28 deletions(-) diff --git a/tests/charging-station/ocpp/1.6/OCPP16IncomingRequestService-RemoteStartTransaction.test.ts b/tests/charging-station/ocpp/1.6/OCPP16IncomingRequestService-RemoteStartTransaction.test.ts index bcf40e75..2010ee0d 100644 --- a/tests/charging-station/ocpp/1.6/OCPP16IncomingRequestService-RemoteStartTransaction.test.ts +++ b/tests/charging-station/ocpp/1.6/OCPP16IncomingRequestService-RemoteStartTransaction.test.ts @@ -41,12 +41,14 @@ await describe('OCPP16IncomingRequestService — RemoteStartTransaction', async standardCleanup() }) - // @spec §5.11 — TC_021_CS: connectorId=0 must be rejected - await it('should reject remote start transaction with connectorId=0', async () => { + // --- Happy path --- + + // @spec §5.11 — Valid connectorId with available connector + await it('should accept remote start transaction with valid connectorId and available connector', async () => { // Arrange const { station, testableService } = testContext const request: RemoteStartTransactionRequest = { - connectorId: 0, + connectorId: 1, idTag: TEST_ID_TAG, } @@ -54,15 +56,14 @@ await describe('OCPP16IncomingRequestService — RemoteStartTransaction', async const response = await testableService.handleRequestRemoteStartTransaction(station, request) // Assert - assert.strictEqual(response.status, GenericStatus.Rejected) + assert.strictEqual(response.status, GenericStatus.Accepted) }) - // @spec §5.11 — TC_013_CS: Valid connectorId with available connector - await it('should accept remote start transaction with valid connectorId and available connector', async () => { + // @spec §5.11 — No connectorId specified, finds first available connector + await it('should accept remote start transaction without connectorId when connector is available', async () => { // Arrange const { station, testableService } = testContext const request: RemoteStartTransactionRequest = { - connectorId: 1, idTag: TEST_ID_TAG, } @@ -73,17 +74,14 @@ await describe('OCPP16IncomingRequestService — RemoteStartTransaction', async assert.strictEqual(response.status, GenericStatus.Accepted) }) - // @spec §5.11 — TC_014_CS: All connectors have active transactions, no connectorId specified - await it('should reject remote start transaction when all connectors have active transactions', async () => { + // --- Input validation --- + + // @spec §5.11 — connectorId=0 must be rejected + await it('should reject remote start transaction with connectorId=0', async () => { // Arrange const { station, testableService } = testContext - - // Set all connectors as having active transactions - for (let connectorId = 1; connectorId <= station.getNumberOfConnectors(); connectorId++) { - setupConnectorWithTransaction(station, connectorId, { transactionId: connectorId * 100 }) - } - const request: RemoteStartTransactionRequest = { + connectorId: 0, idTag: TEST_ID_TAG, } @@ -94,11 +92,12 @@ await describe('OCPP16IncomingRequestService — RemoteStartTransaction', async assert.strictEqual(response.status, GenericStatus.Rejected) }) - // @spec §5.11 — TC_015_CS: No connectorId specified, finds first available connector - await it('should accept remote start transaction without connectorId when connector is available', async () => { + // @spec §5.11 — Non-existing connector + await it('should reject remote start transaction with non-existing connectorId', async () => { // Arrange const { station, testableService } = testContext const request: RemoteStartTransactionRequest = { + connectorId: 99, idTag: TEST_ID_TAG, } @@ -106,9 +105,11 @@ await describe('OCPP16IncomingRequestService — RemoteStartTransaction', async const response = await testableService.handleRequestRemoteStartTransaction(station, request) // Assert - assert.strictEqual(response.status, GenericStatus.Accepted) + assert.strictEqual(response.status, GenericStatus.Rejected) }) + // --- Availability --- + // @spec §5.11 — Connector in Unavailable (Inoperative) status await it('should reject remote start transaction when connector is unavailable', async () => { // Arrange @@ -175,11 +176,7 @@ await describe('OCPP16IncomingRequestService — RemoteStartTransaction', async // Assert — should select connector 2 (which is Operative) and accept assert.strictEqual(response.status, GenericStatus.Accepted) - assert.strictEqual( - request.connectorId, - 2, - 'should auto-select connector 2 since connector 1 is Inoperative' - ) + assert.strictEqual(request.connectorId, 2) }) // @spec §5.11 — All connectors Inoperative, no connectorId specified @@ -206,12 +203,19 @@ await describe('OCPP16IncomingRequestService — RemoteStartTransaction', async assert.strictEqual(response.status, GenericStatus.Rejected) }) - // @spec §5.11 — Non-existing connector - await it('should reject remote start transaction with non-existing connectorId', async () => { + // --- Transaction state --- + + // @spec §5.11 — All connectors have active transactions, no connectorId specified + await it('should reject remote start transaction when all connectors have active transactions', async () => { // Arrange const { station, testableService } = testContext + + // Set all connectors as having active transactions + for (let connectorId = 1; connectorId <= station.getNumberOfConnectors(); connectorId++) { + setupConnectorWithTransaction(station, connectorId, { transactionId: connectorId * 100 }) + } + const request: RemoteStartTransactionRequest = { - connectorId: 99, idTag: TEST_ID_TAG, } @@ -222,6 +226,8 @@ await describe('OCPP16IncomingRequestService — RemoteStartTransaction', async assert.strictEqual(response.status, GenericStatus.Rejected) }) + // --- Event listeners --- + await describe('REMOTE_START_TRANSACTION event listener', async () => { let incomingRequestService: OCPP16IncomingRequestService let requestHandlerMock: ReturnType diff --git a/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-RequestStartTransaction.test.ts b/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-RequestStartTransaction.test.ts index 4eea501f..7a632ca9 100644 --- a/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-RequestStartTransaction.test.ts +++ b/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-RequestStartTransaction.test.ts @@ -32,6 +32,7 @@ import { OCPP20ComponentName, OCPP20IdTokenEnumType, OCPP20IncomingRequestCommand, + OCPP20OperationalStatusEnumType, OCPP20RequestCommand, OCPP20RequiredVariableName, OCPP20TransactionEventEnumType, @@ -87,7 +88,7 @@ await describe('F01 & F02 - Remote Start Transaction', async () => { OCPPAuthServiceFactory.clearAllInstances() }) - // FR: F01.FR.03, F01.FR.04, F01.FR.05, F01.FR.13 + // FR: F01.FR.03, F01.FR.04, F01.FR.05, F01.FR.13 — TC_F_02_CS await it('should handle RequestStartTransaction with valid evseId and idToken', async () => { const validRequest: OCPP20RequestStartTransactionRequest = { evseId: 1, @@ -262,7 +263,7 @@ await describe('F01 & F02 - Remote Start Transaction', async () => { assert.notStrictEqual(response.transactionId, undefined) }) - // OCPP 2.0.1 §2.10 ChargingProfile validation tests + // OCPP 2.0.1 §2.10 ChargingProfile validation tests — TC_K_37_CS await it('should accept RequestStartTransaction with valid TxProfile (no transactionId)', async () => { const validChargingProfile: OCPP20ChargingProfileType = { chargingProfileKind: OCPP20ChargingProfileKindEnumType.Relative, @@ -458,6 +459,7 @@ await describe('F01 & F02 - Remote Start Transaction', async () => { assert.strictEqual(response.status, RequestStartStopStatusEnumType.Rejected) }) + // FR: F01.FR.02 — TC_F_03_CS await it('should accept RequestStartTransaction when AuthorizeRemoteStart is false', async () => { // Arrange const variableManager = OCPP20VariableManager.getInstance() @@ -519,6 +521,51 @@ await describe('F01 & F02 - Remote Start Transaction', async () => { assert.ok(response.transactionId.length > 0, 'transactionId should not be empty') }) + // FR: F01 — EVSE availability during auto-selection + await it('should skip inoperative EVSEs during auto-selection without evseId', async () => { + // Arrange + const evse1Status = mockStation.getEvseStatus(1) + if (evse1Status != null) { + evse1Status.availability = OCPP20OperationalStatusEnumType.Inoperative + } + + const request: OCPP20RequestStartTransactionRequest = { + idToken: { + idToken: 'VALID_TOKEN_123', + type: OCPP20IdTokenEnumType.ISO14443, + }, + remoteStartId: 500, + } + + // Act + const response = await testableService.handleRequestStartTransaction(mockStation, request) + + // Assert + assert.strictEqual(response.status, RequestStartStopStatusEnumType.Accepted) + }) + + // FR: F01 — All EVSEs Inoperative, no evseId specified + await it('should reject remote start when all EVSEs are inoperative and no evseId specified', async () => { + // Arrange + for (const { evseStatus } of mockStation.iterateEvses(true)) { + evseStatus.availability = OCPP20OperationalStatusEnumType.Inoperative + } + + const request: OCPP20RequestStartTransactionRequest = { + idToken: { + idToken: 'VALID_TOKEN_123', + type: OCPP20IdTokenEnumType.ISO14443, + }, + remoteStartId: 501, + } + + // Act + const response = await testableService.handleRequestStartTransaction(mockStation, request) + + // Assert + assert.strictEqual(response.status, RequestStartStopStatusEnumType.Rejected) + }) + await describe('REQUEST_START_TRANSACTION event listener', async () => { let listenerService: OCPP20IncomingRequestService let requestHandlerMock: ReturnType -- 2.43.0