From 573523b323b4923efff81e16717700e64017c6e3 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Tue, 3 Mar 2026 20:49:46 +0100 Subject: [PATCH] test(ocpp): add OCPPServiceUtils connector status management tests Add tests for sendAndSetConnectorStatus and restoreConnectorStatus functions covering StatusNotification sending, connector status updates, event emission, and reservation-based status restoration. --- .../OCPPServiceUtils-connectorStatus.test.ts | 177 ++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 tests/charging-station/ocpp/OCPPServiceUtils-connectorStatus.test.ts diff --git a/tests/charging-station/ocpp/OCPPServiceUtils-connectorStatus.test.ts b/tests/charging-station/ocpp/OCPPServiceUtils-connectorStatus.test.ts new file mode 100644 index 00000000..7b1f840b --- /dev/null +++ b/tests/charging-station/ocpp/OCPPServiceUtils-connectorStatus.test.ts @@ -0,0 +1,177 @@ +/** + * @file Tests for OCPPServiceUtils connector status management + * @description Verifies sendAndSetConnectorStatus and restoreConnectorStatus functions + * + * Covers: + * - sendAndSetConnectorStatus — sends StatusNotification + updates connector + emits event + * - restoreConnectorStatus — restores Reserved or Available based on reservation state + */ + +import { expect } from '@std/expect' +import { afterEach, describe, it, mock } from 'node:test' + +import type { Reservation } from '../../../src/types/index.js' + +import { + restoreConnectorStatus, + sendAndSetConnectorStatus, +} from '../../../src/charging-station/ocpp/OCPPServiceUtils.js' +import { ConnectorStatusEnum, OCPPVersion } from '../../../src/types/index.js' +import { standardCleanup } from '../../helpers/TestLifecycleHelpers.js' +import { createMockChargingStation } from '../ChargingStationTestUtils.js' + +await describe('OCPPServiceUtils — connector status management', async () => { + afterEach(() => { + standardCleanup() + }) + + await describe('sendAndSetConnectorStatus', async () => { + await it('should send StatusNotification and update connector status', async () => { + const requestHandler = mock.fn(() => Promise.resolve({})) + const { station } = createMockChargingStation({ + ocppRequestService: { requestHandler }, + }) + + await sendAndSetConnectorStatus(station, 1, ConnectorStatusEnum.Occupied) + + expect(requestHandler.mock.calls.length).toBe(1) + expect(station.getConnectorStatus(1)?.status).toBe(ConnectorStatusEnum.Occupied) + }) + + await it('should return early when connector does not exist', async () => { + const requestHandler = mock.fn(() => Promise.resolve({})) + const { station } = createMockChargingStation({ + ocppRequestService: { requestHandler }, + }) + + await sendAndSetConnectorStatus(station, 99, ConnectorStatusEnum.Occupied) + + expect(requestHandler.mock.calls.length).toBe(0) + }) + + await it('should skip sending when options.send is false', async () => { + const requestHandler = mock.fn(() => Promise.resolve({})) + const { station } = createMockChargingStation({ + ocppRequestService: { requestHandler }, + }) + + await sendAndSetConnectorStatus(station, 1, ConnectorStatusEnum.Occupied, undefined, { + send: false, + }) + + expect(requestHandler.mock.calls.length).toBe(0) + expect(station.getConnectorStatus(1)?.status).toBe(ConnectorStatusEnum.Occupied) + }) + + await it('should update connector status even when send is true', async () => { + const requestHandler = mock.fn(() => Promise.resolve({})) + const { station } = createMockChargingStation({ + ocppRequestService: { requestHandler }, + }) + + expect(station.getConnectorStatus(1)?.status).toBe(ConnectorStatusEnum.Available) + + await sendAndSetConnectorStatus(station, 1, ConnectorStatusEnum.Unavailable) + + expect(station.getConnectorStatus(1)?.status).toBe(ConnectorStatusEnum.Unavailable) + }) + + await it('should call emitChargingStationEvent with connectorStatusChanged', async () => { + const requestHandler = mock.fn(() => Promise.resolve({})) + const emitMock = mock.fn() + const { station } = createMockChargingStation({ + ocppRequestService: { requestHandler }, + }) + + const stationWithEmit = station as unknown as { + emitChargingStationEvent: (...args: unknown[]) => void + } + stationWithEmit.emitChargingStationEvent = emitMock + + await sendAndSetConnectorStatus(station, 1, ConnectorStatusEnum.Occupied) + + expect(emitMock.mock.calls.length).toBe(1) + }) + + await it('should pass evseId to buildStatusNotificationRequest for OCPP 2.0', async () => { + const requestHandler = mock.fn(() => Promise.resolve({})) + const { station } = createMockChargingStation({ + ocppRequestService: { requestHandler }, + ocppVersion: OCPPVersion.VERSION_20, + }) + + await sendAndSetConnectorStatus(station, 1, ConnectorStatusEnum.Occupied, 1) + + expect(requestHandler.mock.calls.length).toBe(1) + expect(station.getConnectorStatus(1)?.status).toBe(ConnectorStatusEnum.Occupied) + }) + + await it('should default options.send to true when options not provided', async () => { + const requestHandler = mock.fn(() => Promise.resolve({})) + const { station } = createMockChargingStation({ + ocppRequestService: { requestHandler }, + }) + + await sendAndSetConnectorStatus(station, 1, ConnectorStatusEnum.Occupied) + + expect(requestHandler.mock.calls.length).toBe(1) + }) + }) + + await describe('restoreConnectorStatus', async () => { + await it('should restore to Reserved when connector has reservation and is not Reserved', async () => { + const requestHandler = mock.fn(() => Promise.resolve({})) + const { station } = createMockChargingStation({ + ocppRequestService: { requestHandler }, + }) + + const connector = station.getConnectorStatus(1) + if (connector != null) { + connector.reservation = { + connectorId: 1, + expiryDate: new Date().toISOString(), + idTag: 'TEST-TAG', + reservationId: 1, + } as unknown as Reservation + connector.status = ConnectorStatusEnum.Occupied + } + + await restoreConnectorStatus(station, 1, connector) + + expect(station.getConnectorStatus(1)?.status).toBe(ConnectorStatusEnum.Reserved) + }) + + await it('should restore to Available when connector has no reservation and is not Available', async () => { + const requestHandler = mock.fn(() => Promise.resolve({})) + const { station } = createMockChargingStation({ + ocppRequestService: { requestHandler }, + }) + + const connector = station.getConnectorStatus(1) + if (connector != null) { + connector.status = ConnectorStatusEnum.Occupied + } + + await restoreConnectorStatus(station, 1, connector) + + expect(station.getConnectorStatus(1)?.status).toBe(ConnectorStatusEnum.Available) + }) + + await it('should not change status when connector is already Available with no reservation', async () => { + const requestHandler = mock.fn(() => Promise.resolve({})) + const { station } = createMockChargingStation({ + ocppRequestService: { requestHandler }, + }) + + const connector = station.getConnectorStatus(1) + if (connector != null) { + connector.status = ConnectorStatusEnum.Available + } + + await restoreConnectorStatus(station, 1, connector) + + expect(requestHandler.mock.calls.length).toBe(0) + expect(station.getConnectorStatus(1)?.status).toBe(ConnectorStatusEnum.Available) + }) + }) +}) -- 2.43.0