From 8fc0b49a0e46c7fd21a4db9cd347326d36547787 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Mon, 15 Jun 2026 18:38:50 +0200 Subject: [PATCH] test(ui-common): cover buildStatusNotificationPayload 1.6/2.0.x branches (#1834) (#1902) * test(ui-common): cover buildStatusNotificationPayload 1.6/2.0.x branches (#1834) Adds a describe('buildStatusNotificationPayload') block matching the existing payloadBuilders.test.ts style. Cases (9): OCPP 1.6 - explicit errorCode: payload carries {connectorId, status, errorCode} - errorCode omitted: payload omits errorCode (no NoError default) - evseId pass-through with errorCode - ocppVersion=undefined falls through to 1.6 shape - unsupported version (e.g. '3.0') throws via assertOCPP16OrUndefined OCPP 2.0.x - VERSION_201 + evseId: {connectorId, connectorStatus, evseId}, no status / errorCode fields - VERSION_20 without evseId: {connectorId, connectorStatus} - errorCode option ignored (1.6-only field) Cross-version - same input, different ocppVersion: shapes differ in the documented way (status vs connectorStatus, errorCode/evseId presence) Uses OCPP enum constants directly (OCPP16ChargePointStatus, OCPP16ChargePointErrorCode, OCPP20ConnectorStatusEnumType, OCPPVersion) per the OCPP enumeration-naming convention. Production code in payloadBuilders.ts is unchanged. * test(ui-common): exercise evseId-only path for OCPP 1.6 StatusNotification Address review comment on #1902: the prior test 'should pass evseId through for OCPP 1.6 when provided' passed both errorCode and evseId together, so the evseId-only OCPP 1.6 branch was never independently exercised. The errorCode-only path is already covered by the case 'should build OCPP 1.6 payload with status and explicit errorCode', and the combined case is trivial composition of the two via the spread syntax in the function body. Test renamed to 'should pass evseId through for OCPP 1.6 without errorCode' and now only passes options.evseId, asserting errorCode is absent from the resulting payload. --- ui/common/tests/payloadBuilders.test.ts | 138 ++++++++++++++++++++++++ 1 file changed, 138 insertions(+) diff --git a/ui/common/tests/payloadBuilders.test.ts b/ui/common/tests/payloadBuilders.test.ts index 1a36dfba..08237589 100644 --- a/ui/common/tests/payloadBuilders.test.ts +++ b/ui/common/tests/payloadBuilders.test.ts @@ -2,6 +2,9 @@ import assert from 'node:assert' import { describe, it } from 'node:test' import { + OCPP16ChargePointErrorCode, + OCPP16ChargePointStatus, + OCPP20ConnectorStatusEnumType, OCPP20IdTokenEnumType, OCPP20TransactionEventEnumType, OCPPVersion, @@ -11,6 +14,7 @@ import { buildAuthorizePayload, buildIdToken, buildStartTransactionPayload, + buildStatusNotificationPayload, buildStopTransactionPayload, isOCPP20x, } from '../src/utils/payloadBuilders.js' @@ -174,4 +178,138 @@ await describe('payloadBuilders', async () => { }) }) }) + + await describe('buildStatusNotificationPayload', async () => { + const connectorId = 1 + + await it('should build OCPP 1.6 payload with status and explicit errorCode', () => { + const result = buildStatusNotificationPayload( + connectorId, + OCPP16ChargePointStatus.AVAILABLE, + OCPPVersion.VERSION_16, + { errorCode: OCPP16ChargePointErrorCode.NO_ERROR } + ) + assert.deepStrictEqual(result, { + connectorId, + errorCode: OCPP16ChargePointErrorCode.NO_ERROR, + status: OCPP16ChargePointStatus.AVAILABLE, + }) + }) + + await it('should omit errorCode for OCPP 1.6 when not provided', () => { + const result = buildStatusNotificationPayload( + connectorId, + OCPP16ChargePointStatus.AVAILABLE, + OCPPVersion.VERSION_16 + ) + assert.deepStrictEqual(result, { + connectorId, + status: OCPP16ChargePointStatus.AVAILABLE, + }) + assert.strictEqual((result as Record).errorCode, undefined) + }) + + await it('should pass evseId through for OCPP 1.6 without errorCode', () => { + const result = buildStatusNotificationPayload( + connectorId, + OCPP16ChargePointStatus.CHARGING, + OCPPVersion.VERSION_16, + { evseId: 2 } + ) + assert.deepStrictEqual(result, { + connectorId, + evseId: 2, + status: OCPP16ChargePointStatus.CHARGING, + }) + assert.strictEqual((result as Record).errorCode, undefined) + }) + + await it('should default to OCPP 1.6 shape when ocppVersion is undefined', () => { + const result = buildStatusNotificationPayload( + connectorId, + OCPP16ChargePointStatus.AVAILABLE, + undefined + ) + assert.deepStrictEqual(result, { + connectorId, + status: OCPP16ChargePointStatus.AVAILABLE, + }) + }) + + await it('should throw for unsupported OCPP version', () => { + assert.throws( + () => + buildStatusNotificationPayload( + connectorId, + OCPP16ChargePointStatus.AVAILABLE, + '3.0' as unknown as OCPPVersion + ), + (error: Error) => error.message.includes('Unsupported OCPP version') + ) + }) + + await it('should build OCPP 2.0.1 payload with connectorStatus and evseId', () => { + const result = buildStatusNotificationPayload( + connectorId, + OCPP20ConnectorStatusEnumType.AVAILABLE, + OCPPVersion.VERSION_201, + { evseId: 1 } + ) + assert.deepStrictEqual(result, { + connectorId, + connectorStatus: OCPP20ConnectorStatusEnumType.AVAILABLE, + evseId: 1, + }) + assert.strictEqual((result as Record).status, undefined) + assert.strictEqual((result as Record).errorCode, undefined) + }) + + await it('should build OCPP 2.0 payload with connectorStatus when evseId omitted', () => { + const result = buildStatusNotificationPayload( + connectorId, + OCPP20ConnectorStatusEnumType.OCCUPIED, + OCPPVersion.VERSION_20 + ) + assert.deepStrictEqual(result, { + connectorId, + connectorStatus: OCPP20ConnectorStatusEnumType.OCCUPIED, + }) + }) + + await it('should ignore errorCode option for OCPP 2.0.x (1.6-only field)', () => { + const result = buildStatusNotificationPayload( + connectorId, + OCPP20ConnectorStatusEnumType.FAULTED, + OCPPVersion.VERSION_201, + { errorCode: OCPP16ChargePointErrorCode.GROUND_FAILURE, evseId: 3 } + ) + assert.deepStrictEqual(result, { + connectorId, + connectorStatus: OCPP20ConnectorStatusEnumType.FAULTED, + evseId: 3, + }) + assert.strictEqual((result as Record).errorCode, undefined) + }) + + await it('should dispatch shape on ocppVersion (1.6 vs 2.0.x) for the same input', () => { + const status16 = OCPP16ChargePointStatus.AVAILABLE + const status20 = OCPP20ConnectorStatusEnumType.AVAILABLE + const result16 = buildStatusNotificationPayload(connectorId, status16, OCPPVersion.VERSION_16) + const result20 = buildStatusNotificationPayload( + connectorId, + status20, + OCPPVersion.VERSION_201, + { evseId: 1 } + ) + const flat16 = result16 as Record + const flat20 = result20 as Record + assert.strictEqual(flat16.status, status16) + assert.strictEqual(flat16.connectorStatus, undefined) + assert.strictEqual(flat16.evseId, undefined) + assert.strictEqual(flat20.connectorStatus, status20) + assert.strictEqual(flat20.status, undefined) + assert.strictEqual(flat20.evseId, 1) + assert.notDeepStrictEqual(result16, result20) + }) + }) }) -- 2.53.0