await describe('I04 - CertificateSigned', async () => {
let station: ChargingStation
let stationWithCertManager: ChargingStationWithCertificateManager
+ let closeWSConnectionMock: ReturnType<typeof mock.fn>
let testableService: ReturnType<typeof createTestableIncomingRequestService>
beforeEach(() => {
station,
createMockCertificateManager()
)
- station.closeWSConnection = mock.fn()
+ closeWSConnectionMock = mock.fn()
+ station.closeWSConnection = closeWSConnectionMock as unknown as () => void
testableService = createTestableIncomingRequestService(new OCPP20IncomingRequestService())
})
assert.notStrictEqual(response.status, undefined)
assert.strictEqual(typeof response.status, 'string')
assert.strictEqual(response.status, GenericStatus.Accepted)
+ assert.strictEqual(closeWSConnectionMock.mock.callCount(), 1)
})
await it('should accept single certificate (no chain)', async () => {
assert.notStrictEqual(response, undefined)
assert.strictEqual(response.status, GenericStatus.Accepted)
assert.strictEqual(response.statusInfo, undefined)
+ assert.strictEqual(closeWSConnectionMock.mock.callCount(), 0)
})
})
assert.notStrictEqual(response.statusInfo, undefined)
assert.notStrictEqual(response.statusInfo?.reasonCode, undefined)
assert.strictEqual(typeof response.statusInfo?.reasonCode, 'string')
+ assert.strictEqual(closeWSConnectionMock.mock.callCount(), 0)
})
})
await testableService.handleRequestCertificateSigned(station, request)
assert.strictEqual(response.status, GenericStatus.Accepted)
- // Verify closeWSConnection was called to trigger reconnect
- assert.ok(mockCloseWSConnection.mock.calls.length > 0)
+ assert.strictEqual(mockCloseWSConnection.mock.callCount(), 1)
})
})
await testableService.handleRequestCertificateSigned(station, request)
assert.strictEqual(response.status, GenericStatus.Accepted)
- // Verify storeCertificate was called
- assert.ok(mockCertManager.storeCertificate.mock.calls.length > 0)
- // Verify closeWSConnection was NOT called for V2GCertificate
- assert.strictEqual(mockCloseWSConnection.mock.calls.length, 0)
+ assert.strictEqual(mockCertManager.storeCertificate.mock.callCount(), 1)
+ assert.strictEqual(mockCloseWSConnection.mock.callCount(), 0)
})
})
* @description Unit tests for OCPP 2.0.1 ChangeAvailability command handling (G03)
*/
+import type { mock } from 'node:test'
+
import assert from 'node:assert/strict'
import { afterEach, beforeEach, describe, it } from 'node:test'
import {
ChangeAvailabilityStatusEnumType,
OCPP20OperationalStatusEnumType,
- OCPPVersion,
+ OCPP20RequestCommand,
ReasonCodeEnumType,
} from '../../../../src/types/index.js'
-import { Constants } from '../../../../src/utils/index.js'
import {
+ flushMicrotasks,
setupConnectorWithTransaction,
standardCleanup,
} from '../../../helpers/TestLifecycleHelpers.js'
import { TEST_CHARGING_STATION_BASE_NAME } from '../../ChargingStationTestConstants.js'
-import { createMockChargingStation } from '../../ChargingStationTestUtils.js'
+import { createOCPP20ListenerStation } from './OCPP20TestUtils.js'
await describe('G03 - ChangeAvailability', async () => {
let station: ChargingStation
+ let requestHandlerMock: ReturnType<typeof mock.fn>
let testableService: ReturnType<typeof createTestableIncomingRequestService>
beforeEach(() => {
- const { station: mockStation } = createMockChargingStation({
- baseName: TEST_CHARGING_STATION_BASE_NAME,
- connectorsCount: 3,
- evseConfiguration: { evsesCount: 3 },
- heartbeatInterval: Constants.DEFAULT_HEARTBEAT_INTERVAL,
- ocppRequestService: {
- requestHandler: async () => await Promise.resolve({}),
- },
- stationInfo: {
- ocppStrictCompliance: false,
- ocppVersion: OCPPVersion.VERSION_201,
- },
- websocketPingInterval: Constants.DEFAULT_WS_PING_INTERVAL,
- })
- station = mockStation
+ ;({ requestHandlerMock, station } = createOCPP20ListenerStation(
+ TEST_CHARGING_STATION_BASE_NAME
+ ))
const incomingRequestService = new OCPP20IncomingRequestService()
testableService = createTestableIncomingRequestService(incomingRequestService)
})
})
// FR: G03.FR.01
- await it('should accept EVSE-level Inoperative when no ongoing transaction', () => {
+ await it('should accept EVSE-level Inoperative when no ongoing transaction', async () => {
const response = testableService.handleRequestChangeAvailability(station, {
evse: { id: 1 },
operationalStatus: OCPP20OperationalStatusEnumType.Inoperative,
assert.strictEqual(response.status, ChangeAvailabilityStatusEnumType.Accepted)
const evseStatus = station.getEvseStatus(1)
assert.strictEqual(evseStatus?.availability, OCPP20OperationalStatusEnumType.Inoperative)
+ await flushMicrotasks()
+ assert.ok(requestHandlerMock.mock.callCount() >= 1)
+ const args = requestHandlerMock.mock.calls[0].arguments as [unknown, string]
+ assert.strictEqual(args[1], OCPP20RequestCommand.STATUS_NOTIFICATION)
})
// FR: G03.FR.02
assert.strictEqual(response.status, ChangeAvailabilityStatusEnumType.Scheduled)
})
- await it('should reject when EVSE does not exist', () => {
+ await it('should reject when EVSE does not exist', async () => {
const response = testableService.handleRequestChangeAvailability(station, {
evse: { id: 999 },
operationalStatus: OCPP20OperationalStatusEnumType.Inoperative,
assert.strictEqual(response.status, ChangeAvailabilityStatusEnumType.Rejected)
assert.notStrictEqual(response.statusInfo, undefined)
assert.strictEqual(response.statusInfo?.reasonCode, ReasonCodeEnumType.UnknownEvse)
+ await flushMicrotasks()
+ assert.strictEqual(requestHandlerMock.mock.callCount(), 0)
})
await it('should accept when already in requested state (idempotent)', () => {
*/
import assert from 'node:assert/strict'
-import { afterEach, beforeEach, describe, it } from 'node:test'
+import { afterEach, beforeEach, describe, it, mock } from 'node:test'
import type { ChargingStation } from '../../../../src/charging-station/index.js'
await describe('CLR-001 - ClearCache clears Authorization Cache', async () => {
await it('should call authService.clearCache() on ClearCache request', async () => {
// Create a mock auth service to verify clearCache is called
- let clearCacheCalled = false
+ const clearCacheMock = mock.fn()
const mockAuthService = {
- clearCache: (): void => {
- clearCacheCalled = true
- },
+ clearCache: clearCacheMock,
getConfiguration: () => ({
authorizationCacheEnabled: true,
}),
try {
const response = await testableService.handleRequestClearCache(station)
- assert.strictEqual(clearCacheCalled, true)
+ assert.strictEqual(clearCacheMock.mock.callCount(), 1)
assert.strictEqual(response.status, GenericStatus.Accepted)
} finally {
// Restore original factory method
await it('should not call idTagsCache.deleteIdTags() on ClearCache request', async () => {
// Verify that IdTagsCache is not touched
- let deleteIdTagsCalled = false
+ const deleteIdTagsMock = mock.fn()
const originalDeleteIdTags = station.idTagsCache.deleteIdTags.bind(station.idTagsCache)
Object.assign(station.idTagsCache, {
- deleteIdTags: () => {
- deleteIdTagsCalled = true
- },
+ deleteIdTags: deleteIdTagsMock,
})
try {
await testableService.handleRequestClearCache(station)
- assert.strictEqual(deleteIdTagsCalled, false)
+ assert.strictEqual(deleteIdTagsMock.mock.callCount(), 0)
} finally {
// Restore original method
Object.assign(station.idTagsCache, { deleteIdTags: originalDeleteIdTags })
})
await it('should not attempt to clear cache when AuthCacheEnabled is false', async () => {
- let clearCacheAttempted = false
+ const clearCacheMock = mock.fn()
const mockAuthService = {
- clearCache: (): void => {
- clearCacheAttempted = true
- },
+ clearCache: clearCacheMock,
getConfiguration: () => ({
authorizationCacheEnabled: false,
}),
await testableService.handleRequestClearCache(station)
// clearCache should NOT be called when cache is disabled
- assert.strictEqual(clearCacheAttempted, false)
+ assert.strictEqual(clearCacheMock.mock.callCount(), 0)
} finally {
// Restore original factory method
Object.assign(OCPPAuthServiceFactory, { getInstance: originalGetInstance })
import { createTestableIncomingRequestService } from '../../../../src/charging-station/ocpp/2.0/__testable__/index.js'
import { OCPP20IncomingRequestService } from '../../../../src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.js'
import {
+ OCPP20RequestCommand,
OCPPVersion,
ReasonCodeEnumType,
UnlockStatusEnumType,
await testableService.handleRequestUnlockConnector(mockStation, request)
// sendAndSetConnectorStatus calls requestHandler internally for StatusNotification
- assert.ok(requestHandlerMock.mock.calls.length > 0)
+ assert.strictEqual(requestHandlerMock.mock.callCount(), 1)
+ const args = requestHandlerMock.mock.calls[0].arguments as [unknown, string]
+ assert.strictEqual(args[1], OCPP20RequestCommand.STATUS_NOTIFICATION)
})
await it('should return a Promise from async handler', async () => {