From 9c2a803e444b5976dcc5d1fe34f1b4e19a6236d6 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Sat, 28 Feb 2026 16:48:00 +0100 Subject: [PATCH] fix(test-isolation): move mock instances to beforeEach in OCPP 2.0 tests Move module-level mock instances inside beforeEach blocks to ensure proper test isolation per TEST_STYLE_GUIDE.md requirements. - 11 IncomingRequestService test files fixed - 1 VariableManager test file fixed - 1 ServiceUtils-TransactionEvent test file fixed - 1 TestUtils file updated with JSDoc documentation Each test now gets fresh mock instances, eliminating potential cross-test state contamination. --- ...ngRequestService-CertificateSigned.test.ts | 44 ++++--- ...0IncomingRequestService-ClearCache.test.ts | 33 +++-- ...ngRequestService-DeleteCertificate.test.ts | 42 +++--- ...comingRequestService-GetBaseReport.test.ts | 42 +++--- ...Service-GetInstalledCertificateIds.test.ts | 49 ++++--- ...ncomingRequestService-GetVariables.test.ts | 30 +++-- ...gRequestService-InstallCertificate.test.ts | 43 ++++--- ...estService-RequestStartTransaction.test.ts | 37 +++--- ...uestService-RequestStopTransaction.test.ts | 61 ++++----- ...OCPP20IncomingRequestService-Reset.test.ts | 58 +++++---- ...ncomingRequestService-SetVariables.test.ts | 33 +++-- ...tils-TransactionEvent-IdTokenFirst.test.ts | 10 +- .../ocpp/2.0/OCPP20TestUtils.ts | 120 +++++++++++++----- .../ocpp/2.0/OCPP20VariableManager.test.ts | 27 ++-- 14 files changed, 374 insertions(+), 255 deletions(-) diff --git a/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-CertificateSigned.test.ts b/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-CertificateSigned.test.ts index 2227f8c5..00fb18df 100644 --- a/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-CertificateSigned.test.ts +++ b/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-CertificateSigned.test.ts @@ -4,7 +4,7 @@ */ import { expect } from '@std/expect' -import { afterEach, describe, it, mock, type Mock } from 'node:test' +import { afterEach, beforeEach, describe, it, mock, type Mock } from 'node:test' import type { ChargingStation } from '../../../../src/charging-station/index.js' import type { @@ -105,29 +105,31 @@ const createMockCertificateManager = ( }) await describe('I04 - CertificateSigned', async () => { + let mockChargingStation: TestableChargingStationWithCertificate + let incomingRequestService: OCPP20IncomingRequestService + let testableService: ReturnType + + beforeEach(() => { + mockChargingStation = createChargingStation({ + baseName: TEST_CHARGING_STATION_BASE_NAME, + connectorsCount: 3, + evseConfiguration: { evsesCount: 3 }, + heartbeatInterval: Constants.DEFAULT_HEARTBEAT_INTERVAL, + stationInfo: { + ocppStrictCompliance: false, + ocppVersion: OCPPVersion.VERSION_201, + }, + websocketPingInterval: Constants.DEFAULT_WEBSOCKET_PING_INTERVAL, + }) as TestableChargingStationWithCertificate + mockChargingStation.certificateManager = createMockCertificateManager() + mockChargingStation.closeWSConnection = mock.fn() + incomingRequestService = new OCPP20IncomingRequestService() + testableService = createTestableIncomingRequestService(incomingRequestService) + }) + afterEach(() => { mock.restoreAll() }) - - const mockChargingStation = createChargingStation({ - baseName: TEST_CHARGING_STATION_BASE_NAME, - connectorsCount: 3, - evseConfiguration: { evsesCount: 3 }, - heartbeatInterval: Constants.DEFAULT_HEARTBEAT_INTERVAL, - stationInfo: { - ocppStrictCompliance: false, - ocppVersion: OCPPVersion.VERSION_201, - }, - websocketPingInterval: Constants.DEFAULT_WEBSOCKET_PING_INTERVAL, - }) as TestableChargingStationWithCertificate - - mockChargingStation.certificateManager = createMockCertificateManager() - // Mock closeWSConnection for reconnect tests - mockChargingStation.closeWSConnection = mock.fn() - - const incomingRequestService = new OCPP20IncomingRequestService() - const testableService = createTestableIncomingRequestService(incomingRequestService) - await describe('Valid Certificate Chain Installation', async () => { await it('should accept valid certificate chain', async () => { mockChargingStation.certificateManager = createMockCertificateManager({ diff --git a/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-ClearCache.test.ts b/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-ClearCache.test.ts index b28fa9b4..a9e1a43e 100644 --- a/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-ClearCache.test.ts +++ b/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-ClearCache.test.ts @@ -4,7 +4,7 @@ */ import { expect } from '@std/expect' -import { afterEach, describe, it, mock } from 'node:test' +import { afterEach, beforeEach, describe, it, mock } from 'node:test' import { createTestableIncomingRequestService } from '../../../../src/charging-station/ocpp/2.0/__testable__/index.js' import { OCPP20IncomingRequestService } from '../../../../src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.js' @@ -19,21 +19,26 @@ await describe('C11 - Clear Authorization Data in Authorization Cache', async () mock.restoreAll() }) - const mockChargingStation = createChargingStation({ - baseName: TEST_CHARGING_STATION_BASE_NAME, - connectorsCount: 3, - evseConfiguration: { evsesCount: 3 }, - heartbeatInterval: Constants.DEFAULT_HEARTBEAT_INTERVAL, - stationInfo: { - ocppStrictCompliance: false, - ocppVersion: OCPPVersion.VERSION_201, - }, - websocketPingInterval: Constants.DEFAULT_WEBSOCKET_PING_INTERVAL, + let mockChargingStation: ReturnType + let incomingRequestService: OCPP20IncomingRequestService + let testableService: ReturnType + + beforeEach(() => { + mockChargingStation = createChargingStation({ + baseName: TEST_CHARGING_STATION_BASE_NAME, + connectorsCount: 3, + evseConfiguration: { evsesCount: 3 }, + heartbeatInterval: Constants.DEFAULT_HEARTBEAT_INTERVAL, + stationInfo: { + ocppStrictCompliance: false, + ocppVersion: OCPPVersion.VERSION_201, + }, + websocketPingInterval: Constants.DEFAULT_WEBSOCKET_PING_INTERVAL, + }) + incomingRequestService = new OCPP20IncomingRequestService() + testableService = createTestableIncomingRequestService(incomingRequestService) }) - const incomingRequestService = new OCPP20IncomingRequestService() - const testableService = createTestableIncomingRequestService(incomingRequestService) - // FR: C11.FR.01 - CS SHALL attempt to clear its Authorization Cache await it('should handle ClearCache request successfully', async () => { const response = await testableService.handleRequestClearCache(mockChargingStation) diff --git a/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-DeleteCertificate.test.ts b/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-DeleteCertificate.test.ts index 7408c7c8..6138c821 100644 --- a/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-DeleteCertificate.test.ts +++ b/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-DeleteCertificate.test.ts @@ -4,7 +4,7 @@ */ import { expect } from '@std/expect' -import { afterEach, describe, it, mock } from 'node:test' +import { afterEach, beforeEach, describe, it, mock } from 'node:test' import type { ChargingStationWithCertificateManager } from '../../../../src/charging-station/ocpp/2.0/OCPP20CertificateManager.js' @@ -58,25 +58,31 @@ await describe('I04 - DeleteCertificate', async () => { mock.restoreAll() }) - const mockChargingStation = createChargingStation({ - baseName: TEST_CHARGING_STATION_BASE_NAME, - connectorsCount: 3, - evseConfiguration: { evsesCount: 3 }, - heartbeatInterval: Constants.DEFAULT_HEARTBEAT_INTERVAL, - stationInfo: { - ocppStrictCompliance: false, - ocppVersion: OCPPVersion.VERSION_201, - }, - websocketPingInterval: Constants.DEFAULT_WEBSOCKET_PING_INTERVAL, - }) + let mockChargingStation: ReturnType + let stationWithCertManager: ChargingStationWithCertificateManager + let incomingRequestService: OCPP20IncomingRequestService + let testableService: ReturnType + + beforeEach(() => { + mockChargingStation = createChargingStation({ + baseName: TEST_CHARGING_STATION_BASE_NAME, + connectorsCount: 3, + evseConfiguration: { evsesCount: 3 }, + heartbeatInterval: Constants.DEFAULT_HEARTBEAT_INTERVAL, + stationInfo: { + ocppStrictCompliance: false, + ocppVersion: OCPPVersion.VERSION_201, + }, + websocketPingInterval: Constants.DEFAULT_WEBSOCKET_PING_INTERVAL, + }) - // Cast to allow setting certificateManager property - const stationWithCertManager = - mockChargingStation as unknown as ChargingStationWithCertificateManager - stationWithCertManager.certificateManager = createMockCertificateManager() + // Cast to allow setting certificateManager property + stationWithCertManager = mockChargingStation as unknown as ChargingStationWithCertificateManager + stationWithCertManager.certificateManager = createMockCertificateManager() - const incomingRequestService = new OCPP20IncomingRequestService() - const testableService = createTestableIncomingRequestService(incomingRequestService) + incomingRequestService = new OCPP20IncomingRequestService() + testableService = createTestableIncomingRequestService(incomingRequestService) + }) await describe('Valid Certificate Deletion', async () => { await it('should accept deletion of existing certificate', async () => { diff --git a/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-GetBaseReport.test.ts b/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-GetBaseReport.test.ts index 6084cf25..222ed7aa 100644 --- a/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-GetBaseReport.test.ts +++ b/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-GetBaseReport.test.ts @@ -1,5 +1,5 @@ import { expect } from '@std/expect' -import { afterEach, describe, it } from 'node:test' +import { afterEach, beforeEach, describe, it } from 'node:test' import { addConfigurationKey, @@ -39,25 +39,31 @@ import { } from '../../ChargingStationTestConstants.js' await describe('B07 - Get Base Report', async () => { - const mockChargingStation = createChargingStation({ - baseName: TEST_CHARGING_STATION_BASE_NAME, - connectorsCount: 3, - evseConfiguration: { evsesCount: 3 }, - heartbeatInterval: Constants.DEFAULT_HEARTBEAT_INTERVAL, - stationInfo: { - chargePointModel: TEST_CHARGE_POINT_MODEL, - chargePointSerialNumber: TEST_CHARGE_POINT_SERIAL_NUMBER, - chargePointVendor: TEST_CHARGE_POINT_VENDOR, - firmwareVersion: TEST_FIRMWARE_VERSION, - ocppStrictCompliance: false, - ocppVersion: OCPPVersion.VERSION_201, - }, - websocketPingInterval: Constants.DEFAULT_WEBSOCKET_PING_INTERVAL, - }) + let mockChargingStation: ReturnType + let incomingRequestService: OCPP20IncomingRequestService + let testableService: ReturnType + + beforeEach(() => { + mockChargingStation = createChargingStation({ + baseName: TEST_CHARGING_STATION_BASE_NAME, + connectorsCount: 3, + evseConfiguration: { evsesCount: 3 }, + heartbeatInterval: Constants.DEFAULT_HEARTBEAT_INTERVAL, + stationInfo: { + chargePointModel: TEST_CHARGE_POINT_MODEL, + chargePointSerialNumber: TEST_CHARGE_POINT_SERIAL_NUMBER, + chargePointVendor: TEST_CHARGE_POINT_VENDOR, + firmwareVersion: TEST_FIRMWARE_VERSION, + ocppStrictCompliance: false, + ocppVersion: OCPPVersion.VERSION_201, + }, + websocketPingInterval: Constants.DEFAULT_WEBSOCKET_PING_INTERVAL, + }) - const incomingRequestService = new OCPP20IncomingRequestService() + incomingRequestService = new OCPP20IncomingRequestService() - const testableService = createTestableIncomingRequestService(incomingRequestService) + testableService = createTestableIncomingRequestService(incomingRequestService) + }) // Reset singleton state after each test to ensure test isolation afterEach(() => { diff --git a/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-GetInstalledCertificateIds.test.ts b/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-GetInstalledCertificateIds.test.ts index 8eb24291..1eb1696e 100644 --- a/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-GetInstalledCertificateIds.test.ts +++ b/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-GetInstalledCertificateIds.test.ts @@ -4,7 +4,7 @@ */ import { expect } from '@std/expect' -import { afterEach, describe, it, mock } from 'node:test' +import { afterEach, beforeEach, describe, it, mock } from 'node:test' import type { ChargingStationWithCertificateManager } from '../../../../src/charging-station/ocpp/2.0/OCPP20CertificateManager.js' @@ -59,29 +59,36 @@ const createMockCertificateManager = ( }) await describe('I04 - GetInstalledCertificateIds', async () => { - afterEach(() => { - mock.restoreAll() - }) + let mockChargingStation: ReturnType + let stationWithCertManager: ChargingStationWithCertificateManager + let incomingRequestService: OCPP20IncomingRequestService + let testableService: ReturnType + + beforeEach(() => { + mockChargingStation = createChargingStation({ + baseName: TEST_CHARGING_STATION_BASE_NAME, + connectorsCount: 3, + evseConfiguration: { evsesCount: 3 }, + heartbeatInterval: Constants.DEFAULT_HEARTBEAT_INTERVAL, + stationInfo: { + ocppStrictCompliance: false, + ocppVersion: OCPPVersion.VERSION_201, + }, + websocketPingInterval: Constants.DEFAULT_WEBSOCKET_PING_INTERVAL, + }) - const mockChargingStation = createChargingStation({ - baseName: TEST_CHARGING_STATION_BASE_NAME, - connectorsCount: 3, - evseConfiguration: { evsesCount: 3 }, - heartbeatInterval: Constants.DEFAULT_HEARTBEAT_INTERVAL, - stationInfo: { - ocppStrictCompliance: false, - ocppVersion: OCPPVersion.VERSION_201, - }, - websocketPingInterval: Constants.DEFAULT_WEBSOCKET_PING_INTERVAL, - }) + // Cast to allow setting certificateManager property + stationWithCertManager = + mockChargingStation as unknown as ChargingStationWithCertificateManager + stationWithCertManager.certificateManager = createMockCertificateManager() - // Cast to allow setting certificateManager property - const stationWithCertManager = - mockChargingStation as unknown as ChargingStationWithCertificateManager - stationWithCertManager.certificateManager = createMockCertificateManager() + incomingRequestService = new OCPP20IncomingRequestService() + testableService = createTestableIncomingRequestService(incomingRequestService) + }) - const incomingRequestService = new OCPP20IncomingRequestService() - const testableService = createTestableIncomingRequestService(incomingRequestService) + afterEach(() => { + mock.restoreAll() + }) await describe('Request All Certificate Types', async () => { await it('should return all certificates when no filter is provided', async () => { diff --git a/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-GetVariables.test.ts b/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-GetVariables.test.ts index 65f363bb..196006f8 100644 --- a/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-GetVariables.test.ts +++ b/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-GetVariables.test.ts @@ -4,7 +4,7 @@ */ import { expect } from '@std/expect' import { millisecondsToSeconds } from 'date-fns' -import { afterEach, describe, it } from 'node:test' +import { afterEach, beforeEach, describe, it } from 'node:test' import { OCPP20IncomingRequestService } from '../../../../src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.js' import { OCPP20VariableManager } from '../../../../src/charging-station/ocpp/2.0/OCPP20VariableManager.js' @@ -35,20 +35,24 @@ import { } from './OCPP20TestUtils.js' await describe('B06 - Get Variables', async () => { - const mockChargingStation = createChargingStation({ - baseName: TEST_CHARGING_STATION_BASE_NAME, - connectorsCount: 3, - evseConfiguration: { evsesCount: 3 }, - heartbeatInterval: Constants.DEFAULT_HEARTBEAT_INTERVAL, - stationInfo: { - ocppStrictCompliance: false, - ocppVersion: OCPPVersion.VERSION_201, - }, - websocketPingInterval: Constants.DEFAULT_WEBSOCKET_PING_INTERVAL, + let mockChargingStation: ReturnType + let incomingRequestService: OCPP20IncomingRequestService + + beforeEach(() => { + mockChargingStation = createChargingStation({ + baseName: TEST_CHARGING_STATION_BASE_NAME, + connectorsCount: 3, + evseConfiguration: { evsesCount: 3 }, + heartbeatInterval: Constants.DEFAULT_HEARTBEAT_INTERVAL, + stationInfo: { + ocppStrictCompliance: false, + ocppVersion: OCPPVersion.VERSION_201, + }, + websocketPingInterval: Constants.DEFAULT_WEBSOCKET_PING_INTERVAL, + }) + incomingRequestService = new OCPP20IncomingRequestService() }) - const incomingRequestService = new OCPP20IncomingRequestService() - // Reset singleton state after each test to ensure test isolation afterEach(() => { OCPP20VariableManager.getInstance().resetRuntimeOverrides() diff --git a/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-InstallCertificate.test.ts b/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-InstallCertificate.test.ts index 6d47f906..eb0e47d2 100644 --- a/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-InstallCertificate.test.ts +++ b/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-InstallCertificate.test.ts @@ -4,7 +4,7 @@ */ import { expect } from '@std/expect' -import { afterEach, describe, it, mock } from 'node:test' +import { afterEach, beforeEach, describe, it, mock } from 'node:test' import type { ChargingStationWithCertificateManager } from '../../../../src/charging-station/ocpp/2.0/OCPP20CertificateManager.js' @@ -72,25 +72,32 @@ await describe('I03 - InstallCertificate', async () => { mock.restoreAll() }) - const mockChargingStation = createChargingStation({ - baseName: TEST_CHARGING_STATION_BASE_NAME, - connectorsCount: 3, - evseConfiguration: { evsesCount: 3 }, - heartbeatInterval: Constants.DEFAULT_HEARTBEAT_INTERVAL, - stationInfo: { - ocppStrictCompliance: false, - ocppVersion: OCPPVersion.VERSION_201, - }, - websocketPingInterval: Constants.DEFAULT_WEBSOCKET_PING_INTERVAL, - }) + let mockChargingStation: ReturnType + let stationWithCertManager: ChargingStationWithCertificateManager + let incomingRequestService: OCPP20IncomingRequestService + let testableService: ReturnType + + beforeEach(() => { + mockChargingStation = createChargingStation({ + baseName: TEST_CHARGING_STATION_BASE_NAME, + connectorsCount: 3, + evseConfiguration: { evsesCount: 3 }, + heartbeatInterval: Constants.DEFAULT_HEARTBEAT_INTERVAL, + stationInfo: { + ocppStrictCompliance: false, + ocppVersion: OCPPVersion.VERSION_201, + }, + websocketPingInterval: Constants.DEFAULT_WEBSOCKET_PING_INTERVAL, + }) - // Cast to allow setting certificateManager property - const stationWithCertManager = - mockChargingStation as unknown as ChargingStationWithCertificateManager - stationWithCertManager.certificateManager = createMockCertificateManager() + // Cast to allow setting certificateManager property + stationWithCertManager = + mockChargingStation as unknown as ChargingStationWithCertificateManager + stationWithCertManager.certificateManager = createMockCertificateManager() - const incomingRequestService = new OCPP20IncomingRequestService() - const testableService = createTestableIncomingRequestService(incomingRequestService) + incomingRequestService = new OCPP20IncomingRequestService() + testableService = createTestableIncomingRequestService(incomingRequestService) + }) await describe('Valid Certificate Installation', async () => { await it('should accept valid V2GRootCertificate', async () => { 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 245616eb..0998a1c9 100644 --- a/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-RequestStartTransaction.test.ts +++ b/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-RequestStartTransaction.test.ts @@ -31,25 +31,26 @@ import { } from './OCPP20TestUtils.js' await describe('F01 & F02 - Remote Start Transaction', async () => { - const mockChargingStation = createChargingStation({ - baseName: TEST_CHARGING_STATION_BASE_NAME, - connectorsCount: 3, - evseConfiguration: { evsesCount: 3 }, - heartbeatInterval: Constants.DEFAULT_HEARTBEAT_INTERVAL, - ocppRequestService: { - requestHandler: async () => Promise.resolve({}), - }, - stationInfo: { - ocppStrictCompliance: false, - ocppVersion: OCPPVersion.VERSION_201, - }, - websocketPingInterval: Constants.DEFAULT_WEBSOCKET_PING_INTERVAL, - }) - - const incomingRequestService = new OCPP20IncomingRequestService() - const testableService = createTestableIncomingRequestService(incomingRequestService) - + let mockChargingStation: ReturnType + let incomingRequestService: OCPP20IncomingRequestService + let testableService: ReturnType beforeEach(() => { + mockChargingStation = createChargingStation({ + baseName: TEST_CHARGING_STATION_BASE_NAME, + connectorsCount: 3, + evseConfiguration: { evsesCount: 3 }, + heartbeatInterval: Constants.DEFAULT_HEARTBEAT_INTERVAL, + ocppRequestService: { + requestHandler: async () => Promise.resolve({}), + }, + stationInfo: { + ocppStrictCompliance: false, + ocppVersion: OCPPVersion.VERSION_201, + }, + websocketPingInterval: Constants.DEFAULT_WEBSOCKET_PING_INTERVAL, + }) + incomingRequestService = new OCPP20IncomingRequestService() + testableService = createTestableIncomingRequestService(incomingRequestService) const stationId = mockChargingStation.stationInfo?.chargingStationId ?? 'unknown' OCPPAuthServiceFactory.setInstanceForTesting(stationId, createMockAuthService()) resetConnectorTransactionState(mockChargingStation) diff --git a/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-RequestStopTransaction.test.ts b/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-RequestStopTransaction.test.ts index 2418c35b..c0f1a355 100644 --- a/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-RequestStopTransaction.test.ts +++ b/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-RequestStopTransaction.test.ts @@ -35,47 +35,48 @@ import { resetLimits, resetReportingValueSize } from './OCPP20TestUtils.js' await describe('F03 - Remote Stop Transaction', async () => { let sentTransactionEvents: OCPP20TransactionEventRequest[] = [] + let mockChargingStation: ReturnType + let incomingRequestService: OCPP20IncomingRequestService + let testableService: ReturnType - const mockChargingStation = createChargingStation({ - baseName: TEST_CHARGING_STATION_BASE_NAME, - connectorsCount: 3, - evseConfiguration: { evsesCount: 3 }, - heartbeatInterval: Constants.DEFAULT_HEARTBEAT_INTERVAL, - ocppRequestService: { - requestHandler: async ( - _chargingStation: unknown, - commandName: unknown, - commandPayload: unknown - ) => { - if (commandName === OCPP20RequestCommand.TRANSACTION_EVENT) { - sentTransactionEvents.push(commandPayload as OCPP20TransactionEventRequest) + beforeEach(() => { + sentTransactionEvents = [] + mockChargingStation = createChargingStation({ + baseName: TEST_CHARGING_STATION_BASE_NAME, + connectorsCount: 3, + evseConfiguration: { evsesCount: 3 }, + heartbeatInterval: Constants.DEFAULT_HEARTBEAT_INTERVAL, + ocppRequestService: { + requestHandler: async ( + _chargingStation: unknown, + commandName: unknown, + commandPayload: unknown + ) => { + if (commandName === OCPP20RequestCommand.TRANSACTION_EVENT) { + sentTransactionEvents.push(commandPayload as OCPP20TransactionEventRequest) + return Promise.resolve({}) + } return Promise.resolve({}) - } - return Promise.resolve({}) + }, }, - }, - stationInfo: { - ocppStrictCompliance: false, - ocppVersion: OCPPVersion.VERSION_201, - }, - websocketPingInterval: Constants.DEFAULT_WEBSOCKET_PING_INTERVAL, - }) - - const incomingRequestService = new OCPP20IncomingRequestService() - const testableService = createTestableIncomingRequestService(incomingRequestService) - - beforeEach(() => { + stationInfo: { + ocppStrictCompliance: false, + ocppVersion: OCPPVersion.VERSION_201, + }, + websocketPingInterval: Constants.DEFAULT_WEBSOCKET_PING_INTERVAL, + }) + incomingRequestService = new OCPP20IncomingRequestService() + testableService = createTestableIncomingRequestService(incomingRequestService) const stationId = mockChargingStation.stationInfo?.chargingStationId ?? 'unknown' OCPPAuthServiceFactory.setInstanceForTesting(stationId, createMockAuthService()) + resetLimits(mockChargingStation) + resetReportingValueSize(mockChargingStation) }) afterEach(() => { OCPPAuthServiceFactory.clearAllInstances() }) - resetLimits(mockChargingStation) - resetReportingValueSize(mockChargingStation) - /** * Helper function to reset all connector transaction states */ diff --git a/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-Reset.test.ts b/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-Reset.test.ts index 56e4e053..edda5868 100644 --- a/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-Reset.test.ts +++ b/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-Reset.test.ts @@ -28,39 +28,47 @@ import { createChargingStation } from '../../../ChargingStationFactory.js' import { TEST_CHARGING_STATION_BASE_NAME } from '../../ChargingStationTestConstants.js' await describe('B11 & B12 - Reset', async () => { + let mockChargingStation: ReturnType + let mockStation: ReturnType & { + getNumberOfRunningTransactions: () => number + reset: () => Promise + } + let incomingRequestService: OCPP20IncomingRequestService + let testableService: ReturnType + beforeEach(() => { mock.timers.enable({ apis: ['setInterval', 'setTimeout', 'setImmediate'] }) + + mockChargingStation = createChargingStation({ + 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, + }) + + // Add missing method to mock using interface extension pattern + interface MockChargingStation extends ChargingStation { + getNumberOfRunningTransactions: () => number + reset: () => Promise + } + mockStation = mockChargingStation as MockChargingStation + mockStation.getNumberOfRunningTransactions = () => 0 + mockStation.reset = () => Promise.resolve() + + incomingRequestService = new OCPP20IncomingRequestService() + testableService = createTestableIncomingRequestService(incomingRequestService) }) afterEach(() => { mock.timers.reset() }) - const mockChargingStation = createChargingStation({ - 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, - }) - - // Add missing method to mock using interface extension pattern - interface MockChargingStation extends ChargingStation { - getNumberOfRunningTransactions: () => number - reset: () => Promise - } - const mockStation = mockChargingStation as MockChargingStation - mockStation.getNumberOfRunningTransactions = () => 0 - mockStation.reset = () => Promise.resolve() - - const incomingRequestService = new OCPP20IncomingRequestService() - const testableService = createTestableIncomingRequestService(incomingRequestService) - await describe('B11 - Reset - Without Ongoing Transaction', async () => { // FR: B11.FR.01 await it('should handle Reset request with Immediate type when no transactions', async () => { diff --git a/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-SetVariables.test.ts b/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-SetVariables.test.ts index 97e025c8..9444c174 100644 --- a/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-SetVariables.test.ts +++ b/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-SetVariables.test.ts @@ -5,7 +5,7 @@ import { expect } from '@std/expect' import { millisecondsToSeconds } from 'date-fns' -import { afterEach, describe, it } from 'node:test' +import { afterEach, beforeEach, describe, it } from 'node:test' import { createTestableIncomingRequestService } from '../../../../src/charging-station/ocpp/2.0/__testable__/index.js' import { OCPP20IncomingRequestService } from '../../../../src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.js' @@ -40,21 +40,26 @@ import { } from './OCPP20TestUtils.js' await describe('B05 - Set Variables', async () => { - const mockChargingStation = createChargingStation({ - baseName: TEST_CHARGING_STATION_BASE_NAME, - connectorsCount: 3, - evseConfiguration: { evsesCount: 3 }, - heartbeatInterval: Constants.DEFAULT_HEARTBEAT_INTERVAL, - stationInfo: { - ocppStrictCompliance: false, - ocppVersion: OCPPVersion.VERSION_201, - }, - websocketPingInterval: Constants.DEFAULT_WEBSOCKET_PING_INTERVAL, + let mockChargingStation: ReturnType + let incomingRequestService: OCPP20IncomingRequestService + let testableService: ReturnType + + beforeEach(() => { + mockChargingStation = createChargingStation({ + baseName: TEST_CHARGING_STATION_BASE_NAME, + connectorsCount: 3, + evseConfiguration: { evsesCount: 3 }, + heartbeatInterval: Constants.DEFAULT_HEARTBEAT_INTERVAL, + stationInfo: { + ocppStrictCompliance: false, + ocppVersion: OCPPVersion.VERSION_201, + }, + websocketPingInterval: Constants.DEFAULT_WEBSOCKET_PING_INTERVAL, + }) + incomingRequestService = new OCPP20IncomingRequestService() + testableService = createTestableIncomingRequestService(incomingRequestService) }) - const incomingRequestService = new OCPP20IncomingRequestService() - const testableService = createTestableIncomingRequestService(incomingRequestService) - // Reset singleton state after each test to ensure test isolation afterEach(() => { OCPP20VariableManager.getInstance().resetRuntimeOverrides() diff --git a/tests/charging-station/ocpp/2.0/OCPP20ServiceUtils-TransactionEvent-IdTokenFirst.test.ts b/tests/charging-station/ocpp/2.0/OCPP20ServiceUtils-TransactionEvent-IdTokenFirst.test.ts index 9830cc02..ef87b37a 100644 --- a/tests/charging-station/ocpp/2.0/OCPP20ServiceUtils-TransactionEvent-IdTokenFirst.test.ts +++ b/tests/charging-station/ocpp/2.0/OCPP20ServiceUtils-TransactionEvent-IdTokenFirst.test.ts @@ -3,7 +3,7 @@ * @description Unit tests for OCPP 2.0 IdToken-first pre-authorization flow (E03) */ import { expect } from '@std/expect' -import { afterEach, describe, it } from 'node:test' +import { afterEach, beforeEach, describe, it } from 'node:test' import { OCPP20ServiceUtils } from '../../../../src/charging-station/ocpp/2.0/OCPP20ServiceUtils.js' import { @@ -42,10 +42,12 @@ import { * - E02: Cable connection -> EV detection -> Authorization -> Charging */ await describe('E03 - IdToken-First Pre-Authorization Flow', async () => { - const mockChargingStation = createMockOCPP20TransactionTestStation() + let mockChargingStation: ReturnType - // Reset limits and state before tests - resetLimits(mockChargingStation) + beforeEach(() => { + mockChargingStation = createMockOCPP20TransactionTestStation() + resetLimits(mockChargingStation) + }) afterEach(() => { resetConnectorTransactionState(mockChargingStation) diff --git a/tests/charging-station/ocpp/2.0/OCPP20TestUtils.ts b/tests/charging-station/ocpp/2.0/OCPP20TestUtils.ts index 0451ebf4..d41584ba 100644 --- a/tests/charging-station/ocpp/2.0/OCPP20TestUtils.ts +++ b/tests/charging-station/ocpp/2.0/OCPP20TestUtils.ts @@ -348,8 +348,9 @@ function ensureConfig (chargingStation: ChargingStation): ConfigurationKey[] { */ export const IdTokenFixtures = { /** - * Central (server-side) token - * @param idToken + * Central (server-side) token. + * @param idToken - The ID token string. + * @returns An OCPP20IdTokenType with Central type. */ central: (idToken = 'CENTRAL_TOKEN_001'): OCPP20IdTokenType => ({ idToken, @@ -357,8 +358,9 @@ export const IdTokenFixtures = { }), /** - * eMAID contract identifier token - * @param idToken + * eMAID contract identifier token. + * @param idToken - The eMAID token string. + * @returns An OCPP20IdTokenType with eMAID type. */ emaid: (idToken = 'DE*ABC*E123456*1'): OCPP20IdTokenType => ({ idToken, @@ -366,8 +368,9 @@ export const IdTokenFixtures = { }), /** - * ISO14443 RFID token (most common type) - * @param idToken + * ISO14443 RFID token (most common type). + * @param idToken - The RFID token string. + * @returns An OCPP20IdTokenType with ISO14443 type. */ iso14443: (idToken = 'TEST_RFID_TOKEN_001'): OCPP20IdTokenType => ({ idToken, @@ -375,15 +378,19 @@ export const IdTokenFixtures = { }), /** - * ISO15693 RFID token - * @param idToken + * ISO15693 RFID token. + * @param idToken - The ISO15693 token string. + * @returns An OCPP20IdTokenType with ISO15693 type. */ iso15693: (idToken = 'TEST_ISO15693_001'): OCPP20IdTokenType => ({ idToken, type: OCPP20IdTokenEnumType.ISO15693, }), - /** NoAuthorization token (free charging) */ + /** + * NoAuthorization token (free charging). + * @returns An OCPP20IdTokenType with NoAuthorization type. + */ noAuth: (): OCPP20IdTokenType => ({ idToken: '', type: OCPP20IdTokenEnumType.NoAuthorization, @@ -398,21 +405,28 @@ export const TransactionContextFixtures = { // ===== Local Authorization Contexts ===== /** - * Abnormal condition (with optional condition type) - * @param condition + * Abnormal condition (with optional condition type). + * @param condition - The abnormal condition type. + * @returns An OCPP20TransactionContext for abnormal conditions. */ abnormalCondition: (condition = 'OverCurrent'): OCPP20TransactionContext => ({ abnormalCondition: condition, source: 'abnormal_condition', }), - /** Cable plugged in (E02 cable-first start) */ + /** + * Cable plugged in (E02 cable-first start). + * @returns An OCPP20TransactionContext for cable plugged in. + */ cablePluggedIn: (): OCPP20TransactionContext => ({ cableState: 'plugged_in', source: 'cable_action', }), - /** Deauthorization (token revoked or invalid) */ + /** + * Deauthorization (token revoked or invalid). + * @returns An OCPP20TransactionContext for deauthorization. + */ deauthorized: (): OCPP20TransactionContext => ({ authorizationMethod: 'idToken', isDeauthorized: true, @@ -421,18 +435,27 @@ export const TransactionContextFixtures = { // ===== Cable Action Contexts (E02 flow) ===== - /** Energy limit reached */ + /** + * Energy limit reached. + * @returns An OCPP20TransactionContext for energy limit reached. + */ energyLimitReached: (): OCPP20TransactionContext => ({ source: 'energy_limit', }), - /** EV communication lost */ + /** + * EV communication lost. + * @returns An OCPP20TransactionContext for EV communication lost. + */ evCommunicationLost: (): OCPP20TransactionContext => ({ source: 'system_event', systemEvent: 'ev_communication_lost', }), - /** EV connect timeout */ + /** + * EV connect timeout. + * @returns An OCPP20TransactionContext for EV connect timeout. + */ evConnectTimeout: (): OCPP20TransactionContext => ({ source: 'system_event', systemEvent: 'ev_connect_timeout', @@ -440,21 +463,28 @@ export const TransactionContextFixtures = { // ===== Remote Command Contexts ===== - /** Cable unplugged / EV departed */ + /** + * Cable unplugged / EV departed. + * @returns An OCPP20TransactionContext for EV departure. + */ evDeparted: (): OCPP20TransactionContext => ({ cableState: 'unplugged', source: 'cable_action', }), - /** EV detected after cable connection */ + /** + * EV detected after cable connection. + * @returns An OCPP20TransactionContext for EV detection. + */ evDetected: (): OCPP20TransactionContext => ({ cableState: 'detected', source: 'cable_action', }), /** - * IdToken-first authorization (E03 flow start) - * @param authorizationMethod + * IdToken-first authorization (E03 flow start). + * @param authorizationMethod - The authorization method used. + * @returns An OCPP20TransactionContext for IdToken authorization. */ idTokenAuthorized: ( authorizationMethod: 'groupIdToken' | 'idToken' = 'idToken' @@ -463,13 +493,19 @@ export const TransactionContextFixtures = { source: 'local_authorization', }), - /** Clock-aligned meter value */ + /** + * Clock-aligned meter value. + * @returns An OCPP20TransactionContext for clock-aligned meter values. + */ meterValueClock: (): OCPP20TransactionContext => ({ isPeriodicMeterValue: false, source: 'meter_value', }), - /** Periodic meter value (sampled interval) */ + /** + * Periodic meter value (sampled interval). + * @returns An OCPP20TransactionContext for periodic meter values. + */ meterValuePeriodic: (): OCPP20TransactionContext => ({ isPeriodicMeterValue: true, source: 'meter_value', @@ -477,19 +513,28 @@ export const TransactionContextFixtures = { // ===== Meter Value Contexts ===== - /** Remote start transaction request */ + /** + * Remote start transaction request. + * @returns An OCPP20TransactionContext for remote start. + */ remoteStart: (): OCPP20TransactionContext => ({ command: 'RequestStartTransaction', source: 'remote_command', }), - /** Remote stop transaction request */ + /** + * Remote stop transaction request. + * @returns An OCPP20TransactionContext for remote stop. + */ remoteStop: (): OCPP20TransactionContext => ({ command: 'RequestStopTransaction', source: 'remote_command', }), - /** Reset command */ + /** + * Reset command. + * @returns An OCPP20TransactionContext for reset. + */ reset: (): OCPP20TransactionContext => ({ command: 'Reset', source: 'remote_command', @@ -497,13 +542,19 @@ export const TransactionContextFixtures = { // ===== System Event Contexts ===== - /** Signed data received */ + /** + * Signed data received. + * @returns An OCPP20TransactionContext for signed data. + */ signedData: (): OCPP20TransactionContext => ({ isSignedDataReceived: true, source: 'meter_value', }), - /** Stop authorized by local token presentation */ + /** + * Stop authorized by local token presentation. + * @returns An OCPP20TransactionContext for stop authorization. + */ stopAuthorized: (): OCPP20TransactionContext => ({ authorizationMethod: 'stopAuthorized', source: 'local_authorization', @@ -511,12 +562,18 @@ export const TransactionContextFixtures = { // ===== Limit Contexts ===== - /** Time limit reached */ + /** + * Time limit reached. + * @returns An OCPP20TransactionContext for time limit. + */ timeLimitReached: (): OCPP20TransactionContext => ({ source: 'time_limit', }), - /** Trigger message command */ + /** + * Trigger message command. + * @returns An OCPP20TransactionContext for trigger message. + */ triggerMessage: (): OCPP20TransactionContext => ({ command: 'TriggerMessage', source: 'remote_command', @@ -524,7 +581,10 @@ export const TransactionContextFixtures = { // ===== Abnormal Condition Contexts ===== - /** Unlock connector command */ + /** + * Unlock connector command. + * @returns An OCPP20TransactionContext for unlock connector. + */ unlockConnector: (): OCPP20TransactionContext => ({ command: 'UnlockConnector', source: 'remote_command', diff --git a/tests/charging-station/ocpp/2.0/OCPP20VariableManager.test.ts b/tests/charging-station/ocpp/2.0/OCPP20VariableManager.test.ts index ade54289..95648da0 100644 --- a/tests/charging-station/ocpp/2.0/OCPP20VariableManager.test.ts +++ b/tests/charging-station/ocpp/2.0/OCPP20VariableManager.test.ts @@ -5,7 +5,7 @@ import { expect } from '@std/expect' import { millisecondsToSeconds } from 'date-fns' -import { afterEach, describe, it } from 'node:test' +import { afterEach, beforeEach, describe, it } from 'node:test' import { deleteConfigurationKey, @@ -63,16 +63,21 @@ function buildWsExampleUrl (targetLength: number, fillerChar = 'a'): string { } await describe('B05/B06 - OCPP20VariableManager test suite', async () => { - // Create mock ChargingStation with EVSEs for OCPP 2.0 testing - const mockChargingStation = createChargingStation({ - baseName: TEST_CHARGING_STATION_BASE_NAME, - connectorsCount: 3, - evseConfiguration: { evsesCount: 3 }, - heartbeatInterval: Constants.DEFAULT_HEARTBEAT_INTERVAL, - stationInfo: { - ocppVersion: OCPPVersion.VERSION_201, - }, - websocketPingInterval: Constants.DEFAULT_WEBSOCKET_PING_INTERVAL, + // Type declaration for mock ChargingStation + let mockChargingStation: ReturnType + + // Initialize mock ChargingStation before each test + beforeEach(() => { + mockChargingStation = createChargingStation({ + baseName: TEST_CHARGING_STATION_BASE_NAME, + connectorsCount: 3, + evseConfiguration: { evsesCount: 3 }, + heartbeatInterval: Constants.DEFAULT_HEARTBEAT_INTERVAL, + stationInfo: { + ocppVersion: OCPPVersion.VERSION_201, + }, + websocketPingInterval: Constants.DEFAULT_WEBSOCKET_PING_INTERVAL, + }) }) // Reset singleton state after each test to ensure test isolation -- 2.43.0