From: Jérôme Benoit Date: Fri, 27 Mar 2026 14:30:15 +0000 (+0100) Subject: refactor: type buildTransactionEvent with OCPP20TransactionEventOptions X-Git-Tag: ocpp-server@v4.0.0~40 X-Git-Url: https://git.piment-noir.org/?a=commitdiff_plain;h=b6de95e291e521391ed4f362c8526362f34e5e47;p=e-mobility-charging-stations-simulator.git refactor: type buildTransactionEvent with OCPP20TransactionEventOptions Replace the untyped Record cast in buildTransactionEvent with the proper OCPP20TransactionEventOptions parameter type. Add build hint fields to the options interface. Remove 64 stale as-unknown-as casts from transaction event tests. --- diff --git a/src/charging-station/ocpp/2.0/OCPP20RequestService.ts b/src/charging-station/ocpp/2.0/OCPP20RequestService.ts index fad5b547..0e5b8261 100644 --- a/src/charging-station/ocpp/2.0/OCPP20RequestService.ts +++ b/src/charging-station/ocpp/2.0/OCPP20RequestService.ts @@ -12,7 +12,7 @@ import { OCPP20RequestCommand, type OCPP20SignCertificateRequest, type OCPP20StatusNotificationRequest, - type OCPP20TransactionEventRequest, + type OCPP20TransactionEventOptions, OCPPVersion, type RequestParams, } from '../../../types/index.js' @@ -205,7 +205,7 @@ export class OCPP20RequestService extends OCPPRequestService { case OCPP20RequestCommand.TRANSACTION_EVENT: return buildTransactionEvent( chargingStation, - commandParams as unknown as OCPP20TransactionEventRequest + commandParams as unknown as OCPP20TransactionEventOptions ) as unknown as Request default: { // OCPPError usage here is debatable: it's an error in the OCPP stack but not targeted to sendError(). diff --git a/src/charging-station/ocpp/2.0/OCPP20ServiceUtils.ts b/src/charging-station/ocpp/2.0/OCPP20ServiceUtils.ts index 9070127c..f35a29e1 100644 --- a/src/charging-station/ocpp/2.0/OCPP20ServiceUtils.ts +++ b/src/charging-station/ocpp/2.0/OCPP20ServiceUtils.ts @@ -525,7 +525,7 @@ export class OCPP20ServiceUtils extends OCPPServiceUtils { triggerReason, ...options, offline: true, - } as unknown as OCPP20TransactionEventRequest) + }) logger.info( `${chargingStation.logPrefix()} ${moduleName}.sendTransactionEvent: Station offline, queueing TransactionEvent with seqNo=${transactionEventRequest.seqNo.toString()}` ) @@ -879,29 +879,28 @@ export class OCPP20ServiceUtils extends OCPPServiceUtils { */ export function buildTransactionEvent ( chargingStation: ChargingStation, - commandParams: OCPP20TransactionEventRequest + commandParams: OCPP20TransactionEventOptions ): OCPP20TransactionEventRequest { - const params = commandParams as Record - const eventType = params.eventType as OCPP20TransactionEventEnumType - const triggerReason = - params.triggerReason != null - ? (params.triggerReason as OCPP20TriggerReasonEnumType) - : eventType === OCPP20TransactionEventEnumType.Ended - ? OCPP20TriggerReasonEnumType.RemoteStop - : OCPP20TriggerReasonEnumType.Authorized - const inputEvse = params.evse as undefined | { connectorId?: number; id?: number } - const connectorId = - params.connectorId != null - ? (params.connectorId as number) - : (inputEvse?.connectorId ?? inputEvse?.id ?? 1) + const eventType = commandParams.eventType + if (eventType == null) { + throw new OCPPError( + ErrorType.PROPERTY_CONSTRAINT_VIOLATION, + 'eventType is required for TransactionEvent' + ) + } + const defaultTriggerReason = + eventType === OCPP20TransactionEventEnumType.Ended + ? OCPP20TriggerReasonEnumType.RemoteStop + : OCPP20TriggerReasonEnumType.Authorized + const triggerReason = commandParams.triggerReason ?? defaultTriggerReason + const inputEvse = commandParams.evse + const connectorId = commandParams.connectorId ?? inputEvse?.connectorId ?? inputEvse?.id ?? 1 const transactionId = - params.transactionId != null - ? (params.transactionId as string) - : eventType === OCPP20TransactionEventEnumType.Ended - ? (chargingStation.getConnectorStatus(connectorId)?.transactionId?.toString() ?? - generateUUID()) - : generateUUID() - const options = params as unknown as OCPP20TransactionEventOptions + commandParams.transactionId ?? + (eventType === OCPP20TransactionEventEnumType.Ended + ? (chargingStation.getConnectorStatus(connectorId)?.transactionId?.toString() ?? + generateUUID()) + : generateUUID()) if (!validateIdentifierString(transactionId, 36)) { const errorMsg = `Invalid transaction ID format (must be non-empty string ≤36 characters): ${transactionId}` @@ -909,7 +908,7 @@ export function buildTransactionEvent ( throw new OCPPError(ErrorType.PROPERTY_CONSTRAINT_VIOLATION, errorMsg) } - const evseId = options.evseId ?? chargingStation.getEvseIdByConnectorId(connectorId) + const evseId = commandParams.evseId ?? chargingStation.getEvseIdByConnectorId(connectorId) if (evseId == null) { const errorMsg = `Cannot find EVSE ID for connector ${connectorId.toString()}` logger.error(`${chargingStation.logPrefix()} ${moduleName}.buildTransactionEvent: ${errorMsg}`) @@ -943,14 +942,14 @@ export function buildTransactionEvent ( transactionId: transactionId as UUIDv4, } - if (options.chargingState !== undefined) { - transactionInfo.chargingState = options.chargingState + if (commandParams.chargingState !== undefined) { + transactionInfo.chargingState = commandParams.chargingState } - if (options.stoppedReason !== undefined) { - transactionInfo.stoppedReason = options.stoppedReason + if (commandParams.stoppedReason !== undefined) { + transactionInfo.stoppedReason = commandParams.stoppedReason } - if (options.remoteStartId !== undefined) { - transactionInfo.remoteStartId = options.remoteStartId + if (commandParams.remoteStartId !== undefined) { + transactionInfo.remoteStartId = commandParams.remoteStartId } const transactionEventRequest: OCPP20TransactionEventRequest = { @@ -966,27 +965,27 @@ export function buildTransactionEvent ( } // E03.FR.01: Include idToken only once per transaction - if (options.idToken !== undefined && connectorStatus.transactionIdTokenSent !== true) { - transactionEventRequest.idToken = options.idToken + if (commandParams.idToken !== undefined && connectorStatus.transactionIdTokenSent !== true) { + transactionEventRequest.idToken = commandParams.idToken connectorStatus.transactionIdTokenSent = true } - if (options.meterValue !== undefined && options.meterValue.length > 0) { - transactionEventRequest.meterValue = options.meterValue + if (commandParams.meterValue !== undefined && commandParams.meterValue.length > 0) { + transactionEventRequest.meterValue = commandParams.meterValue } - if (options.cableMaxCurrent !== undefined) { - transactionEventRequest.cableMaxCurrent = options.cableMaxCurrent + if (commandParams.cableMaxCurrent !== undefined) { + transactionEventRequest.cableMaxCurrent = commandParams.cableMaxCurrent } - if (options.numberOfPhasesUsed !== undefined) { - transactionEventRequest.numberOfPhasesUsed = options.numberOfPhasesUsed + if (commandParams.numberOfPhasesUsed !== undefined) { + transactionEventRequest.numberOfPhasesUsed = commandParams.numberOfPhasesUsed } - if (options.offline !== undefined) { - transactionEventRequest.offline = options.offline + if (commandParams.offline !== undefined) { + transactionEventRequest.offline = commandParams.offline } - if (options.reservationId !== undefined) { - transactionEventRequest.reservationId = options.reservationId + if (commandParams.reservationId !== undefined) { + transactionEventRequest.reservationId = commandParams.reservationId } - if (options.customData !== undefined) { - transactionEventRequest.customData = options.customData + if (commandParams.customData !== undefined) { + transactionEventRequest.customData = commandParams.customData } logger.debug( diff --git a/src/types/ocpp/2.0/Transaction.ts b/src/types/ocpp/2.0/Transaction.ts index 415efe47..b4b62825 100644 --- a/src/types/ocpp/2.0/Transaction.ts +++ b/src/types/ocpp/2.0/Transaction.ts @@ -266,33 +266,28 @@ export interface OCPP20MessageContentType extends JsonObject { format: OCPP20MessageFormatEnumType language?: string } + /** * Optional parameters for building and sending TransactionEvent requests. * Aligned with OCPP 2.0.1 TransactionEvent.req optional fields. */ export interface OCPP20TransactionEventOptions { - /** Maximum current the cable can handle (A) */ cableMaxCurrent?: number - /** Current charging state per OCPP 2.0.1 ChargingStateEnumType */ chargingState?: OCPP20ChargingStateEnumType - /** Vendor-specific custom data */ + connectorId?: number customData?: CustomDataType - /** EVSE identifier (1-based) */ + eventType?: OCPP20TransactionEventEnumType + evse?: Partial evseId?: number - /** Token used for authorization */ idToken?: OCPP20IdTokenType - /** Meter values associated with this event */ meterValue?: OCPP20MeterValue[] - /** Number of phases used for charging */ numberOfPhasesUsed?: number - /** Whether event occurred while offline */ offline?: boolean - /** Remote start transaction ID */ remoteStartId?: number - /** Reservation ID if applicable */ reservationId?: number - /** Reason for stopping transaction */ stoppedReason?: OCPP20ReasonEnumType + transactionId?: string + triggerReason?: OCPP20TriggerReasonEnumType } export interface OCPP20TransactionEventRequest extends JsonObject { diff --git a/tests/charging-station/ocpp/2.0/OCPP20ServiceUtils-TransactionEvent.test.ts b/tests/charging-station/ocpp/2.0/OCPP20ServiceUtils-TransactionEvent.test.ts index a7634f72..26dfae65 100644 --- a/tests/charging-station/ocpp/2.0/OCPP20ServiceUtils-TransactionEvent.test.ts +++ b/tests/charging-station/ocpp/2.0/OCPP20ServiceUtils-TransactionEvent.test.ts @@ -38,7 +38,6 @@ import { OCPP20RequestCommand, OCPP20RequiredVariableName, OCPP20TransactionEventEnumType, - type OCPP20TransactionEventRequest, type OCPP20TransactionType, OCPP20TriggerReasonEnumType, OCPPVersion, @@ -128,7 +127,7 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { eventType: OCPP20TransactionEventEnumType.Started, transactionId, triggerReason, - } as unknown as OCPP20TransactionEventRequest) + }) // Validate required fields assert.strictEqual(transactionEvent.eventType, OCPP20TransactionEventEnumType.Started) @@ -162,7 +161,7 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { eventType: OCPP20TransactionEventEnumType.Started, transactionId, triggerReason: OCPP20TriggerReasonEnumType.Authorized, - } as unknown as OCPP20TransactionEventRequest) + }) // Build second event (Updated) const updateEvent = buildTransactionEvent(mockStation, { @@ -170,7 +169,7 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { eventType: OCPP20TransactionEventEnumType.Updated, transactionId, triggerReason: OCPP20TriggerReasonEnumType.MeterValuePeriodic, - } as unknown as OCPP20TransactionEventRequest) + }) // Build third event (Ended) const endEvent = buildTransactionEvent(mockStation, { @@ -179,7 +178,7 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { stoppedReason: OCPP20ReasonEnumType.Local, transactionId, triggerReason: OCPP20TriggerReasonEnumType.StopAuthorized, - } as unknown as OCPP20TransactionEventRequest) + }) // Validate sequence number progression: 0 → 1 → 2 assert.strictEqual(startEvent.seqNo, 0) @@ -220,7 +219,7 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { reservationId: options.reservationId, transactionId, triggerReason: OCPP20TriggerReasonEnumType.ChargingStateChanged, - } as unknown as OCPP20TransactionEventRequest) + }) // Validate optional fields are included if (transactionEvent.idToken == null) { @@ -250,7 +249,7 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { eventType: OCPP20TransactionEventEnumType.Started, transactionId: invalidTransactionId, triggerReason: OCPP20TriggerReasonEnumType.Authorized, - } as unknown as OCPP20TransactionEventRequest) + }) throw new Error('Should have thrown error for invalid identifier string') } catch (error) { assert.ok((error as Error).message.includes('Invalid transaction ID format')) @@ -296,7 +295,7 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { eventType: OCPP20TransactionEventEnumType.Updated, transactionId, triggerReason, - } as unknown as OCPP20TransactionEventRequest) + }) assert.strictEqual(transactionEvent.triggerReason, triggerReason) assert.strictEqual(transactionEvent.eventType, OCPP20TransactionEventEnumType.Updated) @@ -371,7 +370,7 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { eventType: OCPP20TransactionEventEnumType.Started, transactionId: generateUUID(), triggerReason: OCPP20TriggerReasonEnumType.Authorized, - } as unknown as OCPP20TransactionEventRequest) + }) // Verify sequence number is set const connectorStatus = mockStation.getConnectorStatus(connectorId) @@ -411,7 +410,7 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { }, transactionId, triggerReason: OCPP20TriggerReasonEnumType.Authorized, - } as unknown as OCPP20TransactionEventRequest) + }) // Validate all required fields exist const requiredFields = [ @@ -465,7 +464,7 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { eventType: OCPP20TransactionEventEnumType.Started, transactionId, triggerReason: OCPP20TriggerReasonEnumType.Authorized, - } as unknown as OCPP20TransactionEventRequest) + }) // For this test setup, EVSE ID should match connector ID if (transactionEvent.evse == null) { @@ -547,7 +546,7 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { eventType: OCPP20TransactionEventEnumType.Started, transactionId, triggerReason: OCPP20TriggerReasonEnumType.Authorized, - } as unknown as OCPP20TransactionEventRequest) + }) assert.strictEqual(oldEvent.eventType, OCPP20TransactionEventEnumType.Started) assert.strictEqual(oldEvent.triggerReason, OCPP20TriggerReasonEnumType.Authorized) @@ -625,7 +624,7 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { transactionId, triggerReason: expectedStartTrigger, ...(idToken != null ? { idToken } : {}), - } as unknown as OCPP20TransactionEventRequest) + }) assert.strictEqual(startedEvent.eventType, OCPP20TransactionEventEnumType.Started) assert.strictEqual(startedEvent.triggerReason, expectedStartTrigger) @@ -657,7 +656,7 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { transactionId, triggerReason: expectedStartTrigger, ...(idToken != null ? { idToken } : {}), - } as unknown as OCPP20TransactionEventRequest) + }) // Step 2: Charging state change const chargingEvent = buildTransactionEvent(mockStation, { @@ -666,7 +665,7 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { eventType: OCPP20TransactionEventEnumType.Updated, transactionId, triggerReason: OCPP20TriggerReasonEnumType.ChargingStateChanged, - } as unknown as OCPP20TransactionEventRequest) + }) // Step 3: Ended event const endedEvent = buildTransactionEvent(mockStation, { @@ -674,7 +673,7 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { eventType: OCPP20TransactionEventEnumType.Ended, transactionId, triggerReason: OCPP20TriggerReasonEnumType.StopAuthorized, - } as unknown as OCPP20TransactionEventRequest) + }) // Validate event sequence assert.strictEqual(startedEvent.seqNo, 0) @@ -702,7 +701,7 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { eventType: OCPP20TransactionEventEnumType.Started, transactionId: transaction1Id, triggerReason: expectedStartTrigger, - } as unknown as OCPP20TransactionEventRequest) + }) // Start transaction on connector 2 const conn2Event1 = buildTransactionEvent(mockStation, { @@ -710,7 +709,7 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { eventType: OCPP20TransactionEventEnumType.Started, transactionId: transaction2Id, triggerReason: expectedStartTrigger, - } as unknown as OCPP20TransactionEventRequest) + }) // Update connector 1 const conn1Event2 = buildTransactionEvent(mockStation, { @@ -718,7 +717,7 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { eventType: OCPP20TransactionEventEnumType.Updated, transactionId: transaction1Id, triggerReason: OCPP20TriggerReasonEnumType.ChargingStateChanged, - } as unknown as OCPP20TransactionEventRequest) + }) // Update connector 2 const conn2Event2 = buildTransactionEvent(mockStation, { @@ -726,7 +725,7 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { eventType: OCPP20TransactionEventEnumType.Updated, transactionId: transaction2Id, triggerReason: OCPP20TriggerReasonEnumType.ChargingStateChanged, - } as unknown as OCPP20TransactionEventRequest) + }) // Verify independent sequence numbers assert.strictEqual(conn1Event1.seqNo, 0) @@ -762,7 +761,7 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { eventType: OCPP20TransactionEventEnumType.Started, transactionId, triggerReason: OCPP20TriggerReasonEnumType.CablePluggedIn, - } as unknown as OCPP20TransactionEventRequest) + }) // Step 2: EV detected (Updated) const evDetectedEvent = buildTransactionEvent(mockStation, { @@ -770,7 +769,7 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { eventType: OCPP20TransactionEventEnumType.Updated, transactionId, triggerReason: OCPP20TriggerReasonEnumType.EVDetected, - } as unknown as OCPP20TransactionEventRequest) + }) // Step 3: Charging starts (Updated with ChargingStateChanged) const chargingStartedEvent = buildTransactionEvent(mockStation, { @@ -779,7 +778,7 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { eventType: OCPP20TransactionEventEnumType.Updated, transactionId, triggerReason: OCPP20TriggerReasonEnumType.ChargingStateChanged, - } as unknown as OCPP20TransactionEventRequest) + }) // Assert sequence numbers follow correct order assert.strictEqual(cablePluggedEvent.seqNo, 0) @@ -809,7 +808,7 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { eventType: OCPP20TransactionEventEnumType.Started, transactionId, triggerReason: OCPP20TriggerReasonEnumType.CablePluggedIn, - } as unknown as OCPP20TransactionEventRequest) + }) // End transaction with EV departure (cable removal) const endEvent = buildTransactionEvent(mockStation, { @@ -817,7 +816,7 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { eventType: OCPP20TransactionEventEnumType.Ended, transactionId, triggerReason: OCPP20TriggerReasonEnumType.EVDeparted, - } as unknown as OCPP20TransactionEventRequest) + }) // Assert proper sequencing for cable-initiated start and end assert.strictEqual(startEvent.seqNo, 0) @@ -842,26 +841,26 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { eventType: OCPP20TransactionEventEnumType.Started, transactionId, triggerReason: OCPP20TriggerReasonEnumType.CablePluggedIn, - } as unknown as OCPP20TransactionEventRequest), + }), buildTransactionEvent(mockStation, { connectorId, eventType: OCPP20TransactionEventEnumType.Updated, transactionId, triggerReason: OCPP20TriggerReasonEnumType.EVDetected, - } as unknown as OCPP20TransactionEventRequest), + }), buildTransactionEvent(mockStation, { connectorId, eventType: OCPP20TransactionEventEnumType.Updated, transactionId, triggerReason: OCPP20TriggerReasonEnumType.Authorized, - } as unknown as OCPP20TransactionEventRequest), + }), buildTransactionEvent(mockStation, { chargingState: OCPP20ChargingStateEnumType.Charging, connectorId, eventType: OCPP20TransactionEventEnumType.Updated, transactionId, triggerReason: OCPP20TriggerReasonEnumType.ChargingStateChanged, - } as unknown as OCPP20TransactionEventRequest), + }), ] // Assert EVDetected comes after CablePluggedIn and before authorization @@ -957,7 +956,7 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { eventType: OCPP20TransactionEventEnumType.Started, transactionId, triggerReason: OCPP20TriggerReasonEnumType.CablePluggedIn, - } as unknown as OCPP20TransactionEventRequest), + }), // 2. Start charging buildTransactionEvent(mockStation, { chargingState: OCPP20ChargingStateEnumType.Charging, @@ -965,7 +964,7 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { eventType: OCPP20TransactionEventEnumType.Updated, transactionId, triggerReason: OCPP20TriggerReasonEnumType.ChargingStateChanged, - } as unknown as OCPP20TransactionEventRequest), + }), // 3. Suspended by EV buildTransactionEvent(mockStation, { chargingState: OCPP20ChargingStateEnumType.SuspendedEV, @@ -973,7 +972,7 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { eventType: OCPP20TransactionEventEnumType.Updated, transactionId, triggerReason: OCPP20TriggerReasonEnumType.ChargingStateChanged, - } as unknown as OCPP20TransactionEventRequest), + }), // 4. Resume charging buildTransactionEvent(mockStation, { chargingState: OCPP20ChargingStateEnumType.Charging, @@ -981,14 +980,14 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { eventType: OCPP20TransactionEventEnumType.Updated, transactionId, triggerReason: OCPP20TriggerReasonEnumType.ChargingStateChanged, - } as unknown as OCPP20TransactionEventRequest), + }), // 5. EV departed buildTransactionEvent(mockStation, { connectorId, eventType: OCPP20TransactionEventEnumType.Ended, transactionId, triggerReason: OCPP20TriggerReasonEnumType.EVDeparted, - } as unknown as OCPP20TransactionEventRequest), + }), ] // Verify sequence numbers are continuous through suspend/resume @@ -1021,7 +1020,7 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { idToken, transactionId, triggerReason: OCPP20TriggerReasonEnumType.Authorized, - } as unknown as OCPP20TransactionEventRequest) + }) if (startedEvent.idToken == null) { assert.fail('Expected idToken to be defined') @@ -1049,7 +1048,7 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { idToken, transactionId, triggerReason: OCPP20TriggerReasonEnumType.Authorized, - } as unknown as OCPP20TransactionEventRequest) + }) // Second event should NOT include idToken (flag is set after first inclusion) const updatedEvent = buildTransactionEvent(mockStation, { @@ -1059,7 +1058,7 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { idToken, transactionId, triggerReason: OCPP20TriggerReasonEnumType.ChargingStateChanged, - } as unknown as OCPP20TransactionEventRequest) + }) assert.notStrictEqual(startedEvent.idToken, undefined) assert.strictEqual(updatedEvent.idToken, undefined) @@ -1083,7 +1082,7 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { idToken: rfidToken, transactionId, triggerReason: OCPP20TriggerReasonEnumType.Authorized, - } as unknown as OCPP20TransactionEventRequest) + }) assert.strictEqual(rfidEvent.idToken?.type, OCPP20IdTokenEnumType.ISO14443) @@ -1106,7 +1105,7 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { idToken: emaidToken, transactionId: generateUUID(), triggerReason: OCPP20TriggerReasonEnumType.Authorized, - } as unknown as OCPP20TransactionEventRequest) + }) assert.strictEqual(emaidEvent.idToken?.type, OCPP20IdTokenEnumType.eMAID) assert.strictEqual(emaidEvent.idToken.idToken, 'DE*ABC*E123456*1') @@ -1131,7 +1130,7 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { idToken, transactionId, triggerReason: OCPP20TriggerReasonEnumType.Authorized, - } as unknown as OCPP20TransactionEventRequest) + }) // E03 Step 2: Cable connected (Updated event) const cableConnectedEvent = buildTransactionEvent(mockStation, { @@ -1139,7 +1138,7 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { eventType: OCPP20TransactionEventEnumType.Updated, transactionId, triggerReason: OCPP20TriggerReasonEnumType.CablePluggedIn, - } as unknown as OCPP20TransactionEventRequest) + }) // E03 Step 3: Charging starts const chargingEvent = buildTransactionEvent(mockStation, { @@ -1148,7 +1147,7 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { eventType: OCPP20TransactionEventEnumType.Updated, transactionId, triggerReason: OCPP20TriggerReasonEnumType.ChargingStateChanged, - } as unknown as OCPP20TransactionEventRequest) + }) // E03 Step 4: Transaction ends const endedEvent = buildTransactionEvent(mockStation, { @@ -1156,7 +1155,7 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { eventType: OCPP20TransactionEventEnumType.Ended, transactionId, triggerReason: OCPP20TriggerReasonEnumType.StopAuthorized, - } as unknown as OCPP20TransactionEventRequest) + }) // Validate event sequence assert.strictEqual(authorizedEvent.eventType, OCPP20TransactionEventEnumType.Started) @@ -1207,7 +1206,7 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { idToken, transactionId: e03TransactionId, triggerReason: OCPP20TriggerReasonEnumType.Authorized, - } as unknown as OCPP20TransactionEventRequest) + }) // E02 Cable-First: Starts with CablePluggedIn trigger OCPP20ServiceUtils.resetTransactionSequenceNumber(mockStation, connectorId) @@ -1220,7 +1219,7 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { eventType: OCPP20TransactionEventEnumType.Started, transactionId: e02TransactionId, triggerReason: OCPP20TriggerReasonEnumType.CablePluggedIn, - } as unknown as OCPP20TransactionEventRequest) + }) // Key difference: E03 starts with Authorized, E02 starts with CablePluggedIn assert.strictEqual(e03Start.triggerReason, OCPP20TriggerReasonEnumType.Authorized) @@ -1250,7 +1249,7 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { idToken, transactionId, triggerReason: OCPP20TriggerReasonEnumType.Authorized, - } as unknown as OCPP20TransactionEventRequest) + }) // E03.FR.06: Cable not connected within timeout - transaction ends with Timeout const timeoutEvent = buildTransactionEvent(mockStation, { @@ -1258,7 +1257,7 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { eventType: OCPP20TransactionEventEnumType.Ended, transactionId, triggerReason: OCPP20TriggerReasonEnumType.EVConnectTimeout, - } as unknown as OCPP20TransactionEventRequest) + }) assert.strictEqual(authorizedEvent.eventType, OCPP20TransactionEventEnumType.Started) assert.strictEqual(authorizedEvent.triggerReason, OCPP20TriggerReasonEnumType.Authorized) @@ -1296,7 +1295,7 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { idToken, transactionId, triggerReason: OCPP20TriggerReasonEnumType.Authorized, - } as unknown as OCPP20TransactionEventRequest) + }) // Transaction ended due to deauthorization (e.g., token revoked mid-session) const revokedEvent = buildTransactionEvent(mockStation, { @@ -1304,7 +1303,7 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { eventType: OCPP20TransactionEventEnumType.Ended, transactionId, triggerReason: OCPP20TriggerReasonEnumType.Deauthorized, - } as unknown as OCPP20TransactionEventRequest) + }) assert.strictEqual(startEvent.eventType, OCPP20TransactionEventEnumType.Started) assert.strictEqual(revokedEvent.eventType, OCPP20TransactionEventEnumType.Ended) @@ -1330,31 +1329,31 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { idToken, transactionId, triggerReason: OCPP20TriggerReasonEnumType.Authorized, - } as unknown as OCPP20TransactionEventRequest), + }), buildTransactionEvent(mockStation, { connectorId, eventType: OCPP20TransactionEventEnumType.Updated, transactionId, triggerReason: OCPP20TriggerReasonEnumType.CablePluggedIn, - } as unknown as OCPP20TransactionEventRequest), + }), buildTransactionEvent(mockStation, { connectorId, eventType: OCPP20TransactionEventEnumType.Updated, transactionId, triggerReason: OCPP20TriggerReasonEnumType.ChargingStateChanged, - } as unknown as OCPP20TransactionEventRequest), + }), buildTransactionEvent(mockStation, { connectorId, eventType: OCPP20TransactionEventEnumType.Updated, transactionId, triggerReason: OCPP20TriggerReasonEnumType.MeterValuePeriodic, - } as unknown as OCPP20TransactionEventRequest), + }), buildTransactionEvent(mockStation, { connectorId, eventType: OCPP20TransactionEventEnumType.Ended, transactionId, triggerReason: OCPP20TriggerReasonEnumType.StopAuthorized, - } as unknown as OCPP20TransactionEventRequest), + }), ] // E03.FR.07: Sequence numbers must be continuous @@ -1379,7 +1378,7 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { eventType: OCPP20TransactionEventEnumType.Started, transactionId: transaction1Id, triggerReason: OCPP20TriggerReasonEnumType.Authorized, - } as unknown as OCPP20TransactionEventRequest) + }) OCPP20ServiceUtils.resetTransactionSequenceNumber(mockStation, connectorId) @@ -1388,7 +1387,7 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { eventType: OCPP20TransactionEventEnumType.Started, transactionId: transaction2Id, triggerReason: OCPP20TriggerReasonEnumType.Authorized, - } as unknown as OCPP20TransactionEventRequest) + }) assert.strictEqual(event1.transactionInfo.transactionId, transaction1Id) assert.strictEqual(event2.transactionInfo.transactionId, transaction2Id) @@ -2103,7 +2102,7 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { eventType: OCPP20TransactionEventEnumType.Started, transactionId, triggerReason: OCPP20TriggerReasonEnumType.Authorized, - } as unknown as OCPP20TransactionEventRequest) + }) assert.strictEqual(startEvent.seqNo, 0) // Send multiple periodic events (simulating timer ticks) @@ -2113,7 +2112,7 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { eventType: OCPP20TransactionEventEnumType.Updated, transactionId, triggerReason: OCPP20TriggerReasonEnumType.MeterValuePeriodic, - } as unknown as OCPP20TransactionEventRequest) + }) assert.strictEqual(periodicEvent.seqNo, i) } @@ -2196,7 +2195,7 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { eventType: OCPP20TransactionEventEnumType.Started, transactionId, triggerReason: OCPP20TriggerReasonEnumType.Authorized, - } as unknown as OCPP20TransactionEventRequest) + }) assert.strictEqual(startEvent.seqNo, 0) // 2. Multiple periodic updates (seqNo: 1, 2, 3) @@ -2206,7 +2205,7 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { eventType: OCPP20TransactionEventEnumType.Updated, transactionId, triggerReason: OCPP20TriggerReasonEnumType.MeterValuePeriodic, - } as unknown as OCPP20TransactionEventRequest) + }) assert.strictEqual(updateEvent.seqNo, i) } @@ -2216,7 +2215,7 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { eventType: OCPP20TransactionEventEnumType.Ended, transactionId, triggerReason: OCPP20TriggerReasonEnumType.StopAuthorized, - } as unknown as OCPP20TransactionEventRequest) + }) assert.strictEqual(endEvent.seqNo, 4) }) @@ -2234,13 +2233,13 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { eventType: OCPP20TransactionEventEnumType.Started, transactionId: transactionId1, triggerReason: OCPP20TriggerReasonEnumType.Authorized, - } as unknown as OCPP20TransactionEventRequest) + }) const event1Update = buildTransactionEvent(mockStation, { connectorId: 1, eventType: OCPP20TransactionEventEnumType.Updated, transactionId: transactionId1, triggerReason: OCPP20TriggerReasonEnumType.MeterValuePeriodic, - } as unknown as OCPP20TransactionEventRequest) + }) // Build events for connector 2 const event2Start = buildTransactionEvent(mockStation, { @@ -2248,13 +2247,13 @@ await describe('OCPP20 TransactionEvent ServiceUtils', async () => { eventType: OCPP20TransactionEventEnumType.Started, transactionId: transactionId2, triggerReason: OCPP20TriggerReasonEnumType.Authorized, - } as unknown as OCPP20TransactionEventRequest) + }) const event2Update = buildTransactionEvent(mockStation, { connectorId: 2, eventType: OCPP20TransactionEventEnumType.Updated, transactionId: transactionId2, triggerReason: OCPP20TriggerReasonEnumType.MeterValuePeriodic, - } as unknown as OCPP20TransactionEventRequest) + }) // Verify independent sequence numbers assert.strictEqual(event1Start.seqNo, 0)