]> Piment Noir Git Repositories - e-mobility-charging-stations-simulator.git/commitdiff
test(ui-common): cover buildStatusNotificationPayload 1.6/2.0.x branches (#1834)...
authorJérôme Benoit <jerome.benoit@piment-noir.org>
Mon, 15 Jun 2026 16:38:50 +0000 (18:38 +0200)
committerGitHub <noreply@github.com>
Mon, 15 Jun 2026 16:38:50 +0000 (18:38 +0200)
* 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

index 1a36dfba4365373c6d4456e31d40328221c5042e..08237589f56b3a4ab37bedb34f9a2c59fcc330ae 100644 (file)
@@ -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<string, unknown>).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<string, unknown>).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<string, unknown>).status, undefined)
+      assert.strictEqual((result as Record<string, unknown>).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<string, unknown>).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<string, unknown>
+      const flat20 = result20 as Record<string, unknown>
+      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)
+    })
+  })
 })