import type {
OCPP20RequestStartTransactionRequest,
OCPP20RequestStopTransactionRequest,
+ OCPP20RequestStopTransactionResponse,
OCPP20TransactionEventRequest,
UUIDv4,
} from '../../../../src/types/index.js'
import { OCPP20IncomingRequestService } from '../../../../src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.js'
import { OCPPAuthServiceFactory } from '../../../../src/charging-station/ocpp/auth/services/OCPPAuthServiceFactory.js'
import {
+ OCPP20IncomingRequestCommand,
OCPP20RequestCommand,
OCPP20TransactionEventEnumType,
OCPP20TriggerReasonEnumType,
return startResponse.transactionId as string
}
+ /**
+ * Emits the REQUEST_STOP_TRANSACTION event on the service (mimicking
+ * the base class `handleIncomingRequest` post-response flow) and waits
+ * for any async listeners to settle.
+ * @param station - The charging station instance
+ * @param request - The stop transaction request payload
+ * @param response - The stop transaction response from the handler
+ */
+ async function emitStopEvent (
+ station: ChargingStation,
+ request: OCPP20RequestStopTransactionRequest,
+ response: OCPP20RequestStopTransactionResponse
+ ): Promise<void> {
+ incomingRequestService.emit(
+ OCPP20IncomingRequestCommand.REQUEST_STOP_TRANSACTION,
+ station,
+ request,
+ response
+ )
+ // Allow the async event listener (.catch) to complete
+ await new Promise(resolve => {
+ setTimeout(resolve, 50)
+ })
+ }
+
// FR: F03.FR.02, F03.FR.03, F03.FR.07, F03.FR.09
await it('should successfully stop an active transaction', async () => {
// Start a transaction first
transactionId: transactionId as UUIDv4,
}
- // Execute stop transaction
- const response = await testableService.handleRequestStopTransaction(mockStation, stopRequest)
+ // Execute stop transaction handler (validates and returns Accepted)
+ const response = testableService.handleRequestStopTransaction(mockStation, stopRequest)
// Verify response
assert.notStrictEqual(response, undefined)
assert.strictEqual(response.status, RequestStartStopStatusEnumType.Accepted)
- // Verify TransactionEvent was sent
+ // Emit event to trigger the listener (mimics base class post-response flow)
+ await emitStopEvent(mockStation, stopRequest, response)
+
+ // Verify TransactionEvent was sent by the event listener
assert.strictEqual(sentTransactionEvents.length, 1)
const transactionEvent = sentTransactionEvents[0]
transactionId: transactionId2 as UUIDv4,
}
- const response = await testableService.handleRequestStopTransaction(mockStation, stopRequest)
+ const response = testableService.handleRequestStopTransaction(mockStation, stopRequest)
// Verify response
assert.notStrictEqual(response, undefined)
assert.strictEqual(response.status, RequestStartStopStatusEnumType.Accepted)
+ // Emit event to trigger the listener
+ await emitStopEvent(mockStation, stopRequest, response)
+
// Verify correct TransactionEvent was sent
assert.strictEqual(sentTransactionEvents.length, 1)
const transactionEvent = sentTransactionEvents[0]
})
// FR: F03.FR.08
- await it('should reject stop transaction for non-existent transaction ID', async () => {
+ await it('should reject stop transaction for non-existent transaction ID', () => {
// Clear previous transaction events
sentTransactionEvents = []
transactionId: nonExistentTransactionId as UUIDv4,
}
- const response = await testableService.handleRequestStopTransaction(mockStation, stopRequest)
+ const response = testableService.handleRequestStopTransaction(mockStation, stopRequest)
// Verify rejection
assert.notStrictEqual(response, undefined)
})
// FR: F03.FR.08
- await it('should reject stop transaction for invalid transaction ID format - empty string', async () => {
+ await it('should reject stop transaction for invalid transaction ID format - empty string', () => {
// Clear previous transaction events
sentTransactionEvents = []
transactionId: '' as UUIDv4,
}
- const response = await testableService.handleRequestStopTransaction(mockStation, invalidRequest)
+ const response = testableService.handleRequestStopTransaction(mockStation, invalidRequest)
// Verify rejection
assert.notStrictEqual(response, undefined)
})
// FR: F03.FR.08
- await it('should reject stop transaction for invalid transaction ID format - too long', async () => {
+ await it('should reject stop transaction for invalid transaction ID format - too long', () => {
// Clear previous transaction events
sentTransactionEvents = []
transactionId: tooLongTransactionId as UUIDv4,
}
- const response = await testableService.handleRequestStopTransaction(mockStation, invalidRequest)
+ const response = testableService.handleRequestStopTransaction(mockStation, invalidRequest)
// Verify rejection
assert.notStrictEqual(response, undefined)
transactionId: testTransactionId as UUIDv4,
}
- const response = await testableService.handleRequestStopTransaction(mockStation, stopRequest)
+ const response = testableService.handleRequestStopTransaction(mockStation, stopRequest)
// Verify acceptance (format is valid)
assert.notStrictEqual(response, undefined)
assert.strictEqual(response.status, RequestStartStopStatusEnumType.Accepted)
+ // Emit event to trigger the listener
+ await emitStopEvent(mockStation, stopRequest, response)
+
// Verify TransactionEvent was sent
assert.strictEqual(sentTransactionEvents.length, 1)
})
transactionId: transactionId as UUIDv4,
}
- const response = await testableService.handleRequestStopTransaction(
+ const response = testableService.handleRequestStopTransaction(
failingChargingStation,
stopRequest
)
- // Should be rejected due to TransactionEvent failure
+ // With event listener pattern, handler returns Accepted (validation passed).
+ // The TransactionEvent failure is handled asynchronously by the listener's .catch().
assert.notStrictEqual(response, undefined)
- assert.strictEqual(response.status, RequestStartStopStatusEnumType.Rejected)
+ assert.strictEqual(response.status, RequestStartStopStatusEnumType.Accepted)
+
+ // Emit event — the listener will attempt requestStopTransaction which throws,
+ // but the error is caught and logged (no unhandled rejection).
+ await emitStopEvent(failingChargingStation, stopRequest, response)
})
// FR: F04.FR.01
transactionId: transactionId as UUIDv4,
}
- const response = await testableService.handleRequestStopTransaction(mockStation, stopRequest)
+ const response = testableService.handleRequestStopTransaction(mockStation, stopRequest)
// Verify response structure
assert.notStrictEqual(response, undefined)
transactionId: transactionId as UUIDv4,
}
- const response = await testableService.handleRequestStopTransaction(
+ const response = testableService.handleRequestStopTransaction(
mockStation,
stopRequestWithCustomData
)
assert.notStrictEqual(response, undefined)
assert.strictEqual(response.status, RequestStartStopStatusEnumType.Accepted)
+ // Emit event to trigger the listener
+ await emitStopEvent(mockStation, stopRequestWithCustomData, response)
+
// Verify TransactionEvent was sent despite custom data
assert.strictEqual(sentTransactionEvents.length, 1)
})
transactionId: transactionId as UUIDv4,
}
- const response = await testableService.handleRequestStopTransaction(mockStation, stopRequest)
+ const response = testableService.handleRequestStopTransaction(mockStation, stopRequest)
assert.strictEqual(response.status, RequestStartStopStatusEnumType.Accepted)
+ // Emit event to trigger the listener
+ await emitStopEvent(mockStation, stopRequest, response)
+
// Verify TransactionEvent structure and content
assert.strictEqual(sentTransactionEvents.length, 1)
const transactionEvent = sentTransactionEvents[0]
transactionId: transactionId as UUIDv4,
}
- const response = await testableService.handleRequestStopTransaction(mockStation, stopRequest)
+ const response = testableService.handleRequestStopTransaction(mockStation, stopRequest)
assert.strictEqual(response.status, RequestStartStopStatusEnumType.Accepted)
+ // Emit event to trigger the listener
+ await emitStopEvent(mockStation, stopRequest, response)
+
assert.strictEqual(sentTransactionEvents.length, 1)
const transactionEvent = sentTransactionEvents[0]