From e93c24d98af80465d1a30ed64c47d3423052c60e Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Wed, 22 Oct 2025 13:20:06 +0200 Subject: [PATCH] test: factor out mock charging station instance creation code MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Jérôme Benoit --- tests/ChargingStationFactory.ts | 113 ++++++++++++++++++ tests/charging-station/Helpers.test.ts | 17 +-- ...ncomingRequestService-GetVariables.test.ts | 30 +---- .../ocpp/2.0/OCPP20VariableManager.test.ts | 36 ++---- tests/utils/ErrorUtils.test.ts | 7 +- 5 files changed, 133 insertions(+), 70 deletions(-) create mode 100644 tests/ChargingStationFactory.ts diff --git a/tests/ChargingStationFactory.ts b/tests/ChargingStationFactory.ts new file mode 100644 index 00000000..553ea72c --- /dev/null +++ b/tests/ChargingStationFactory.ts @@ -0,0 +1,113 @@ +import type { ChargingStation } from '../src/charging-station/index.js' +import type { + ChargingStationConfiguration, + ChargingStationInfo, + ChargingStationTemplate, +} from '../src/types/index.js' + +import { + OCPP20ConnectorStatusEnumType, + OCPP20OptionalVariableName, + OCPPVersion, +} from '../src/types/index.js' + +/** + * Options to customize the construction of a ChargingStation test instance + */ +export interface ChargingStationOptions { + baseName?: string + hasEvses?: boolean + heartbeatInterval?: number + ocppConfiguration?: ChargingStationConfiguration + started?: boolean + starting?: boolean + stationInfo?: Partial + websocketPingInterval?: number +} + +/** + * Creates a ChargingStation instance for tests + * @param options - Options to customize the ChargingStation instance + * @returns A mock ChargingStation instance + */ +export function createChargingStation (options: ChargingStationOptions = {}): ChargingStation { + const baseName = options.baseName ?? 'CS-TEST' + const templateIndex = 1 + const heartbeatInterval = options.heartbeatInterval ?? 60 + const websocketPingInterval = options.websocketPingInterval ?? 30 + + return { + connectors: new Map(), + evses: new Map(), + getHeartbeatInterval: () => heartbeatInterval, + hasEvses: options.hasEvses ?? false, + logPrefix: () => `${baseName} |`, + ocppConfiguration: options.ocppConfiguration ?? { + configurationKey: [ + { + key: OCPP20OptionalVariableName.WebSocketPingInterval, + value: websocketPingInterval.toString(), + }, + { key: OCPP20OptionalVariableName.HeartbeatInterval, value: heartbeatInterval.toString() }, + ], + }, + started: options.started ?? false, + starting: options.starting, + stationInfo: { + baseName, + chargingStationId: `${baseName}-00001`, + hashId: 'test-hash-id', + maximumAmperage: 16, + maximumPower: 12000, + templateIndex, + templateName: 'test-template.json', + ...options.stationInfo, + }, + wsConnection: { + pingInterval: websocketPingInterval, + }, + } as unknown as ChargingStation +} + +/** + * Creates a ChargingStation template for tests + * @param baseName - Base name for the template + * @returns A ChargingStationTemplate instance + */ +export function createChargingStationTemplate (baseName = 'CS-TEST'): ChargingStationTemplate { + return { + baseName, + } as ChargingStationTemplate +} + +/** + * Creates a ChargingStation instance with connectors and EVSEs configured for OCPP 2.0 + * @param options - Options to customize the ChargingStation instance + * @returns A mock ChargingStation instance with EVSEs + */ +export function createChargingStationWithEvses ( + options: ChargingStationOptions = {} +): ChargingStation { + const chargingStation = createChargingStation({ + hasEvses: true, + stationInfo: { + ocppVersion: OCPPVersion.VERSION_201, + ...options.stationInfo, + }, + ...options, + }) + + // Add default connectors and EVSEs + Object.assign(chargingStation, { + connectors: new Map([ + [1, { status: OCPP20ConnectorStatusEnumType.Available }], + [2, { status: OCPP20ConnectorStatusEnumType.Available }], + ]), + evses: new Map([ + [1, { connectors: new Map([[1, {}]]) }], + [2, { connectors: new Map([[1, {}]]) }], + ]), + }) + + return chargingStation +} diff --git a/tests/charging-station/Helpers.test.ts b/tests/charging-station/Helpers.test.ts index 71a1a009..4300d5a6 100644 --- a/tests/charging-station/Helpers.test.ts +++ b/tests/charging-station/Helpers.test.ts @@ -3,8 +3,6 @@ import { expect } from '@std/expect' import { describe, it } from 'node:test' -import type { ChargingStation } from '../../src/charging-station/index.js' - import { checkChargingStationState, checkConfiguration, @@ -23,22 +21,15 @@ import { type ChargingStationTemplate, type ConnectorStatus, ConnectorStatusEnum, - type EvseStatus, OCPPVersion, } from '../../src/types/index.js' import { logger } from '../../src/utils/Logger.js' +import { createChargingStation, createChargingStationTemplate } from '../ChargingStationFactory.js' await describe('Helpers test suite', async () => { const baseName = 'CS-TEST' - const chargingStationTemplate = { - baseName, - } as ChargingStationTemplate - const chargingStation = { - connectors: new Map(), - evses: new Map(), - logPrefix: () => `${baseName} |`, - started: false, - } as ChargingStation + const chargingStationTemplate = createChargingStationTemplate(baseName) + const chargingStation = createChargingStation({ baseName }) await it('Verify getChargingStationId()', () => { expect(getChargingStationId(1, chargingStationTemplate)).toBe(`${baseName}-00001`) @@ -51,6 +42,8 @@ await describe('Helpers test suite', async () => { }) await it('Verify validateStationInfo()', () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + delete (chargingStation as any).stationInfo expect(() => { validateStationInfo(chargingStation) }).toThrow(new BaseError('Missing charging station information')) 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 fc7a9d9b..72a78f02 100644 --- a/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-GetVariables.test.ts +++ b/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-GetVariables.test.ts @@ -3,44 +3,26 @@ import { expect } from '@std/expect' import { describe, it } from 'node:test' -import type { ChargingStation } from '../../../../src/charging-station/index.js' - import { OCPP20IncomingRequestService } from '../../../../src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.js' import { AttributeEnumType, GetVariableStatusEnumType, OCPP20ComponentName, - OCPP20ConnectorStatusEnumType, type OCPP20GetVariablesRequest, OCPP20OptionalVariableName, OCPP20RequiredVariableName, } from '../../../../src/types/index.js' +import { createChargingStationWithEvses } from '../../../ChargingStationFactory.js' await describe('OCPP20IncomingRequestService GetVariables integration tests', async () => { - // Mock ChargingStation with comprehensive properties - const mockChargingStation = { - connectors: new Map([ - [1, { status: OCPP20ConnectorStatusEnumType.Available }], - [2, { status: OCPP20ConnectorStatusEnumType.Available }], - ]), - evses: new Map([ - [1, { connectors: new Map([[1, {}]]) }], - [2, { connectors: new Map([[1, {}]]) }], - ]), - getHeartbeatInterval: () => 60, - hasEvses: true, - logPrefix: () => 'CS-TEST-001', - ocppConfiguration: { - configurationKey: [ - { key: OCPP20OptionalVariableName.WebSocketPingInterval, value: '30' }, - { key: OCPP20OptionalVariableName.HeartbeatInterval, value: '60' }, - ], - }, + const mockChargingStation = createChargingStationWithEvses({ + baseName: 'CS-TEST-001', + heartbeatInterval: 60, stationInfo: { - heartbeatInterval: 60, ocppStrictCompliance: false, }, - } as unknown as ChargingStation + websocketPingInterval: 30, + }) const incomingRequestService = new OCPP20IncomingRequestService() diff --git a/tests/charging-station/ocpp/2.0/OCPP20VariableManager.test.ts b/tests/charging-station/ocpp/2.0/OCPP20VariableManager.test.ts index b0838922..46324a47 100644 --- a/tests/charging-station/ocpp/2.0/OCPP20VariableManager.test.ts +++ b/tests/charging-station/ocpp/2.0/OCPP20VariableManager.test.ts @@ -3,48 +3,26 @@ import { expect } from '@std/expect' import { describe, it } from 'node:test' -import type { ChargingStation } from '../../../../src/charging-station/index.js' - import { OCPP20VariableManager } from '../../../../src/charging-station/ocpp/2.0/OCPP20VariableManager.js' import { AttributeEnumType, type ComponentType, GetVariableStatusEnumType, OCPP20ComponentName, - OCPP20ConnectorStatusEnumType, type OCPP20GetVariableDataType, OCPP20OptionalVariableName, OCPP20RequiredVariableName, type VariableType, } from '../../../../src/types/index.js' +import { createChargingStationWithEvses } from '../../../ChargingStationFactory.js' await describe('OCPP20VariableManager test suite', async () => { - // Mock ChargingStation with basic properties - const mockChargingStation = { - connectors: new Map([ - [1, { status: OCPP20ConnectorStatusEnumType.Available }], - [2, { status: OCPP20ConnectorStatusEnumType.Available }], - ]), - evses: new Map([ - [1, { connectors: new Map([[1, {}]]) }], - [2, { connectors: new Map([[1, {}]]) }], - ]), - getHeartbeatInterval: () => 60, - hasEvses: true, - logPrefix: () => 'CS-TEST-001', - ocppConfiguration: { - configurationKey: [ - { key: OCPP20OptionalVariableName.WebSocketPingInterval, value: '30' }, - { key: OCPP20OptionalVariableName.HeartbeatInterval, value: '60' }, - ], - }, - stationInfo: { - heartbeatInterval: 60, - }, - wsConnection: { - pingInterval: 30, - }, - } as unknown as ChargingStation + // Create mock ChargingStation with EVSEs for OCPP 2.0 testing + const mockChargingStation = createChargingStationWithEvses({ + baseName: 'CS-TEST-001', + heartbeatInterval: 60, + websocketPingInterval: 30, + }) await it('Verify that OCPP20VariableManager can be instantiated as singleton', () => { const manager1 = OCPP20VariableManager.getInstance() diff --git a/tests/utils/ErrorUtils.test.ts b/tests/utils/ErrorUtils.test.ts index 77ebcc09..9cbeff5b 100644 --- a/tests/utils/ErrorUtils.test.ts +++ b/tests/utils/ErrorUtils.test.ts @@ -3,8 +3,6 @@ import { expect } from '@std/expect' import { describe, it } from 'node:test' -import type { ChargingStation } from '../../src/charging-station/index.js' - import { FileType, GenericStatus, @@ -18,11 +16,10 @@ import { handleSendMessageError, } from '../../src/utils/ErrorUtils.js' import { logger } from '../../src/utils/Logger.js' +import { createChargingStation } from '../ChargingStationFactory.js' await describe('ErrorUtils test suite', async () => { - const chargingStation = { - logPrefix: () => 'CS-TEST |', - } as ChargingStation + const chargingStation = createChargingStation({ baseName: 'CS-TEST' }) await it('Verify handleFileException()', t => { t.mock.method(console, 'warn') -- 2.43.0