--- /dev/null
+/**
+ * Testable wrapper for OCPP 2.0 RequestService
+ *
+ * This module provides type-safe testing utilities for OCPP20RequestService.
+ * It enables mocking of the protected sendMessage method and provides access
+ * to public request methods for testing ISO 15118 certificate and OCSP flows.
+ * @example
+ * ```typescript
+ * import {
+ * createTestableRequestService,
+ * type SendMessageMock,
+ * } from './__testable__/OCPP20RequestServiceTestable.js'
+ *
+ * const { sendMessageMock, service } = createTestableRequestService({
+ * sendMessageResponse: { status: Iso15118EVCertificateStatusEnumType.Accepted }
+ * })
+ * const response = await service.requestGet15118EVCertificate(station, schema, action, exi)
+ * expect(sendMessageMock.mock.calls.length).toBe(1)
+ * ```
+ */
+
+import { mock } from 'node:test'
+
+import type {
+ CertificateActionEnumType,
+ CertificateSigningUseEnumType,
+ JsonType,
+ OCPP20Get15118EVCertificateResponse,
+ OCPP20GetCertificateStatusResponse,
+ OCPP20RequestCommand,
+ OCPP20SignCertificateResponse,
+ OCSPRequestDataType,
+ RequestParams,
+} from '../../../../types/index.js'
+import type { ChargingStation } from '../../../index.js'
+
+import { OCPP20RequestService } from '../OCPP20RequestService.js'
+import { OCPP20ResponseService } from '../OCPP20ResponseService.js'
+
+/**
+ * Type definition for sendMessage mock function.
+ * Matches the protected sendMessage signature in OCPPRequestService.
+ */
+export type SendMessageFn = (
+ chargingStation: ChargingStation,
+ messageId: string,
+ messagePayload: JsonType,
+ commandName: string,
+ params?: RequestParams
+) => Promise<JsonType>
+
+/**
+ * Interface for the mock function with call tracking
+ */
+export interface SendMessageMock {
+ fn: SendMessageFn
+ mock: {
+ calls: {
+ arguments: [ChargingStation, string, JsonType, string, RequestParams?]
+ }[]
+ }
+}
+
+/**
+ * Interface exposing OCPP20RequestService methods for testing.
+ */
+export interface TestableOCPP20RequestService {
+ /**
+ * Build request payload for OCPP 2.0 commands.
+ * Used internally to construct command-specific payloads.
+ */
+ buildRequestPayload: (
+ chargingStation: ChargingStation,
+ commandName: OCPP20RequestCommand,
+ commandParams?: JsonType
+ ) => JsonType
+
+ /**
+ * Request an ISO 15118 EV certificate from the CSMS.
+ * Forwards EXI-encoded certificate request from EV to CSMS.
+ */
+ requestGet15118EVCertificate: (
+ chargingStation: ChargingStation,
+ iso15118SchemaVersion: string,
+ action: CertificateActionEnumType,
+ exiRequest: string
+ ) => Promise<OCPP20Get15118EVCertificateResponse>
+
+ /**
+ * Request OCSP certificate status from the CSMS.
+ * Sends OCSP request to check certificate revocation status.
+ */
+ requestGetCertificateStatus: (
+ chargingStation: ChargingStation,
+ ocspRequestData: OCSPRequestDataType
+ ) => Promise<OCPP20GetCertificateStatusResponse>
+ /**
+ * Request certificate signing from the CSMS.
+ * Generates a CSR and sends it to CSMS for signing.
+ */
+ requestSignCertificate: (
+ chargingStation: ChargingStation,
+ certificateType?: CertificateSigningUseEnumType
+ ) => Promise<OCPP20SignCertificateResponse>
+}
+
+/**
+ * Configuration options for creating a testable request service
+ */
+export interface TestableRequestServiceOptions<T extends JsonType = JsonType> {
+ /**
+ * Response to return from mocked sendMessage.
+ * Can be a partial response that will be spread into the result.
+ */
+ sendMessageResponse?: Partial<T>
+}
+
+/**
+ * Result of creating a testable request service
+ */
+export interface TestableRequestServiceResult {
+ /**
+ * The mock function for sendMessage with call tracking
+ */
+ sendMessageMock: SendMessageMock
+ /**
+ * The testable service with mocked sendMessage
+ */
+ service: TestableOCPP20RequestService
+}
+
+/**
+ * Creates a testable OCPP20RequestService with mocked sendMessage.
+ *
+ * This factory function creates an OCPP20RequestService instance with its
+ * protected sendMessage method replaced by a mock that returns configured
+ * responses. This allows testing the public request methods without making
+ * actual network calls.
+ * @template T - The expected response type
+ * @param options - Configuration for the testable service
+ * @returns Object containing the testable service and sendMessage mock
+ * @example
+ * ```typescript
+ * // Create service with mocked response
+ * const { sendMessageMock, service } = createTestableRequestService({
+ * sendMessageResponse: {
+ * status: Iso15118EVCertificateStatusEnumType.Accepted,
+ * exiResponse: 'base64EncodedResponse'
+ * }
+ * })
+ *
+ * // Call the public method
+ * const response = await service.requestGet15118EVCertificate(
+ * mockStation,
+ * schemaVersion,
+ * CertificateActionEnumType.Install,
+ * exiRequest
+ * )
+ *
+ * // Verify the call
+ * expect(sendMessageMock.mock.calls.length).toBe(1)
+ * const sentPayload = sendMessageMock.mock.calls[0].arguments[2]
+ * expect(sentPayload.action).toBe(CertificateActionEnumType.Install)
+ * ```
+ */
+export function createTestableRequestService<T extends JsonType = JsonType> (
+ options: TestableRequestServiceOptions<T> = {}
+): TestableRequestServiceResult {
+ const responseService = new OCPP20ResponseService()
+ const requestService = new OCPP20RequestService(responseService)
+
+ // Create mock function with call tracking
+ const mockFn = mock.fn(() =>
+ Promise.resolve({
+ ...options.sendMessageResponse,
+ } as JsonType)
+ )
+
+ // Replace protected sendMessage with mock
+ // Use Object.defineProperty to override the protected method
+ Object.defineProperty(requestService, 'sendMessage', {
+ configurable: true,
+ value: mockFn,
+ writable: true,
+ })
+
+ // Create typed wrapper for the mock
+ const sendMessageMock: SendMessageMock = {
+ fn: mockFn as unknown as SendMessageFn,
+ mock: mockFn.mock as SendMessageMock['mock'],
+ }
+
+ return {
+ sendMessageMock,
+ service: requestService as unknown as TestableOCPP20RequestService,
+ }
+}
--- /dev/null
+/**
+ * Testable interface for OCPP20VariableManager
+ *
+ * This module provides type-safe access to private methods for testing purposes.
+ * It replaces `as any` casts with a properly typed interface, enabling:
+ * - Type-safe method invocations in tests
+ * - IntelliSense and autocompletion for handler parameters/returns
+ * - Compile-time checking for test code
+ * @example
+ * ```typescript
+ * import { createTestableVariableManager } from './__testable__/OCPP20VariableManagerTestable.js'
+ *
+ * const testable = createTestableVariableManager(OCPP20VariableManager.getInstance())
+ * const isValid = testable.isComponentValid(mockChargingStation, component)
+ * ```
+ */
+
+import type { ComponentType, VariableType } from '../../../../types/index.js'
+import type { ChargingStation } from '../../../index.js'
+import type { OCPP20VariableManager } from '../OCPP20VariableManager.js'
+
+/**
+ * Interface exposing private methods of OCPP20VariableManager for testing.
+ * Each method signature matches the corresponding private method in the class.
+ */
+export interface TestableOCPP20VariableManager {
+ /**
+ * Validates whether a component is supported by the charging station.
+ * @param chargingStation - The charging station instance
+ * @param component - The component to validate
+ * @returns true if the component is valid/supported
+ */
+ isComponentValid: (chargingStation: ChargingStation, component: ComponentType) => boolean
+
+ /**
+ * Checks whether a variable is supported for the given component.
+ * @param component - The component containing the variable
+ * @param variable - The variable to check
+ * @returns true if the variable is supported
+ */
+ isVariableSupported: (component: ComponentType, variable: VariableType) => boolean
+}
+
+/**
+ * Creates a testable wrapper around OCPP20VariableManager.
+ * Provides type-safe access to private methods without `as any` casts.
+ * @param manager - The OCPP20VariableManager instance to wrap
+ * @returns A typed interface exposing private methods
+ * @example
+ * ```typescript
+ * // Before (with as any cast):
+ * const isValid = (manager as any).isComponentValid(station, component)
+ *
+ * // After (with testable interface):
+ * const testable = createTestableVariableManager(manager)
+ * const isValid = testable.isComponentValid(station, component)
+ * ```
+ */
+export function createTestableVariableManager (
+ manager: OCPP20VariableManager
+): TestableOCPP20VariableManager {
+ // Cast to unknown first to satisfy TypeScript while preserving runtime behavior
+ const managerImpl = manager as unknown as TestableOCPP20VariableManager
+
+ return {
+ isComponentValid: managerImpl.isComponentValid.bind(manager),
+ isVariableSupported: managerImpl.isVariableSupported.bind(manager),
+ }
+}
OCPP20ResetResponse,
OCPP20SetVariablesRequest,
OCPP20SetVariablesResponse,
+ ReportBaseEnumType,
+ type ReportDataType,
} from '../../../../types/index.js'
import type { ChargingStation } from '../../../index.js'
import type { OCPP20IncomingRequestService } from '../OCPP20IncomingRequestService.js'
* Each method signature matches the corresponding private method in the service class.
*/
export interface TestableOCPP20IncomingRequestService {
+ /**
+ * Builds report data for the device model report.
+ * Used internally by handleRequestGetBaseReport.
+ */
+ buildReportData: (
+ chargingStation: ChargingStation,
+ reportBase: ReportBaseEnumType
+ ) => ReportDataType[]
+
/**
* Handles OCPP 2.0 CertificateSigned request from central system.
* Receives signed certificate chain from CSMS and stores it in the charging station.
const serviceImpl = service as unknown as TestableOCPP20IncomingRequestService
return {
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-explicit-any
+ buildReportData: (serviceImpl as any).buildReportData.bind(service),
handleRequestCertificateSigned: serviceImpl.handleRequestCertificateSigned.bind(service),
handleRequestClearCache: serviceImpl.handleRequestClearCache.bind(service),
handleRequestDeleteCertificate: serviceImpl.handleRequestDeleteCertificate.bind(service),
handleRequestStopTransaction: serviceImpl.handleRequestStopTransaction.bind(service),
}
}
+
+export {
+ createTestableRequestService,
+ type SendMessageFn,
+ type SendMessageMock,
+ type TestableOCPP20RequestService,
+ type TestableRequestServiceOptions,
+ type TestableRequestServiceResult,
+} from './OCPP20RequestServiceTestable.js'
+
+export {
+ createTestableVariableManager,
+ type TestableOCPP20VariableManager,
+} from './OCPP20VariableManagerTestable.js'
* @file Tests for OCPP20IncomingRequestService CertificateSigned
* @description Unit tests for OCPP 2.0 CertificateSigned command handling
*/
-/* eslint-disable @typescript-eslint/no-unsafe-member-access */
-/* eslint-disable @typescript-eslint/no-unsafe-assignment */
-/* eslint-disable @typescript-eslint/no-unsafe-call */
-/* eslint-disable @typescript-eslint/no-explicit-any */
import { expect } from '@std/expect'
-import { describe, it, mock } from 'node:test'
+import { describe, it, mock, type Mock } from 'node:test'
+import type { ChargingStation } from '../../../../src/charging-station/index.js'
+import type {
+ DeleteCertificateResult,
+ GetInstalledCertificatesResult,
+ OCPP20CertificateManagerInterface,
+ StoreCertificateResult,
+} from '../../../../src/charging-station/ocpp/2.0/OCPP20CertificateManager.js'
+
+import { createTestableIncomingRequestService } from '../../../../src/charging-station/ocpp/2.0/__testable__/index.js'
import { OCPP20IncomingRequestService } from '../../../../src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.js'
import {
CertificateSigningUseEnumType,
Rlc3RDQTAeFw0yNDAxMDEwMDAwMDBaFw0yOTAxMDEwMDAwMDBaMBExDzANBgNVBA
MMBnRlc3RDQTBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQC5p8U8zTk8TT5H5s8mjx`
+/**
+ * Mock certificate manager interface for testing
+ * Provides typed access to mock certificate operations
+ */
+interface MockCertificateManager extends OCPP20CertificateManagerInterface {
+ deleteCertificate: Mock<() => DeleteCertificateResult>
+ getInstalledCertificates: Mock<() => GetInstalledCertificatesResult>
+ storeCertificate: Mock<() => StoreCertificateResult>
+ validateCertificateFormat: Mock<(cert: string) => boolean>
+}
+
+/**
+ * Test-specific ChargingStation interface with certificate management
+ * Extends ChargingStation with properties needed for certificate tests
+ */
+interface TestableChargingStationWithCertificate extends ChargingStation {
+ certificateManager?: MockCertificateManager
+ closeWSConnection?: Mock<() => void>
+}
+
const createMockCertificateManager = (
options: {
storeCertificateError?: Error
storeCertificateResult?: boolean
} = {}
-) => ({
+): MockCertificateManager => ({
deleteCertificate: mock.fn(),
- getInstalledCertificates: mock.fn(() => []),
+ getInstalledCertificates: mock.fn(() => ({ certificateHashDataChain: [] })),
storeCertificate: mock.fn(() => {
if (options.storeCertificateError) {
throw options.storeCertificateError
}
- return options.storeCertificateResult ?? true
+ return { success: options.storeCertificateResult ?? true }
}),
validateCertificateFormat: mock.fn((cert: string) => {
return (
ocppVersion: OCPPVersion.VERSION_201,
},
websocketPingInterval: Constants.DEFAULT_WEBSOCKET_PING_INTERVAL,
- })
+ }) as TestableChargingStationWithCertificate
- ;(mockChargingStation as any).certificateManager = createMockCertificateManager()
+ mockChargingStation.certificateManager = createMockCertificateManager()
// Mock closeWSConnection for reconnect tests
- ;(mockChargingStation as any).closeWSConnection = mock.fn()
+ mockChargingStation.closeWSConnection = mock.fn()
const incomingRequestService = new OCPP20IncomingRequestService()
+ const testableService = createTestableIncomingRequestService(incomingRequestService)
await describe('Valid Certificate Chain Installation', async () => {
await it('Should accept valid certificate chain', async () => {
- ;(mockChargingStation as any).certificateManager = createMockCertificateManager({
+ mockChargingStation.certificateManager = createMockCertificateManager({
storeCertificateResult: true,
})
certificateType: CertificateSigningUseEnumType.ChargingStationCertificate,
}
- const response: OCPP20CertificateSignedResponse = await (
- incomingRequestService as any
- ).handleRequestCertificateSigned(mockChargingStation, request)
+ const response: OCPP20CertificateSignedResponse =
+ await testableService.handleRequestCertificateSigned(mockChargingStation, request)
expect(response).toBeDefined()
expect(typeof response).toBe('object')
})
await it('Should accept single certificate (no chain)', async () => {
- ;(mockChargingStation as any).certificateManager = createMockCertificateManager({
+ mockChargingStation.certificateManager = createMockCertificateManager({
storeCertificateResult: true,
})
certificateType: CertificateSigningUseEnumType.V2GCertificate,
}
- const response: OCPP20CertificateSignedResponse = await (
- incomingRequestService as any
- ).handleRequestCertificateSigned(mockChargingStation, request)
+ const response: OCPP20CertificateSignedResponse =
+ await testableService.handleRequestCertificateSigned(mockChargingStation, request)
expect(response).toBeDefined()
expect(response.status).toBe(GenericStatus.Accepted)
certificateType: CertificateSigningUseEnumType.ChargingStationCertificate,
}
- const response: OCPP20CertificateSignedResponse = await (
- incomingRequestService as any
- ).handleRequestCertificateSigned(mockChargingStation, request)
+ const response: OCPP20CertificateSignedResponse =
+ await testableService.handleRequestCertificateSigned(mockChargingStation, request)
expect(response).toBeDefined()
expect(response.status).toBe(GenericStatus.Rejected)
const mockCertManager = createMockCertificateManager({
storeCertificateResult: true,
})
- ;(mockChargingStation as any).certificateManager = mockCertManager
+ mockChargingStation.certificateManager = mockCertManager
const mockCloseWSConnection = mock.fn()
- ;(mockChargingStation as any).closeWSConnection = mockCloseWSConnection
+ mockChargingStation.closeWSConnection = mockCloseWSConnection
const request: OCPP20CertificateSignedRequest = {
certificateChain: VALID_PEM_CERTIFICATE,
certificateType: CertificateSigningUseEnumType.ChargingStationCertificate,
}
- const response: OCPP20CertificateSignedResponse = await (
- incomingRequestService as any
- ).handleRequestCertificateSigned(mockChargingStation, request)
+ const response: OCPP20CertificateSignedResponse =
+ await testableService.handleRequestCertificateSigned(mockChargingStation, request)
expect(response.status).toBe(GenericStatus.Accepted)
// Verify closeWSConnection was called to trigger reconnect
const mockCertManager = createMockCertificateManager({
storeCertificateResult: true,
})
- ;(mockChargingStation as any).certificateManager = mockCertManager
+ mockChargingStation.certificateManager = mockCertManager
const mockCloseWSConnection = mock.fn()
- ;(mockChargingStation as any).closeWSConnection = mockCloseWSConnection
+ mockChargingStation.closeWSConnection = mockCloseWSConnection
const request: OCPP20CertificateSignedRequest = {
certificateChain: VALID_PEM_CERTIFICATE,
certificateType: CertificateSigningUseEnumType.V2GCertificate,
}
- const response: OCPP20CertificateSignedResponse = await (
- incomingRequestService as any
- ).handleRequestCertificateSigned(mockChargingStation, request)
+ const response: OCPP20CertificateSignedResponse =
+ await testableService.handleRequestCertificateSigned(mockChargingStation, request)
expect(response.status).toBe(GenericStatus.Accepted)
// Verify storeCertificate was called
ocppVersion: OCPPVersion.VERSION_201,
},
websocketPingInterval: Constants.DEFAULT_WEBSOCKET_PING_INTERVAL,
- })
+ }) as TestableChargingStationWithCertificate
- // Ensure certificateManager is null/undefined
- delete (stationWithoutCertManager as any).certificateManager
+ // Ensure certificateManager is undefined (not present)
+ delete stationWithoutCertManager.certificateManager
const request: OCPP20CertificateSignedRequest = {
certificateChain: VALID_PEM_CERTIFICATE,
certificateType: CertificateSigningUseEnumType.ChargingStationCertificate,
}
- const response: OCPP20CertificateSignedResponse = await (
- incomingRequestService as any
- ).handleRequestCertificateSigned(stationWithoutCertManager, request)
+ const response: OCPP20CertificateSignedResponse =
+ await testableService.handleRequestCertificateSigned(stationWithoutCertManager, request)
expect(response).toBeDefined()
expect(response.status).toBe(GenericStatus.Rejected)
await describe('Storage Failure Handling', async () => {
await it('Should return Rejected status when storage fails', async () => {
- ;(mockChargingStation as any).certificateManager = createMockCertificateManager({
+ mockChargingStation.certificateManager = createMockCertificateManager({
storeCertificateResult: false,
})
certificateType: CertificateSigningUseEnumType.ChargingStationCertificate,
}
- const response: OCPP20CertificateSignedResponse = await (
- incomingRequestService as any
- ).handleRequestCertificateSigned(mockChargingStation, request)
+ const response: OCPP20CertificateSignedResponse =
+ await testableService.handleRequestCertificateSigned(mockChargingStation, request)
expect(response).toBeDefined()
expect(response.status).toBe(GenericStatus.Rejected)
})
await it('Should return Rejected status when storage throws error', async () => {
- ;(mockChargingStation as any).certificateManager = createMockCertificateManager({
+ mockChargingStation.certificateManager = createMockCertificateManager({
storeCertificateError: new Error('Storage full'),
})
certificateType: CertificateSigningUseEnumType.V2GCertificate,
}
- const response: OCPP20CertificateSignedResponse = await (
- incomingRequestService as any
- ).handleRequestCertificateSigned(mockChargingStation, request)
+ const response: OCPP20CertificateSignedResponse =
+ await testableService.handleRequestCertificateSigned(mockChargingStation, request)
expect(response).toBeDefined()
expect(response.status).toBe(GenericStatus.Rejected)
await describe('Response Structure Validation', async () => {
await it('Should return response matching CertificateSignedResponse schema', async () => {
- ;(mockChargingStation as any).certificateManager = createMockCertificateManager({
+ mockChargingStation.certificateManager = createMockCertificateManager({
storeCertificateResult: true,
})
certificateType: CertificateSigningUseEnumType.ChargingStationCertificate,
}
- const response: OCPP20CertificateSignedResponse = await (
- incomingRequestService as any
- ).handleRequestCertificateSigned(mockChargingStation, request)
+ const response: OCPP20CertificateSignedResponse =
+ await testableService.handleRequestCertificateSigned(mockChargingStation, request)
expect(response).toBeDefined()
expect(typeof response).toBe('object')
certificateType: CertificateSigningUseEnumType.ChargingStationCertificate,
}
- const response: OCPP20CertificateSignedResponse = await (
- incomingRequestService as any
- ).handleRequestCertificateSigned(mockChargingStation, request)
+ const response: OCPP20CertificateSignedResponse =
+ await testableService.handleRequestCertificateSigned(mockChargingStation, request)
expect(response.status).toBe(GenericStatus.Rejected)
expect(response.statusInfo).toBeDefined()
* @file Tests for OCPP20IncomingRequestService ClearCache
* @description Unit tests for OCPP 2.0 ClearCache command handling (C11)
*/
-/* eslint-disable @typescript-eslint/no-unsafe-member-access */
-/* eslint-disable @typescript-eslint/no-unsafe-assignment */
-/* eslint-disable @typescript-eslint/no-unsafe-call */
-/* eslint-disable @typescript-eslint/no-explicit-any */
import { expect } from '@std/expect'
import { describe, it } from 'node:test'
+import { createTestableIncomingRequestService } from '../../../../src/charging-station/ocpp/2.0/__testable__/index.js'
import { OCPP20IncomingRequestService } from '../../../../src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.js'
import { OCPPAuthServiceFactory } from '../../../../src/charging-station/ocpp/auth/services/OCPPAuthServiceFactory.js'
import { GenericStatus, OCPPVersion } from '../../../../src/types/index.js'
})
const incomingRequestService = new OCPP20IncomingRequestService()
+ const testableService = createTestableIncomingRequestService(incomingRequestService)
// FR: C11.FR.01 - CS SHALL attempt to clear its Authorization Cache
await it('Should handle ClearCache request successfully', async () => {
- const response = await (incomingRequestService as any).handleRequestClearCache(
- mockChargingStation
- )
+ const response = await testableService.handleRequestClearCache(mockChargingStation)
expect(response).toBeDefined()
expect(typeof response).toBe('object')
// FR: C11.FR.02 - Return correct status based on cache clearing result
await it('Should return correct status based on cache clearing result', async () => {
- const response = await (incomingRequestService as any).handleRequestClearCache(
- mockChargingStation
- )
+ const response = await testableService.handleRequestClearCache(mockChargingStation)
expect(response).toBeDefined()
expect(response.status).toBeDefined()
// Mock the factory to return our mock auth service
const originalGetInstance = OCPPAuthServiceFactory.getInstance.bind(OCPPAuthServiceFactory)
- ;(OCPPAuthServiceFactory as any).getInstance = (): Promise<typeof mockAuthService> =>
- Promise.resolve(mockAuthService)
+ Object.assign(OCPPAuthServiceFactory, {
+ getInstance: (): Promise<typeof mockAuthService> => Promise.resolve(mockAuthService),
+ })
try {
- const response = await (incomingRequestService as any).handleRequestClearCache(
- mockChargingStation
- )
+ const response = await testableService.handleRequestClearCache(mockChargingStation)
expect(clearCacheCalled).toBe(true)
expect(response.status).toBe(GenericStatus.Accepted)
} finally {
// Restore original factory method
- ;(OCPPAuthServiceFactory as any).getInstance = originalGetInstance
+ Object.assign(OCPPAuthServiceFactory, { getInstance: originalGetInstance })
}
})
await it('Should NOT call idTagsCache.deleteIdTags() on ClearCache request', async () => {
// Verify that IdTagsCache is not touched
let deleteIdTagsCalled = false
- // eslint-disable-next-line @typescript-eslint/unbound-method
const originalDeleteIdTags = mockChargingStation.idTagsCache.deleteIdTags
- ;(mockChargingStation.idTagsCache as any).deleteIdTags = () => {
- deleteIdTagsCalled = true
- }
+ Object.assign(mockChargingStation.idTagsCache, {
+ deleteIdTags: () => {
+ deleteIdTagsCalled = true
+ },
+ })
try {
- await (incomingRequestService as any).handleRequestClearCache(mockChargingStation)
+ await testableService.handleRequestClearCache(mockChargingStation)
expect(deleteIdTagsCalled).toBe(false)
} finally {
// Restore original method
- ;(mockChargingStation.idTagsCache as any).deleteIdTags = originalDeleteIdTags
+ Object.assign(mockChargingStation.idTagsCache, { deleteIdTags: originalDeleteIdTags })
}
})
})
// Mock the factory to return our mock auth service
const originalGetInstance = OCPPAuthServiceFactory.getInstance.bind(OCPPAuthServiceFactory)
- ;(OCPPAuthServiceFactory as any).getInstance = (): Promise<typeof mockAuthService> =>
- Promise.resolve(mockAuthService)
+ Object.assign(OCPPAuthServiceFactory, {
+ getInstance: (): Promise<typeof mockAuthService> => Promise.resolve(mockAuthService),
+ })
try {
- const response = await (incomingRequestService as any).handleRequestClearCache(
- mockChargingStation
- )
+ const response = await testableService.handleRequestClearCache(mockChargingStation)
expect(response.status).toBe(GenericStatus.Rejected)
} finally {
// Restore original factory method
- ;(OCPPAuthServiceFactory as any).getInstance = originalGetInstance
+ Object.assign(OCPPAuthServiceFactory, { getInstance: originalGetInstance })
}
})
// Mock the factory to return our mock auth service
const originalGetInstance = OCPPAuthServiceFactory.getInstance.bind(OCPPAuthServiceFactory)
- ;(OCPPAuthServiceFactory as any).getInstance = (): Promise<typeof mockAuthService> =>
- Promise.resolve(mockAuthService)
+ Object.assign(OCPPAuthServiceFactory, {
+ getInstance: (): Promise<typeof mockAuthService> => Promise.resolve(mockAuthService),
+ })
try {
- const response = await (incomingRequestService as any).handleRequestClearCache(
- mockChargingStation
- )
+ const response = await testableService.handleRequestClearCache(mockChargingStation)
expect(response.status).toBe(GenericStatus.Accepted)
} finally {
// Restore original factory method
- ;(OCPPAuthServiceFactory as any).getInstance = originalGetInstance
+ Object.assign(OCPPAuthServiceFactory, { getInstance: originalGetInstance })
}
})
// Mock the factory to return our mock auth service
const originalGetInstance = OCPPAuthServiceFactory.getInstance.bind(OCPPAuthServiceFactory)
- ;(OCPPAuthServiceFactory as any).getInstance = (): Promise<typeof mockAuthService> =>
- Promise.resolve(mockAuthService)
+ Object.assign(OCPPAuthServiceFactory, {
+ getInstance: (): Promise<typeof mockAuthService> => Promise.resolve(mockAuthService),
+ })
try {
- const response = await (incomingRequestService as any).handleRequestClearCache(
- mockChargingStation
- )
+ const response = await testableService.handleRequestClearCache(mockChargingStation)
expect(response.status).toBe(GenericStatus.Rejected)
} finally {
// Restore original factory method
- ;(OCPPAuthServiceFactory as any).getInstance = originalGetInstance
+ Object.assign(OCPPAuthServiceFactory, { getInstance: originalGetInstance })
}
})
// Mock the factory to return our mock auth service
const originalGetInstance = OCPPAuthServiceFactory.getInstance.bind(OCPPAuthServiceFactory)
- ;(OCPPAuthServiceFactory as any).getInstance = (): Promise<typeof mockAuthService> =>
- Promise.resolve(mockAuthService)
+ Object.assign(OCPPAuthServiceFactory, {
+ getInstance: (): Promise<typeof mockAuthService> => Promise.resolve(mockAuthService),
+ })
try {
- await (incomingRequestService as any).handleRequestClearCache(mockChargingStation)
+ await testableService.handleRequestClearCache(mockChargingStation)
// clearCache should NOT be called when cache is disabled
expect(clearCacheAttempted).toBe(false)
} finally {
// Restore original factory method
- ;(OCPPAuthServiceFactory as any).getInstance = originalGetInstance
+ Object.assign(OCPPAuthServiceFactory, { getInstance: originalGetInstance })
}
})
})
await it('Should return Rejected when authService factory fails (no cache support)', async () => {
// Mock factory to throw error (simulates no Authorization Cache support)
const originalGetInstance = OCPPAuthServiceFactory.getInstance.bind(OCPPAuthServiceFactory)
- ;(OCPPAuthServiceFactory as any).getInstance = (): Promise<never> =>
- Promise.reject(new Error('Authorization Cache not supported'))
+ Object.assign(OCPPAuthServiceFactory, {
+ getInstance: (): Promise<never> =>
+ Promise.reject(new Error('Authorization Cache not supported')),
+ })
try {
- const response = await (incomingRequestService as any).handleRequestClearCache(
- mockChargingStation
- )
+ const response = await testableService.handleRequestClearCache(mockChargingStation)
// Per C11.FR.05: SHALL return Rejected if CS does not support Authorization Cache
expect(response.status).toBe(GenericStatus.Rejected)
} finally {
// Restore original factory method
- ;(OCPPAuthServiceFactory as any).getInstance = originalGetInstance
+ Object.assign(OCPPAuthServiceFactory, { getInstance: originalGetInstance })
}
})
})
* @file Tests for OCPP20IncomingRequestService DeleteCertificate
* @description Unit tests for OCPP 2.0 DeleteCertificate command handling
*/
-/* eslint-disable @typescript-eslint/no-unsafe-member-access */
-/* eslint-disable @typescript-eslint/no-unsafe-assignment */
-/* eslint-disable @typescript-eslint/no-unsafe-call */
-/* eslint-disable @typescript-eslint/no-explicit-any */
import { expect } from '@std/expect'
import { describe, it, mock } from 'node:test'
+import type { ChargingStationWithCertificateManager } from '../../../../src/charging-station/ocpp/2.0/OCPP20CertificateManager.js'
+
+import { createTestableIncomingRequestService } from '../../../../src/charging-station/ocpp/2.0/__testable__/index.js'
import { OCPP20IncomingRequestService } from '../../../../src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.js'
import {
DeleteCertificateStatusEnumType,
websocketPingInterval: Constants.DEFAULT_WEBSOCKET_PING_INTERVAL,
})
- ;(mockChargingStation as any).certificateManager = createMockCertificateManager()
+ // Cast to allow setting certificateManager property
+ const stationWithCertManager = mockChargingStation as unknown as ChargingStationWithCertificateManager
+ stationWithCertManager.certificateManager = createMockCertificateManager()
const incomingRequestService = new OCPP20IncomingRequestService()
+ const testableService = createTestableIncomingRequestService(incomingRequestService)
await describe('Valid Certificate Deletion', async () => {
await it('Should accept deletion of existing certificate', async () => {
- ;(mockChargingStation as any).certificateManager = createMockCertificateManager({
+ stationWithCertManager.certificateManager = createMockCertificateManager({
deleteCertificateResult: { status: 'Accepted' },
})
certificateHashData: VALID_CERTIFICATE_HASH_DATA,
}
- const response: OCPP20DeleteCertificateResponse = await (
- incomingRequestService as any
- ).handleRequestDeleteCertificate(mockChargingStation, request)
+ const response: OCPP20DeleteCertificateResponse =
+ await testableService.handleRequestDeleteCertificate(mockChargingStation, request)
expect(response).toBeDefined()
expect(typeof response).toBe('object')
})
await it('Should accept deletion with SHA384 hash algorithm', async () => {
- ;(mockChargingStation as any).certificateManager = createMockCertificateManager({
+ stationWithCertManager.certificateManager = createMockCertificateManager({
deleteCertificateResult: { status: 'Accepted' },
})
},
}
- const response: OCPP20DeleteCertificateResponse = await (
- incomingRequestService as any
- ).handleRequestDeleteCertificate(mockChargingStation, request)
+ const response: OCPP20DeleteCertificateResponse =
+ await testableService.handleRequestDeleteCertificate(mockChargingStation, request)
expect(response).toBeDefined()
expect(response.status).toBe(DeleteCertificateStatusEnumType.Accepted)
})
await it('Should accept deletion with SHA512 hash algorithm', async () => {
- ;(mockChargingStation as any).certificateManager = createMockCertificateManager({
+ stationWithCertManager.certificateManager = createMockCertificateManager({
deleteCertificateResult: { status: 'Accepted' },
})
},
}
- const response: OCPP20DeleteCertificateResponse = await (
- incomingRequestService as any
- ).handleRequestDeleteCertificate(mockChargingStation, request)
+ const response: OCPP20DeleteCertificateResponse =
+ await testableService.handleRequestDeleteCertificate(mockChargingStation, request)
expect(response).toBeDefined()
expect(response.status).toBe(DeleteCertificateStatusEnumType.Accepted)
await describe('Certificate Not Found', async () => {
await it('Should return NotFound for non-existent certificate', async () => {
- ;(mockChargingStation as any).certificateManager = createMockCertificateManager({
+ stationWithCertManager.certificateManager = createMockCertificateManager({
deleteCertificateResult: { status: 'NotFound' },
})
certificateHashData: NONEXISTENT_CERTIFICATE_HASH_DATA,
}
- const response: OCPP20DeleteCertificateResponse = await (
- incomingRequestService as any
- ).handleRequestDeleteCertificate(mockChargingStation, request)
+ const response: OCPP20DeleteCertificateResponse =
+ await testableService.handleRequestDeleteCertificate(mockChargingStation, request)
expect(response).toBeDefined()
expect(response.status).toBe(DeleteCertificateStatusEnumType.NotFound)
await describe('Deletion Failure Handling', async () => {
await it('Should return Failed status when deletion throws error', async () => {
- ;(mockChargingStation as any).certificateManager = createMockCertificateManager({
+ stationWithCertManager.certificateManager = createMockCertificateManager({
deleteCertificateError: new Error('Deletion failed'),
})
certificateHashData: VALID_CERTIFICATE_HASH_DATA,
}
- const response: OCPP20DeleteCertificateResponse = await (
- incomingRequestService as any
- ).handleRequestDeleteCertificate(mockChargingStation, request)
+ const response: OCPP20DeleteCertificateResponse =
+ await testableService.handleRequestDeleteCertificate(mockChargingStation, request)
expect(response).toBeDefined()
expect(response.status).toBe(DeleteCertificateStatusEnumType.Failed)
websocketPingInterval: Constants.DEFAULT_WEBSOCKET_PING_INTERVAL,
})
- ;(stationWithoutCertManager as any).certificateManager = undefined
+ const stationNoCertManager = stationWithoutCertManager as unknown as ChargingStationWithCertificateManager
+ stationNoCertManager.certificateManager = undefined as unknown as ChargingStationWithCertificateManager['certificateManager']
const request: OCPP20DeleteCertificateRequest = {
certificateHashData: VALID_CERTIFICATE_HASH_DATA,
}
- const response: OCPP20DeleteCertificateResponse = await (
- incomingRequestService as any
- ).handleRequestDeleteCertificate(stationWithoutCertManager, request)
+ const response: OCPP20DeleteCertificateResponse =
+ await testableService.handleRequestDeleteCertificate(stationWithoutCertManager, request)
expect(response).toBeDefined()
expect(response.status).toBe(DeleteCertificateStatusEnumType.Failed)
await describe('Response Structure Validation', async () => {
await it('Should return response matching DeleteCertificateResponse schema', async () => {
- ;(mockChargingStation as any).certificateManager = createMockCertificateManager({
+ stationWithCertManager.certificateManager = createMockCertificateManager({
deleteCertificateResult: { status: 'Accepted' },
})
certificateHashData: VALID_CERTIFICATE_HASH_DATA,
}
- const response: OCPP20DeleteCertificateResponse = await (
- incomingRequestService as any
- ).handleRequestDeleteCertificate(mockChargingStation, request)
+ const response: OCPP20DeleteCertificateResponse =
+ await testableService.handleRequestDeleteCertificate(mockChargingStation, request)
expect(response).toBeDefined()
expect(typeof response).toBe('object')
})
await it('Should include statusInfo with reasonCode for failure', async () => {
- ;(mockChargingStation as any).certificateManager = createMockCertificateManager({
+ stationWithCertManager.certificateManager = createMockCertificateManager({
deleteCertificateError: new Error('Deletion failed'),
})
certificateHashData: VALID_CERTIFICATE_HASH_DATA,
}
- const response: OCPP20DeleteCertificateResponse = await (
- incomingRequestService as any
- ).handleRequestDeleteCertificate(mockChargingStation, request)
+ const response: OCPP20DeleteCertificateResponse =
+ await testableService.handleRequestDeleteCertificate(mockChargingStation, request)
expect(response.status).toBe(DeleteCertificateStatusEnumType.Failed)
expect(response.statusInfo).toBeDefined()
-/**
- * @file Tests for OCPP20IncomingRequestService GetBaseReport
- * @description Unit tests for OCPP 2.0 GetBaseReport command handling (B07)
- */
-/* eslint-disable @typescript-eslint/no-unsafe-member-access */
-/* eslint-disable @typescript-eslint/no-unsafe-assignment */
-/* eslint-disable @typescript-eslint/no-unsafe-call */
-/* eslint-disable @typescript-eslint/no-explicit-any */
import { expect } from '@std/expect'
import { afterEach, describe, it } from 'node:test'
addConfigurationKey,
setConfigurationKeyValue,
} from '../../../../src/charging-station/ConfigurationKeyUtils.js'
+/**
+ * @file Tests for OCPP20IncomingRequestService GetBaseReport
+ * @description Unit tests for OCPP 2.0 GetBaseReport command handling (B07)
+ */
+import { createTestableIncomingRequestService } from '../../../../src/charging-station/ocpp/2.0/__testable__/index.js'
import { OCPP20IncomingRequestService } from '../../../../src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.js'
import { OCPP20VariableManager } from '../../../../src/charging-station/ocpp/2.0/OCPP20VariableManager.js'
import {
const incomingRequestService = new OCPP20IncomingRequestService()
+ const testableService = createTestableIncomingRequestService(incomingRequestService)
+
// Reset singleton state after each test to ensure test isolation
afterEach(() => {
OCPP20VariableManager.getInstance().resetRuntimeOverrides()
requestId: 1,
}
- const response = (incomingRequestService as any).handleRequestGetBaseReport(
- mockChargingStation,
- request
- )
+ const response = testableService.handleRequestGetBaseReport(mockChargingStation, request)
expect(response).toBeDefined()
expect(response.status).toBe(GenericDeviceModelStatusEnumType.Accepted)
requestId: 2,
}
- const response = (incomingRequestService as any).handleRequestGetBaseReport(
- mockChargingStation,
- request
- )
+ const response = testableService.handleRequestGetBaseReport(mockChargingStation, request)
expect(response).toBeDefined()
expect(response.status).toBe(GenericDeviceModelStatusEnumType.Accepted)
})
await it('Should include registry variables with Actual attribute only for unsupported types', () => {
- const reportData: ReportDataType[] = (incomingRequestService as any).buildReportData(
- mockChargingStation,
- ReportBaseEnumType.FullInventory
- )
+ const reportData = testableService.buildReportData(mockChargingStation, ReportBaseEnumType.FullInventory)
const heartbeatEntry = reportData.find(
(item: ReportDataType) =>
item.variable.name === (OCPP20OptionalVariableName.HeartbeatInterval as string) &&
requestId: 3,
}
- const response = (incomingRequestService as any).handleRequestGetBaseReport(
- mockChargingStation,
- request
- )
+ const response = testableService.handleRequestGetBaseReport(mockChargingStation, request)
expect(response).toBeDefined()
expect(response.status).toBe(GenericDeviceModelStatusEnumType.Accepted)
requestId: 4,
}
- const response = (incomingRequestService as any).handleRequestGetBaseReport(
- mockChargingStation,
- request
- )
+ const response = testableService.handleRequestGetBaseReport(mockChargingStation, request)
expect(response).toBeDefined()
expect(response.status).toBe(GenericDeviceModelStatusEnumType.NotSupported)
requestId: 5,
}
- const response = (incomingRequestService as any).handleRequestGetBaseReport(
- minimalChargingStation,
- request
- )
+ const response = testableService.handleRequestGetBaseReport(mockChargingStation, request)
expect(response).toBeDefined()
expect(response.status).toBe(GenericDeviceModelStatusEnumType.EmptyResultSet)
// Test the buildReportData method indirectly by calling handleRequestGetBaseReport
// and checking if it returns Accepted status (which means data was built successfully)
- const response = (incomingRequestService as any).handleRequestGetBaseReport(
- mockChargingStation,
- request
- )
+ const response = testableService.handleRequestGetBaseReport(mockChargingStation, request)
expect(response).toBeDefined()
expect(response.status).toBe(GenericDeviceModelStatusEnumType.Accepted)
// We can also test the buildReportData method directly if needed
- const reportData: ReportDataType[] = (incomingRequestService as any).buildReportData(
+ const reportData = testableService.buildReportData(
mockChargingStation,
ReportBaseEnumType.ConfigurationInventory
)
// FR: B08.FR.07
await it('Should build correct report data for FullInventory with station info', () => {
- const reportData: ReportDataType[] = (incomingRequestService as any).buildReportData(
- mockChargingStation,
- ReportBaseEnumType.FullInventory
- )
+ const reportData = testableService.buildReportData(mockChargingStation, ReportBaseEnumType.FullInventory)
expect(Array.isArray(reportData)).toBe(true)
expect(reportData.length).toBeGreaterThan(0)
// FR: B08.FR.08
await it('Should build correct report data for SummaryInventory', () => {
- const reportData: ReportDataType[] = (incomingRequestService as any).buildReportData(
+ const reportData = testableService.buildReportData(
mockChargingStation,
ReportBaseEnumType.SummaryInventory
)
expect(setResult[0].attributeStatus).toBe('Accepted')
// Build report; value should be truncated to length 10
- const reportData: ReportDataType[] = (incomingRequestService as any).buildReportData(
- mockChargingStation,
- ReportBaseEnumType.FullInventory
- )
+ const reportData = testableService.buildReportData(mockChargingStation, ReportBaseEnumType.FullInventory)
const timeSourceEntry = reportData.find(
(item: ReportDataType) =>
item.variable.name === (OCPP20RequiredVariableName.TimeSource as string) &&
},
})
- const reportData: ReportDataType[] = (incomingRequestService as any).buildReportData(
- stationWithEvses,
- ReportBaseEnumType.FullInventory
- )
+ const reportData = testableService.buildReportData(stationWithEvses, ReportBaseEnumType.FullInventory)
expect(Array.isArray(reportData)).toBe(true)
expect(reportData.length).toBeGreaterThan(0)
// FR: B08.FR.10
await it('Should validate unsupported reportBase correctly', () => {
- const reportData: ReportDataType[] = (incomingRequestService as any).buildReportData(
+ const reportData = testableService.buildReportData(
mockChargingStation,
'InvalidReportBase' as unknown as ReportBaseEnumType
)
* @file Tests for OCPP20IncomingRequestService GetInstalledCertificateIds
* @description Unit tests for OCPP 2.0 GetInstalledCertificateIds command handling
*/
-/* eslint-disable @typescript-eslint/no-unsafe-member-access */
-/* eslint-disable @typescript-eslint/no-unsafe-assignment */
-/* eslint-disable @typescript-eslint/no-unsafe-call */
-/* eslint-disable @typescript-eslint/no-explicit-any */
import { expect } from '@std/expect'
import { describe, it, mock } from 'node:test'
+import type { ChargingStationWithCertificateManager } from '../../../../src/charging-station/ocpp/2.0/OCPP20CertificateManager.js'
+
+import { createTestableIncomingRequestService } from '../../../../src/charging-station/ocpp/2.0/__testable__/index.js'
import { OCPP20IncomingRequestService } from '../../../../src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.js'
import {
type CertificateHashDataChainType,
websocketPingInterval: Constants.DEFAULT_WEBSOCKET_PING_INTERVAL,
})
- ;(mockChargingStation as any).certificateManager = createMockCertificateManager()
+ // Cast to allow setting certificateManager property
+ const stationWithCertManager = mockChargingStation as unknown as ChargingStationWithCertificateManager
+ stationWithCertManager.certificateManager = createMockCertificateManager()
const incomingRequestService = new OCPP20IncomingRequestService()
+ const testableService = createTestableIncomingRequestService(incomingRequestService)
await describe('Request All Certificate Types', async () => {
await it('Should return all certificates when no filter is provided', async () => {
createMockCertificateHashDataChain(GetCertificateIdUseEnumType.CSMSRootCertificate, '333'),
]
- ;(mockChargingStation as any).certificateManager = createMockCertificateManager({
+ stationWithCertManager.certificateManager = createMockCertificateManager({
getInstalledCertificatesResult: mockCerts,
})
const request: OCPP20GetInstalledCertificateIdsRequest = {}
- const response: OCPP20GetInstalledCertificateIdsResponse = await (
- incomingRequestService as any
- ).handleRequestGetInstalledCertificateIds(mockChargingStation, request)
+ const response: OCPP20GetInstalledCertificateIdsResponse =
+ await testableService.handleRequestGetInstalledCertificateIds(mockChargingStation, request)
expect(response).toBeDefined()
expect(response.status).toBe(GetInstalledCertificateStatusEnumType.Accepted)
'111'
)
- ;(mockChargingStation as any).certificateManager = createMockCertificateManager({
+ stationWithCertManager.certificateManager = createMockCertificateManager({
getInstalledCertificatesResult: [v2gCert],
})
certificateType: [GetCertificateIdUseEnumType.V2GRootCertificate],
}
- const response: OCPP20GetInstalledCertificateIdsResponse = await (
- incomingRequestService as any
- ).handleRequestGetInstalledCertificateIds(mockChargingStation, request)
+ const response: OCPP20GetInstalledCertificateIdsResponse =
+ await testableService.handleRequestGetInstalledCertificateIds(mockChargingStation, request)
expect(response).toBeDefined()
expect(response.status).toBe(GetInstalledCertificateStatusEnumType.Accepted)
createMockCertificateHashDataChain(GetCertificateIdUseEnumType.CSMSRootCertificate, '222'),
]
- ;(mockChargingStation as any).certificateManager = createMockCertificateManager({
+ stationWithCertManager.certificateManager = createMockCertificateManager({
getInstalledCertificatesResult: mockCerts,
})
],
}
- const response: OCPP20GetInstalledCertificateIdsResponse = await (
- incomingRequestService as any
- ).handleRequestGetInstalledCertificateIds(mockChargingStation, request)
+ const response: OCPP20GetInstalledCertificateIdsResponse =
+ await testableService.handleRequestGetInstalledCertificateIds(mockChargingStation, request)
expect(response).toBeDefined()
expect(response.status).toBe(GetInstalledCertificateStatusEnumType.Accepted)
await describe('No Certificates Found', async () => {
await it('Should return Accepted with empty array when no certificates found', async () => {
- ;(mockChargingStation as any).certificateManager = createMockCertificateManager({
+ stationWithCertManager.certificateManager = createMockCertificateManager({
getInstalledCertificatesResult: [],
})
const request: OCPP20GetInstalledCertificateIdsRequest = {}
- const response: OCPP20GetInstalledCertificateIdsResponse = await (
- incomingRequestService as any
- ).handleRequestGetInstalledCertificateIds(mockChargingStation, request)
+ const response: OCPP20GetInstalledCertificateIdsResponse =
+ await testableService.handleRequestGetInstalledCertificateIds(mockChargingStation, request)
expect(response).toBeDefined()
// Per OCPP 2.0.1 spec: NotFound is returned when no certificates match the request
})
await it('Should return NotFound when filtered type has no certificates', async () => {
- ;(mockChargingStation as any).certificateManager = createMockCertificateManager({
+ stationWithCertManager.certificateManager = createMockCertificateManager({
getInstalledCertificatesResult: [],
})
certificateType: [GetCertificateIdUseEnumType.ManufacturerRootCertificate],
}
- const response: OCPP20GetInstalledCertificateIdsResponse = await (
- incomingRequestService as any
- ).handleRequestGetInstalledCertificateIds(mockChargingStation, request)
+ const response: OCPP20GetInstalledCertificateIdsResponse =
+ await testableService.handleRequestGetInstalledCertificateIds(mockChargingStation, request)
expect(response).toBeDefined()
expect(response.status).toBe(GetInstalledCertificateStatusEnumType.NotFound)
await describe('Response Structure Validation', async () => {
await it('Should return response with required status field', async () => {
- ;(mockChargingStation as any).certificateManager = createMockCertificateManager({
+ stationWithCertManager.certificateManager = createMockCertificateManager({
getInstalledCertificatesResult: [],
})
const request: OCPP20GetInstalledCertificateIdsRequest = {}
- const response: OCPP20GetInstalledCertificateIdsResponse = await (
- incomingRequestService as any
- ).handleRequestGetInstalledCertificateIds(mockChargingStation, request)
+ const response: OCPP20GetInstalledCertificateIdsResponse =
+ await testableService.handleRequestGetInstalledCertificateIds(mockChargingStation, request)
expect(response).toBeDefined()
expect(typeof response).toBe('object')
'123456'
)
- ;(mockChargingStation as any).certificateManager = createMockCertificateManager({
+ stationWithCertManager.certificateManager = createMockCertificateManager({
getInstalledCertificatesResult: [mockCert],
})
const request: OCPP20GetInstalledCertificateIdsRequest = {}
- const response: OCPP20GetInstalledCertificateIdsResponse = await (
- incomingRequestService as any
- ).handleRequestGetInstalledCertificateIds(mockChargingStation, request)
+ const response: OCPP20GetInstalledCertificateIdsResponse =
+ await testableService.handleRequestGetInstalledCertificateIds(mockChargingStation, request)
expect(response.status).toBe(GetInstalledCertificateStatusEnumType.Accepted)
expect(response.certificateHashDataChain).toBeDefined()
})
// Explicitly set to null/undefined
- ;(stationWithoutCertManager as any).certificateManager = null
+ const stationNoCertManager = stationWithoutCertManager as unknown as ChargingStationWithCertificateManager
+ stationNoCertManager.certificateManager = null as unknown as ChargingStationWithCertificateManager['certificateManager']
const request: OCPP20GetInstalledCertificateIdsRequest = {}
- const response: OCPP20GetInstalledCertificateIdsResponse = await (
- incomingRequestService as any
- ).handleRequestGetInstalledCertificateIds(stationWithoutCertManager, request)
+ const response: OCPP20GetInstalledCertificateIdsResponse =
+ await testableService.handleRequestGetInstalledCertificateIds(stationWithoutCertManager, request)
expect(response).toBeDefined()
expect(response.status).toBe(GetInstalledCertificateStatusEnumType.NotFound)
* @file Tests for OCPP20IncomingRequestService InstallCertificate
* @description Unit tests for OCPP 2.0 InstallCertificate command handling
*/
-/* eslint-disable @typescript-eslint/no-unsafe-member-access */
-/* eslint-disable @typescript-eslint/no-unsafe-assignment */
-/* eslint-disable @typescript-eslint/no-unsafe-call */
-/* eslint-disable @typescript-eslint/no-explicit-any */
import { expect } from '@std/expect'
import { describe, it, mock } from 'node:test'
+import type { ChargingStationWithCertificateManager } from '../../../../src/charging-station/ocpp/2.0/OCPP20CertificateManager.js'
+
+import { createTestableIncomingRequestService } from '../../../../src/charging-station/ocpp/2.0/__testable__/index.js'
import { OCPP20IncomingRequestService } from '../../../../src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.js'
import {
InstallCertificateStatusEnumType,
websocketPingInterval: Constants.DEFAULT_WEBSOCKET_PING_INTERVAL,
})
- ;(mockChargingStation as any).certificateManager = createMockCertificateManager()
+ // Cast to allow setting certificateManager property
+ const stationWithCertManager = mockChargingStation as unknown as ChargingStationWithCertificateManager
+ stationWithCertManager.certificateManager = createMockCertificateManager()
const incomingRequestService = new OCPP20IncomingRequestService()
+ const testableService = createTestableIncomingRequestService(incomingRequestService)
await describe('Valid Certificate Installation', async () => {
await it('Should accept valid V2GRootCertificate', async () => {
- ;(mockChargingStation as any).certificateManager = createMockCertificateManager({
+ stationWithCertManager.certificateManager = createMockCertificateManager({
storeCertificateResult: true,
})
certificateType: InstallCertificateUseEnumType.V2GRootCertificate,
}
- const response: OCPP20InstallCertificateResponse = await (
- incomingRequestService as any
- ).handleRequestInstallCertificate(mockChargingStation, request)
+ const response: OCPP20InstallCertificateResponse =
+ await testableService.handleRequestInstallCertificate(mockChargingStation, request)
expect(response).toBeDefined()
expect(typeof response).toBe('object')
})
await it('Should accept valid MORootCertificate', async () => {
- ;(mockChargingStation as any).certificateManager = createMockCertificateManager({
+ stationWithCertManager.certificateManager = createMockCertificateManager({
storeCertificateResult: true,
})
certificateType: InstallCertificateUseEnumType.MORootCertificate,
}
- const response: OCPP20InstallCertificateResponse = await (
- incomingRequestService as any
- ).handleRequestInstallCertificate(mockChargingStation, request)
+ const response: OCPP20InstallCertificateResponse =
+ await testableService.handleRequestInstallCertificate(mockChargingStation, request)
expect(response).toBeDefined()
expect(response.status).toBe(InstallCertificateStatusEnumType.Accepted)
})
await it('Should accept valid CSMSRootCertificate', async () => {
- ;(mockChargingStation as any).certificateManager = createMockCertificateManager({
+ stationWithCertManager.certificateManager = createMockCertificateManager({
storeCertificateResult: true,
})
certificateType: InstallCertificateUseEnumType.CSMSRootCertificate,
}
- const response: OCPP20InstallCertificateResponse = await (
- incomingRequestService as any
- ).handleRequestInstallCertificate(mockChargingStation, request)
+ const response: OCPP20InstallCertificateResponse =
+ await testableService.handleRequestInstallCertificate(mockChargingStation, request)
expect(response).toBeDefined()
expect(response.status).toBe(InstallCertificateStatusEnumType.Accepted)
})
await it('Should accept valid ManufacturerRootCertificate', async () => {
- ;(mockChargingStation as any).certificateManager = createMockCertificateManager({
+ stationWithCertManager.certificateManager = createMockCertificateManager({
storeCertificateResult: true,
})
certificateType: InstallCertificateUseEnumType.ManufacturerRootCertificate,
}
- const response: OCPP20InstallCertificateResponse = await (
- incomingRequestService as any
- ).handleRequestInstallCertificate(mockChargingStation, request)
+ const response: OCPP20InstallCertificateResponse =
+ await testableService.handleRequestInstallCertificate(mockChargingStation, request)
expect(response).toBeDefined()
expect(response.status).toBe(InstallCertificateStatusEnumType.Accepted)
certificateType: InstallCertificateUseEnumType.V2GRootCertificate,
}
- const response: OCPP20InstallCertificateResponse = await (
- incomingRequestService as any
- ).handleRequestInstallCertificate(mockChargingStation, request)
+ const response: OCPP20InstallCertificateResponse =
+ await testableService.handleRequestInstallCertificate(mockChargingStation, request)
expect(response).toBeDefined()
expect(response.status).toBe(InstallCertificateStatusEnumType.Rejected)
})
await it('Should reject expired certificate when validation is enabled', async () => {
- ;(mockChargingStation as any).certificateManager = createMockCertificateManager({
+ stationWithCertManager.certificateManager = createMockCertificateManager({
storeCertificateResult: false,
})
- ;(mockChargingStation as any).stationInfo.validateCertificateExpiry = true
+ mockChargingStation.stationInfo!.validateCertificateExpiry = true
const request: OCPP20InstallCertificateRequest = {
certificate: EXPIRED_PEM_CERTIFICATE,
certificateType: InstallCertificateUseEnumType.CSMSRootCertificate,
}
- const response: OCPP20InstallCertificateResponse = await (
- incomingRequestService as any
- ).handleRequestInstallCertificate(mockChargingStation, request)
+ const response: OCPP20InstallCertificateResponse =
+ await testableService.handleRequestInstallCertificate(mockChargingStation, request)
expect(response).toBeDefined()
expect(response.status).toBe(InstallCertificateStatusEnumType.Rejected)
expect(response.statusInfo).toBeDefined()
expect(response.statusInfo?.reasonCode).toBeDefined()
- delete (mockChargingStation as any).stationInfo.validateCertificateExpiry
+ delete mockChargingStation.stationInfo!.validateCertificateExpiry
})
})
await describe('Storage Failure Handling', async () => {
await it('Should return Failed status when storage is full', async () => {
- ;(mockChargingStation as any).certificateManager = createMockCertificateManager({
+ stationWithCertManager.certificateManager = createMockCertificateManager({
storeCertificateError: new Error('Storage full'),
})
certificateType: InstallCertificateUseEnumType.MORootCertificate,
}
- const response: OCPP20InstallCertificateResponse = await (
- incomingRequestService as any
- ).handleRequestInstallCertificate(mockChargingStation, request)
+ const response: OCPP20InstallCertificateResponse =
+ await testableService.handleRequestInstallCertificate(mockChargingStation, request)
expect(response).toBeDefined()
expect(response.status).toBe(InstallCertificateStatusEnumType.Failed)
await describe('Response Structure Validation', async () => {
await it('Should return response matching InstallCertificateResponse schema', async () => {
- ;(mockChargingStation as any).certificateManager = createMockCertificateManager({
+ stationWithCertManager.certificateManager = createMockCertificateManager({
storeCertificateResult: true,
})
certificateType: InstallCertificateUseEnumType.V2GRootCertificate,
}
- const response: OCPP20InstallCertificateResponse = await (
- incomingRequestService as any
- ).handleRequestInstallCertificate(mockChargingStation, request)
+ const response: OCPP20InstallCertificateResponse =
+ await testableService.handleRequestInstallCertificate(mockChargingStation, request)
expect(response).toBeDefined()
expect(typeof response).toBe('object')
certificateType: InstallCertificateUseEnumType.V2GRootCertificate,
}
- const response: OCPP20InstallCertificateResponse = await (
- incomingRequestService as any
- ).handleRequestInstallCertificate(mockChargingStation, request)
+ const response: OCPP20InstallCertificateResponse =
+ await testableService.handleRequestInstallCertificate(mockChargingStation, request)
expect(response.status).toBe(InstallCertificateStatusEnumType.Rejected)
expect(response.statusInfo).toBeDefined()
-/**
- * @file Tests for OCPP20IncomingRequestService RequestStartTransaction
- * @description Unit tests for OCPP 2.0 RequestStartTransaction command handling (F01/F02)
- */
-/* eslint-disable @typescript-eslint/no-unsafe-member-access */
-/* eslint-disable @typescript-eslint/no-unsafe-assignment */
-/* eslint-disable @typescript-eslint/no-unsafe-call */
-/* eslint-disable @typescript-eslint/no-explicit-any */
-
import { expect } from '@std/expect'
import { afterEach, beforeEach, describe, it } from 'node:test'
import type { OCPP20RequestStartTransactionRequest } from '../../../../src/types/index.js'
-import type { OCPP20ChargingProfileType } from '../../../../src/types/ocpp/2.0/Transaction.js'
+import type { OCPP20ChargingProfileType, OCPP20ChargingRateUnitType } from '../../../../src/types/ocpp/2.0/Transaction.js'
+/**
+ * @file Tests for OCPP20IncomingRequestService RequestStartTransaction
+ * @description Unit tests for OCPP 2.0 RequestStartTransaction command handling (F01/F02)
+ */
+import { createTestableIncomingRequestService } from '../../../../src/charging-station/ocpp/2.0/__testable__/index.js'
import { OCPP20IncomingRequestService } from '../../../../src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.js'
import { OCPPAuthServiceFactory } from '../../../../src/charging-station/ocpp/auth/services/OCPPAuthServiceFactory.js'
import { OCPPVersion, RequestStartStopStatusEnumType } from '../../../../src/types/index.js'
} from '../../../../src/types/ocpp/2.0/Transaction.js'
import { Constants } from '../../../../src/utils/index.js'
import { createChargingStation } from '../../../ChargingStationFactory.js'
-import { createMockAuthService } from '../auth/helpers/MockFactories.js'
import { TEST_CHARGING_STATION_BASE_NAME } from '../../ChargingStationTestConstants.js'
+import { createMockAuthService } from '../auth/helpers/MockFactories.js'
import {
resetConnectorTransactionState,
resetLimits,
})
const incomingRequestService = new OCPP20IncomingRequestService()
+ const testableService = createTestableIncomingRequestService(incomingRequestService)
beforeEach(() => {
const stationId = mockChargingStation.stationInfo?.chargingStationId ?? 'unknown'
remoteStartId: 1,
}
- const response = await (incomingRequestService as any).handleRequestStartTransaction(
+ const response = await testableService.handleRequestStartTransaction(
mockChargingStation,
validRequest
)
remoteStartId: 42,
}
- const response = await (incomingRequestService as any).handleRequestStartTransaction(
+ const response = await testableService.handleRequestStartTransaction(
spyChargingStation,
requestWithRemoteStartId
)
remoteStartId: 3,
}
- const response = await (incomingRequestService as any).handleRequestStartTransaction(
+ const response = await testableService.handleRequestStartTransaction(
mockChargingStation,
requestWithGroupToken
)
chargingProfilePurpose: OCPP20ChargingProfilePurposeEnumType.TxProfile,
chargingSchedule: [
{
- chargingRateUnit: 'A' as any,
+ chargingRateUnit: 'A' as OCPP20ChargingRateUnitType,
chargingSchedulePeriod: [
{
limit: 30,
remoteStartId: 301,
}
- const response = await (incomingRequestService as any).handleRequestStartTransaction(
+ const response = await testableService.handleRequestStartTransaction(
mockChargingStation,
requestWithValidProfile
)
chargingProfilePurpose: OCPP20ChargingProfilePurposeEnumType.TxDefaultProfile,
chargingSchedule: [
{
- chargingRateUnit: 'A' as any,
+ chargingRateUnit: 'A' as OCPP20ChargingRateUnitType,
chargingSchedulePeriod: [
{
limit: 25,
remoteStartId: 302,
}
- const response = await (incomingRequestService as any).handleRequestStartTransaction(
+ const response = await testableService.handleRequestStartTransaction(
mockChargingStation,
requestWithInvalidProfile
)
chargingProfilePurpose: OCPP20ChargingProfilePurposeEnumType.TxProfile,
chargingSchedule: [
{
- chargingRateUnit: 'A' as any,
+ chargingRateUnit: 'A' as OCPP20ChargingRateUnitType,
chargingSchedulePeriod: [
{
limit: 32,
remoteStartId: 303,
}
- const response = await (incomingRequestService as any).handleRequestStartTransaction(
+ const response = await testableService.handleRequestStartTransaction(
mockChargingStation,
requestWithTransactionIdProfile
)
// Should throw OCPPError for invalid evseId
await expect(
- (incomingRequestService as any).handleRequestStartTransaction(
- mockChargingStation,
- invalidEvseRequest
- )
+ testableService.handleRequestStartTransaction(mockChargingStation, invalidEvseRequest)
).rejects.toThrow('EVSE 999 does not exist on charging station')
})
remoteStartId: 100,
}
- await (incomingRequestService as any).handleRequestStartTransaction(
- mockChargingStation,
- firstRequest
- )
+ await testableService.handleRequestStartTransaction(mockChargingStation, firstRequest)
// Now try to start another transaction on the same EVSE
const secondRequest: OCPP20RequestStartTransactionRequest = {
remoteStartId: 101,
}
- const response = await (incomingRequestService as any).handleRequestStartTransaction(
+ const response = await testableService.handleRequestStartTransaction(
mockChargingStation,
secondRequest
)
remoteStartId: 200,
}
- const response = await (incomingRequestService as any).handleRequestStartTransaction(
+ const response = await testableService.handleRequestStartTransaction(
mockChargingStation,
validRequest
)
* @file Tests for OCPP20IncomingRequestService RequestStopTransaction
* @description Unit tests for OCPP 2.0 RequestStopTransaction command handling (F03)
*/
-/* eslint-disable @typescript-eslint/no-unsafe-member-access */
-/* eslint-disable @typescript-eslint/no-unsafe-assignment */
-/* eslint-disable @typescript-eslint/no-unsafe-call */
-/* eslint-disable @typescript-eslint/no-explicit-any */
import { expect } from '@std/expect'
import { afterEach, beforeEach, describe, it } from 'node:test'
UUIDv4,
} from '../../../../src/types/index.js'
+import { createTestableIncomingRequestService } from '../../../../src/charging-station/ocpp/2.0/__testable__/index.js'
import { OCPP20IncomingRequestService } from '../../../../src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.js'
import { OCPPAuthServiceFactory } from '../../../../src/charging-station/ocpp/auth/services/OCPPAuthServiceFactory.js'
import {
} from '../../../../src/types/ocpp/2.0/Transaction.js'
import { Constants } from '../../../../src/utils/index.js'
import { createChargingStation } from '../../../ChargingStationFactory.js'
-import { createMockAuthService } from '../auth/helpers/MockFactories.js'
import { TEST_CHARGING_STATION_BASE_NAME } from '../../ChargingStationTestConstants.js'
+import { createMockAuthService } from '../auth/helpers/MockFactories.js'
import { resetLimits, resetReportingValueSize } from './OCPP20TestUtils.js'
await describe('F03 - Remote Stop Transaction', async () => {
})
const incomingRequestService = new OCPP20IncomingRequestService()
+ const testableService = createTestableIncomingRequestService(incomingRequestService)
beforeEach(() => {
const stationId = mockChargingStation.stationInfo?.chargingStationId ?? 'unknown'
remoteStartId,
}
- const startResponse = await (incomingRequestService as any).handleRequestStartTransaction(
+ const startResponse = await testableService.handleRequestStartTransaction(
mockChargingStation,
startRequest
)
}
// Execute stop transaction
- const response = await (incomingRequestService as any).handleRequestStopTransaction(
+ const response = await testableService.handleRequestStopTransaction(
mockChargingStation,
stopRequest
)
transactionId: transactionId2 as UUIDv4,
}
- const response = await (incomingRequestService as any).handleRequestStopTransaction(
+ const response = await testableService.handleRequestStopTransaction(
mockChargingStation,
stopRequest
)
transactionId: nonExistentTransactionId as UUIDv4,
}
- const response = await (incomingRequestService as any).handleRequestStopTransaction(
+ const response = await testableService.handleRequestStopTransaction(
mockChargingStation,
stopRequest
)
transactionId: '' as UUIDv4,
}
- const response = await (incomingRequestService as any).handleRequestStopTransaction(
+ const response = await testableService.handleRequestStopTransaction(
mockChargingStation,
invalidRequest
)
transactionId: tooLongTransactionId as UUIDv4,
}
- const response = await (incomingRequestService as any).handleRequestStopTransaction(
+ const response = await testableService.handleRequestStopTransaction(
mockChargingStation,
invalidRequest
)
transactionId: testTransactionId as UUIDv4,
}
- const response = await (incomingRequestService as any).handleRequestStopTransaction(
+ const response = await testableService.handleRequestStopTransaction(
mockChargingStation,
stopRequest
)
remoteStartId: 999,
}
- const startResponse = await (incomingRequestService as any).handleRequestStartTransaction(
+ const startResponse = await testableService.handleRequestStartTransaction(
failingChargingStation,
startRequest
)
transactionId: transactionId as UUIDv4,
}
- const response = await (incomingRequestService as any).handleRequestStopTransaction(
+ const response = await testableService.handleRequestStopTransaction(
failingChargingStation,
stopRequest
)
transactionId: transactionId as UUIDv4,
}
- const response = await (incomingRequestService as any).handleRequestStopTransaction(
+ const response = await testableService.handleRequestStopTransaction(
mockChargingStation,
stopRequest
)
transactionId: transactionId as UUIDv4,
}
- const response = await (incomingRequestService as any).handleRequestStopTransaction(
+ const response = await testableService.handleRequestStopTransaction(
mockChargingStation,
stopRequestWithCustomData
)
transactionId: transactionId as UUIDv4,
}
- const response = await (incomingRequestService as any).handleRequestStopTransaction(
+ const response = await testableService.handleRequestStopTransaction(
mockChargingStation,
stopRequest
)
transactionId: transactionId as UUIDv4,
}
- const response = await (incomingRequestService as any).handleRequestStopTransaction(
+ const response = await testableService.handleRequestStopTransaction(
mockChargingStation,
stopRequest
)
* @file Tests for OCPP20IncomingRequestService Reset
* @description Unit tests for OCPP 2.0 Reset command handling (B11/B12)
*/
-/* eslint-disable @typescript-eslint/no-unsafe-member-access */
-/* eslint-disable @typescript-eslint/no-unsafe-assignment */
-/* eslint-disable @typescript-eslint/no-unsafe-call */
-/* eslint-disable @typescript-eslint/no-explicit-any */
import { expect } from '@std/expect'
import { afterEach, beforeEach, describe, it, mock } from 'node:test'
-import { OCPP20IncomingRequestService } from '../../../../src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.js'
-import {
+import type { ChargingStation, EvseStatus } from '../../../../src/charging-station/index.js'
+import type {
FirmwareStatus,
type OCPP20ResetRequest,
type OCPP20ResetResponse,
OCPPVersion,
ReasonCodeEnumType,
+ Reservation,
ResetEnumType,
- ResetStatusEnumType,
+ ResetStatusEnumType
} from '../../../../src/types/index.js'
+
+import { createTestableIncomingRequestService } from '../../../../src/charging-station/ocpp/2.0/__testable__/index.js'
+import { OCPP20IncomingRequestService } from '../../../../src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.js'
import { Constants } from '../../../../src/utils/index.js'
import { createChargingStation } from '../../../ChargingStationFactory.js'
import { TEST_CHARGING_STATION_BASE_NAME } from '../../ChargingStationTestConstants.js'
websocketPingInterval: Constants.DEFAULT_WEBSOCKET_PING_INTERVAL,
})
- // Add missing method to mock
- ;(mockChargingStation as any).getNumberOfRunningTransactions = () => 0
- ;(mockChargingStation as any).reset = () => Promise.resolve()
+ // Add missing method to mock using interface extension pattern
+ interface MockChargingStation extends ChargingStation {
+ getNumberOfRunningTransactions: () => number
+ reset: () => Promise<void>
+ }
+ const mockStation = mockChargingStation as MockChargingStation
+ mockStation.getNumberOfRunningTransactions = () => 0
+ mockStation.reset = () => Promise.resolve()
const incomingRequestService = new OCPP20IncomingRequestService()
+ const testableService = createTestableIncomingRequestService(incomingRequestService)
await describe('B11 - Reset - Without Ongoing Transaction', async () => {
// FR: B11.FR.01
type: ResetEnumType.Immediate,
}
- const response: OCPP20ResetResponse = await (
- incomingRequestService as any
- ).handleRequestReset(mockChargingStation, resetRequest)
+ const response: OCPP20ResetResponse = await testableService.handleRequestReset(
+ mockChargingStation,
+ resetRequest
+ )
expect(response).toBeDefined()
expect(typeof response).toBe('object')
type: ResetEnumType.OnIdle,
}
- const response: OCPP20ResetResponse = await (
- incomingRequestService as any
- ).handleRequestReset(mockChargingStation, resetRequest)
+ const response: OCPP20ResetResponse = await testableService.handleRequestReset(
+ mockChargingStation,
+ resetRequest
+ )
expect(response).toBeDefined()
expect(response.status).toBeDefined()
type: ResetEnumType.Immediate,
}
- const response: OCPP20ResetResponse = await (
- incomingRequestService as any
- ).handleRequestReset(mockChargingStation, resetRequest)
+ const response: OCPP20ResetResponse = await testableService.handleRequestReset(
+ mockChargingStation,
+ resetRequest
+ )
expect(response).toBeDefined()
expect(response.status).toBeDefined()
type: ResetEnumType.Immediate,
}
- const response: OCPP20ResetResponse = await (
- incomingRequestService as any
- ).handleRequestReset(mockChargingStation, resetRequest)
+ const response: OCPP20ResetResponse = await testableService.handleRequestReset(
+ mockChargingStation,
+ resetRequest
+ )
expect(response).toBeDefined()
expect(response.status).toBe(ResetStatusEnumType.Rejected)
type: ResetEnumType.Immediate,
}
- const response: OCPP20ResetResponse = await (
- incomingRequestService as any
- ).handleRequestReset(mockChargingStation, resetRequest)
+ const response: OCPP20ResetResponse = await testableService.handleRequestReset(
+ mockChargingStation,
+ resetRequest
+ )
expect(response).toBeDefined()
expect(response.status).toBeDefined()
expect(typeof response.status).toBe('string')
// For immediate reset without active transactions, should be accepted
- if (mockChargingStation.getNumberOfRunningTransactions() === 0) {
+ if (mockStation.getNumberOfRunningTransactions() === 0) {
expect(response.status).toBe(ResetStatusEnumType.Accepted)
}
})
type: ResetEnumType.OnIdle,
}
- const response: OCPP20ResetResponse = await (
- incomingRequestService as any
- ).handleRequestReset(mockChargingStation, resetRequest)
+ const response: OCPP20ResetResponse = await testableService.handleRequestReset(
+ mockChargingStation,
+ resetRequest
+ )
expect(response).toBeDefined()
expect(response.status).toBe(ResetStatusEnumType.Accepted)
await it('Should reject EVSE reset when not supported and no transactions', async () => {
// Mock charging station without EVSE support
const originalHasEvses = mockChargingStation.hasEvses
- ;(mockChargingStation as any).hasEvses = false
+ ;(mockChargingStation as { hasEvses: boolean }).hasEvses = false
const resetRequest: OCPP20ResetRequest = {
evseId: 1,
type: ResetEnumType.Immediate,
}
- const response: OCPP20ResetResponse = await (
- incomingRequestService as any
- ).handleRequestReset(mockChargingStation, resetRequest)
+ const response: OCPP20ResetResponse = await testableService.handleRequestReset(
+ mockChargingStation,
+ resetRequest
+ )
expect(response).toBeDefined()
expect(response.status).toBe(ResetStatusEnumType.Rejected)
)
// Restore original state
- ;(mockChargingStation as any).hasEvses = originalHasEvses
+ ;(mockChargingStation as { hasEvses: boolean }).hasEvses = originalHasEvses
})
await it('Should handle EVSE-specific reset without transactions', async () => {
type: ResetEnumType.Immediate,
}
- const response: OCPP20ResetResponse = await (
- incomingRequestService as any
- ).handleRequestReset(mockChargingStation, resetRequest)
+ const response: OCPP20ResetResponse = await testableService.handleRequestReset(
+ mockChargingStation,
+ resetRequest
+ )
expect(response).toBeDefined()
expect(response.status).toBe(ResetStatusEnumType.Accepted)
// FR: B12.FR.02
await it('Should handle immediate reset with active transactions', async () => {
// Mock active transactions
- ;(mockChargingStation as any).getNumberOfRunningTransactions = () => 1
+ mockStation.getNumberOfRunningTransactions = () => 1
const resetRequest: OCPP20ResetRequest = {
type: ResetEnumType.Immediate,
}
- const response: OCPP20ResetResponse = await (
- incomingRequestService as any
- ).handleRequestReset(mockChargingStation, resetRequest)
+ const response: OCPP20ResetResponse = await testableService.handleRequestReset(
+ mockChargingStation,
+ resetRequest
+ )
expect(response).toBeDefined()
expect(response.status).toBe(ResetStatusEnumType.Accepted) // Should accept immediate reset
expect(response.statusInfo).toBeUndefined()
// Reset mock
- ;(mockChargingStation as any).getNumberOfRunningTransactions = () => 0
+ mockStation.getNumberOfRunningTransactions = () => 0
})
// FR: B12.FR.01
await it('Should handle OnIdle reset with active transactions', async () => {
// Mock active transactions
- ;(mockChargingStation as any).getNumberOfRunningTransactions = () => 1
+ mockStation.getNumberOfRunningTransactions = () => 1
const resetRequest: OCPP20ResetRequest = {
type: ResetEnumType.OnIdle,
}
- const response: OCPP20ResetResponse = await (
- incomingRequestService as any
- ).handleRequestReset(mockChargingStation, resetRequest)
+ const response: OCPP20ResetResponse = await testableService.handleRequestReset(
+ mockChargingStation,
+ resetRequest
+ )
expect(response).toBeDefined()
expect(response.status).toBe(ResetStatusEnumType.Scheduled) // Should schedule OnIdle reset
expect(response.statusInfo).toBeUndefined()
// Reset mock
- ;(mockChargingStation as any).getNumberOfRunningTransactions = () => 0
+ mockStation.getNumberOfRunningTransactions = () => 0
})
// FR: B12.FR.03
await it('Should handle EVSE-specific reset with active transactions', async () => {
// Mock active transactions
- ;(mockChargingStation as any).getNumberOfRunningTransactions = () => 1
+ mockStation.getNumberOfRunningTransactions = () => 1
const resetRequest: OCPP20ResetRequest = {
evseId: 1,
type: ResetEnumType.Immediate,
}
- const response: OCPP20ResetResponse = await (
- incomingRequestService as any
- ).handleRequestReset(mockChargingStation, resetRequest)
+ const response: OCPP20ResetResponse = await testableService.handleRequestReset(
+ mockChargingStation,
+ resetRequest
+ )
expect(response).toBeDefined()
expect(response.status).toBeDefined()
)
// Reset mock
- ;(mockChargingStation as any).getNumberOfRunningTransactions = () => 0
+ mockStation.getNumberOfRunningTransactions = () => 0
})
await it('Should reject EVSE reset when not supported with active transactions', async () => {
// Mock charging station without EVSE support and active transactions
const originalHasEvses = mockChargingStation.hasEvses
- ;(mockChargingStation as any).hasEvses = false
- ;(mockChargingStation as any).getNumberOfRunningTransactions = () => 1
+ ;(mockChargingStation as { hasEvses: boolean }).hasEvses = false
+ mockStation.getNumberOfRunningTransactions = () => 1
const resetRequest: OCPP20ResetRequest = {
evseId: 1,
type: ResetEnumType.Immediate,
}
- const response: OCPP20ResetResponse = await (
- incomingRequestService as any
- ).handleRequestReset(mockChargingStation, resetRequest)
+ const response: OCPP20ResetResponse = await testableService.handleRequestReset(
+ mockChargingStation,
+ resetRequest
+ )
expect(response).toBeDefined()
expect(response.status).toBe(ResetStatusEnumType.Rejected)
)
// Restore original state
- ;(mockChargingStation as any).hasEvses = originalHasEvses
- ;(mockChargingStation as any).getNumberOfRunningTransactions = () => 0
+ ;(mockChargingStation as { hasEvses: boolean }).hasEvses = originalHasEvses
+ mockStation.getNumberOfRunningTransactions = () => 0
})
// RST-001: Reset OnIdle Errata 2.14 Compliance Tests
// in addition to active transactions, per OCPP 2.0.1 Errata 2.14.
await describe('RST-001 - Reset OnIdle Errata 2.14 Compliance', async () => {
// Create a separate charging station for RST-001 tests with clean state
- const createTestStation = () => {
+ interface TestStation extends ChargingStation {
+ getNumberOfRunningTransactions: () => number
+ reset: () => Promise<void>
+ }
+
+ const createTestStation = (): TestStation => {
const station = createChargingStation({
baseName: TEST_CHARGING_STATION_BASE_NAME,
connectorsCount: 3,
websocketPingInterval: Constants.DEFAULT_WEBSOCKET_PING_INTERVAL,
})
// Add required methods
- ;(station as any).getNumberOfRunningTransactions = () => 0
- ;(station as any).reset = () => Promise.resolve()
- return station
+ const testStation = station as TestStation
+ testStation.getNumberOfRunningTransactions = () => 0
+ testStation.reset = () => Promise.resolve()
+ return testStation
}
await describe('Firmware Update Blocking', async () => {
await it('Should return Scheduled when firmware is Downloading', async () => {
const station = createTestStation()
// Mock firmware status as Downloading
- ;(station as any).stationInfo = {
- ...station.stationInfo,
+ Object.assign(station.stationInfo, {
firmwareStatus: FirmwareStatus.Downloading,
- }
+ })
const resetRequest: OCPP20ResetRequest = {
type: ResetEnumType.OnIdle,
}
- const response: OCPP20ResetResponse = await (
- incomingRequestService as any
- ).handleRequestReset(station, resetRequest)
+ const response: OCPP20ResetResponse = await testableService.handleRequestReset(
+ station,
+ resetRequest
+ )
expect(response).toBeDefined()
expect(response.status).toBe(ResetStatusEnumType.Scheduled)
await it('Should return Scheduled when firmware is Downloaded', async () => {
const station = createTestStation()
// Mock firmware status as Downloaded (waiting to install)
- ;(station as any).stationInfo = {
- ...station.stationInfo,
+ Object.assign(station.stationInfo, {
firmwareStatus: FirmwareStatus.Downloaded,
- }
+ })
const resetRequest: OCPP20ResetRequest = {
type: ResetEnumType.OnIdle,
}
- const response: OCPP20ResetResponse = await (
- incomingRequestService as any
- ).handleRequestReset(station, resetRequest)
+ const response: OCPP20ResetResponse = await testableService.handleRequestReset(
+ station,
+ resetRequest
+ )
expect(response).toBeDefined()
expect(response.status).toBe(ResetStatusEnumType.Scheduled)
await it('Should return Scheduled when firmware is Installing', async () => {
const station = createTestStation()
// Mock firmware status as Installing
- ;(station as any).stationInfo = {
- ...station.stationInfo,
+ Object.assign(station.stationInfo, {
firmwareStatus: FirmwareStatus.Installing,
- }
+ })
const resetRequest: OCPP20ResetRequest = {
type: ResetEnumType.OnIdle,
}
- const response: OCPP20ResetResponse = await (
- incomingRequestService as any
- ).handleRequestReset(station, resetRequest)
+ const response: OCPP20ResetResponse = await testableService.handleRequestReset(
+ station,
+ resetRequest
+ )
expect(response).toBeDefined()
expect(response.status).toBe(ResetStatusEnumType.Scheduled)
await it('Should return Accepted when firmware is Installed (complete)', async () => {
const station = createTestStation()
// Mock firmware status as Installed (update complete)
- ;(station as any).stationInfo = {
- ...station.stationInfo,
+ Object.assign(station.stationInfo, {
firmwareStatus: FirmwareStatus.Installed,
- }
+ })
const resetRequest: OCPP20ResetRequest = {
type: ResetEnumType.OnIdle,
}
- const response: OCPP20ResetResponse = await (
- incomingRequestService as any
- ).handleRequestReset(station, resetRequest)
+ const response: OCPP20ResetResponse = await testableService.handleRequestReset(
+ station,
+ resetRequest
+ )
expect(response).toBeDefined()
expect(response.status).toBe(ResetStatusEnumType.Accepted)
await it('Should return Accepted when firmware status is Idle', async () => {
const station = createTestStation()
// Mock firmware status as Idle (no update in progress)
- ;(station as any).stationInfo = {
- ...station.stationInfo,
+ Object.assign(station.stationInfo, {
firmwareStatus: FirmwareStatus.Idle,
- }
+ })
const resetRequest: OCPP20ResetRequest = {
type: ResetEnumType.OnIdle,
}
- const response: OCPP20ResetResponse = await (
- incomingRequestService as any
- ).handleRequestReset(station, resetRequest)
+ const response: OCPP20ResetResponse = await testableService.handleRequestReset(
+ station,
+ resetRequest
+ )
expect(response).toBeDefined()
expect(response.status).toBe(ResetStatusEnumType.Accepted)
const station = createTestStation()
// Create a reservation that expires in 1 hour (future)
const futureExpiryDate = new Date(Date.now() + 3600000)
- const mockReservation = {
+ const mockReservation: Partial<Reservation> = {
expiryDate: futureExpiryDate,
id: 1,
idTag: 'test-tag',
}
// Set reservation on first connector of first EVSE
- const evse = station.evses.get(1)
+ const evse: EvseStatus | undefined = station.evses.get(1)
if (evse) {
const connectorId = [...evse.connectors.keys()][0]
const connector = evse.connectors.get(connectorId)
if (connector) {
- connector.reservation = mockReservation as any
+ connector.reservation = mockReservation as Reservation
}
}
type: ResetEnumType.OnIdle,
}
- const response: OCPP20ResetResponse = await (
- incomingRequestService as any
- ).handleRequestReset(station, resetRequest)
+ const response: OCPP20ResetResponse = await testableService.handleRequestReset(
+ station,
+ resetRequest
+ )
expect(response).toBeDefined()
expect(response.status).toBe(ResetStatusEnumType.Scheduled)
const station = createTestStation()
// Create a reservation that expired 1 hour ago (past)
const pastExpiryDate = new Date(Date.now() - 3600000)
- const mockReservation = {
+ const mockReservation: Partial<Reservation> = {
expiryDate: pastExpiryDate,
id: 1,
idTag: 'test-tag',
}
// Set expired reservation on first connector of first EVSE
- const evse = station.evses.get(1)
+ const evse: EvseStatus | undefined = station.evses.get(1)
if (evse) {
const connectorId = [...evse.connectors.keys()][0]
const connector = evse.connectors.get(connectorId)
if (connector) {
- connector.reservation = mockReservation as any
+ connector.reservation = mockReservation as Reservation
}
}
type: ResetEnumType.OnIdle,
}
- const response: OCPP20ResetResponse = await (
- incomingRequestService as any
- ).handleRequestReset(station, resetRequest)
+ const response: OCPP20ResetResponse = await testableService.handleRequestReset(
+ station,
+ resetRequest
+ )
expect(response).toBeDefined()
// Station is idle because the reservation is expired
type: ResetEnumType.OnIdle,
}
- const response: OCPP20ResetResponse = await (
- incomingRequestService as any
- ).handleRequestReset(station, resetRequest)
+ const response: OCPP20ResetResponse = await testableService.handleRequestReset(
+ station,
+ resetRequest
+ )
expect(response).toBeDefined()
expect(response.status).toBe(ResetStatusEnumType.Accepted)
await it('Should return Accepted when all conditions clear (true idle state)', async () => {
const station = createTestStation()
// Ensure no transactions
- ;(station as any).getNumberOfRunningTransactions = () => 0
+ station.getNumberOfRunningTransactions = () => 0
// Ensure no firmware update in progress
- ;(station as any).stationInfo = {
- ...station.stationInfo,
+ Object.assign(station.stationInfo, {
firmwareStatus: FirmwareStatus.Idle,
- }
+ })
// No reservations (default state)
const resetRequest: OCPP20ResetRequest = {
type: ResetEnumType.OnIdle,
}
- const response: OCPP20ResetResponse = await (
- incomingRequestService as any
- ).handleRequestReset(station, resetRequest)
+ const response: OCPP20ResetResponse = await testableService.handleRequestReset(
+ station,
+ resetRequest
+ )
expect(response).toBeDefined()
expect(response.status).toBe(ResetStatusEnumType.Accepted)
await it('Should return Scheduled when multiple blocking conditions exist', async () => {
const station = createTestStation()
// Active transaction
- ;(station as any).getNumberOfRunningTransactions = () => 1
+ station.getNumberOfRunningTransactions = () => 1
// Firmware downloading
- ;(station as any).stationInfo = {
- ...station.stationInfo,
+ Object.assign(station.stationInfo, {
firmwareStatus: FirmwareStatus.Downloading,
- }
+ })
// Active reservation
const futureExpiryDate = new Date(Date.now() + 3600000)
- const mockReservation = {
+ const mockReservation: Partial<Reservation> = {
expiryDate: futureExpiryDate,
id: 1,
idTag: 'test-tag',
}
- const evse = station.evses.get(1)
+ const evse: EvseStatus | undefined = station.evses.get(1)
if (evse) {
const connectorId = [...evse.connectors.keys()][0]
const connector = evse.connectors.get(connectorId)
if (connector) {
- connector.reservation = mockReservation as any
+ connector.reservation = mockReservation as Reservation
}
}
type: ResetEnumType.OnIdle,
}
- const response: OCPP20ResetResponse = await (
- incomingRequestService as any
- ).handleRequestReset(station, resetRequest)
+ const response: OCPP20ResetResponse = await testableService.handleRequestReset(
+ station,
+ resetRequest
+ )
expect(response).toBeDefined()
expect(response.status).toBe(ResetStatusEnumType.Scheduled)
* @file Tests for OCPP20IncomingRequestService SetVariables
* @description Unit tests for OCPP 2.0 SetVariables command handling
*/
-/* eslint-disable @typescript-eslint/no-explicit-any */
import { expect } from '@std/expect'
import { millisecondsToSeconds } from 'date-fns'
import { afterEach, describe, it } from 'node:test'
+import { createTestableIncomingRequestService } from '../../../../src/charging-station/ocpp/2.0/__testable__/index.js'
import { OCPP20IncomingRequestService } from '../../../../src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.js'
import { OCPP20VariableManager } from '../../../../src/charging-station/ocpp/2.0/OCPP20VariableManager.js'
import {
upsertConfigurationKey,
} from './OCPP20TestUtils.js'
-interface IncomingRequestServicePrivate {
- handleRequestGetVariables: (
- chargingStation: any,
- request: OCPP20GetVariablesRequest
- ) => { getVariableResult: OCPP20GetVariableResultType[] }
- handleRequestSetVariables: (
- chargingStation: any,
- request: OCPP20SetVariablesRequest
- ) => { setVariableResult: OCPP20SetVariableResultType[] }
-}
-
-interface OCPP20GetVariablesRequest {
- getVariableData: OCPP20GetVariableDataType[]
-}
-
await describe('B05 - Set Variables', async () => {
const mockChargingStation = createChargingStation({
baseName: TEST_CHARGING_STATION_BASE_NAME,
})
const incomingRequestService = new OCPP20IncomingRequestService()
- const svc = incomingRequestService as unknown as IncomingRequestServicePrivate
+ const testableService = createTestableIncomingRequestService(incomingRequestService)
// Reset singleton state after each test to ensure test isolation
afterEach(() => {
}
const response: { setVariableResult: OCPP20SetVariableResultType[] } =
- svc.handleRequestSetVariables(mockChargingStation, request)
+ testableService.handleRequestSetVariables(mockChargingStation, request)
expect(response).toBeDefined()
expect(response.setVariableResult).toBeDefined()
}
const response: { setVariableResult: OCPP20SetVariableResultType[] } =
- svc.handleRequestSetVariables(mockChargingStation, request)
+ testableService.handleRequestSetVariables(mockChargingStation, request)
expect(response.setVariableResult).toHaveLength(2)
const firstResult = response.setVariableResult[0]
}
const response: { setVariableResult: OCPP20SetVariableResultType[] } =
- svc.handleRequestSetVariables(mockChargingStation, request)
+ testableService.handleRequestSetVariables(mockChargingStation, request)
expect(response.setVariableResult).toHaveLength(1)
const result = response.setVariableResult[0]
],
}
const response: { setVariableResult: OCPP20SetVariableResultType[] } =
- svc.handleRequestSetVariables(mockChargingStation, request)
+ testableService.handleRequestSetVariables(mockChargingStation, request)
expect(response.setVariableResult).toHaveLength(1)
const result = response.setVariableResult[0]
expect(result.attributeStatus).toBe(SetVariableStatusEnumType.UnknownComponent)
}
const response: { setVariableResult: OCPP20SetVariableResultType[] } =
- svc.handleRequestSetVariables(mockChargingStation, request)
+ testableService.handleRequestSetVariables(mockChargingStation, request)
expect(response.setVariableResult).toHaveLength(1)
const result = response.setVariableResult[0]
}
const response: { setVariableResult: OCPP20SetVariableResultType[] } =
- svc.handleRequestSetVariables(mockChargingStation, request)
+ testableService.handleRequestSetVariables(mockChargingStation, request)
expect(response.setVariableResult).toHaveLength(5)
const [accepted, unknownVariable, unsupportedAttrHeartbeat, unsupportedAttrWs, oversize] =
],
}
const response: { setVariableResult: OCPP20SetVariableResultType[] } =
- svc.handleRequestSetVariables(mockChargingStation, request)
+ testableService.handleRequestSetVariables(mockChargingStation, request)
expect(response.setVariableResult).toHaveLength(1)
const result = response.setVariableResult[0]
expect(result.attributeStatus).toBe(SetVariableStatusEnumType.NotSupportedAttributeType)
],
}
const response: { setVariableResult: OCPP20SetVariableResultType[] } =
- svc.handleRequestSetVariables(mockChargingStation, request)
+ testableService.handleRequestSetVariables(mockChargingStation, request)
expect(response.setVariableResult).toHaveLength(1)
const result = response.setVariableResult[0]
expect(result.attributeStatus).toBe(SetVariableStatusEnumType.Rejected)
},
],
}
- svc.handleRequestSetVariables(mockChargingStation, setRequest)
+ testableService.handleRequestSetVariables(mockChargingStation, setRequest)
const getResponse: { getVariableResult: OCPP20GetVariableResultType[] } =
- svc.handleRequestGetVariables(mockChargingStation, {
+ testableService.handleRequestGetVariables(mockChargingStation, {
getVariableData: [
{
attributeType: AttributeEnumType.Actual,
},
],
}
- svc.handleRequestSetVariables(mockChargingStation, setRequest)
+ testableService.handleRequestSetVariables(mockChargingStation, setRequest)
const getBefore: { getVariableResult: OCPP20GetVariableResultType[] } =
- svc.handleRequestGetVariables(mockChargingStation, {
+ testableService.handleRequestGetVariables(mockChargingStation, {
getVariableData: [
{
attributeType: AttributeEnumType.Actual,
OCPP20VariableManager.getInstance().resetRuntimeOverrides()
const getAfter: { getVariableResult: OCPP20GetVariableResultType[] } =
- svc.handleRequestGetVariables(mockChargingStation, {
+ testableService.handleRequestGetVariables(mockChargingStation, {
getVariableData: [
{
component: { name: OCPP20ComponentName.SampledDataCtrlr },
],
}
const response: { setVariableResult: OCPP20SetVariableResultType[] } =
- svc.handleRequestSetVariables(mockChargingStation, request)
+ testableService.handleRequestSetVariables(mockChargingStation, request)
expect(response.setVariableResult).toHaveLength(2)
response.setVariableResult.forEach(r => {
expect(r.attributeStatus).toBe(SetVariableStatusEnumType.Rejected)
],
}
const response: { setVariableResult: OCPP20SetVariableResultType[] } =
- svc.handleRequestSetVariables(mockChargingStation, request)
+ testableService.handleRequestSetVariables(mockChargingStation, request)
expect(response.setVariableResult).toHaveLength(2)
response.setVariableResult.forEach(r => {
expect(r.attributeStatus).toBe(SetVariableStatusEnumType.Rejected)
)
expect(preEstimate).toBeLessThan(postCalcLimit)
const response: { setVariableResult: OCPP20SetVariableResultType[] } =
- svc.handleRequestSetVariables(mockChargingStation, request)
+ testableService.handleRequestSetVariables(mockChargingStation, request)
const actualSize = Buffer.byteLength(JSON.stringify(response.setVariableResult), 'utf8')
expect(actualSize).toBeGreaterThan(postCalcLimit)
expect(response.setVariableResult).toHaveLength(request.setVariableData.length)
const prefix = 'wss://example.com/'
const withinLimit = prefix + 'a'.repeat(100 - prefix.length)
const overLimit = prefix + 'a'.repeat(100 - prefix.length + 1)
- let response = svc.handleRequestSetVariables(mockChargingStation, {
+ let response = testableService.handleRequestSetVariables(mockChargingStation, {
setVariableData: [
{
attributeValue: withinLimit,
],
})
expect(response.setVariableResult[0].attributeStatus).toBe(SetVariableStatusEnumType.Accepted)
- response = svc.handleRequestSetVariables(mockChargingStation, {
+ response = testableService.handleRequestSetVariables(mockChargingStation, {
setVariableData: [
{
attributeValue: overLimit,
const prefix = 'wss://example.com/'
const withinLimit = prefix + 'b'.repeat(120 - prefix.length)
const overLimit = prefix + 'b'.repeat(120 - prefix.length + 1)
- let response = svc.handleRequestSetVariables(mockChargingStation, {
+ let response = testableService.handleRequestSetVariables(mockChargingStation, {
setVariableData: [
{
attributeValue: withinLimit,
],
})
expect(response.setVariableResult[0].attributeStatus).toBe(SetVariableStatusEnumType.Accepted)
- response = svc.handleRequestSetVariables(mockChargingStation, {
+ response = testableService.handleRequestSetVariables(mockChargingStation, {
setVariableData: [
{
attributeValue: overLimit,
const prefix = 'wss://example.com/'
const withinLimit = prefix + 'c'.repeat(350 - prefix.length)
const overLimit = prefix + 'c'.repeat(350 - prefix.length + 1)
- let response = svc.handleRequestSetVariables(mockChargingStation, {
+ let response = testableService.handleRequestSetVariables(mockChargingStation, {
setVariableData: [
{
attributeValue: withinLimit,
],
})
expect(response.setVariableResult[0].attributeStatus).toBe(SetVariableStatusEnumType.Accepted)
- response = svc.handleRequestSetVariables(mockChargingStation, {
+ response = testableService.handleRequestSetVariables(mockChargingStation, {
setVariableData: [
{
attributeValue: overLimit,
const prefix = 'wss://example.com/'
const withinLimit = prefix + 'd'.repeat(260 - prefix.length)
const overLimit = prefix + 'd'.repeat(260 - prefix.length + 1)
- let response = svc.handleRequestSetVariables(mockChargingStation, {
+ let response = testableService.handleRequestSetVariables(mockChargingStation, {
setVariableData: [
{
attributeValue: withinLimit,
],
})
expect(response.setVariableResult[0].attributeStatus).toBe(SetVariableStatusEnumType.Accepted)
- response = svc.handleRequestSetVariables(mockChargingStation, {
+ response = testableService.handleRequestSetVariables(mockChargingStation, {
setVariableData: [
{
attributeValue: overLimit,
setValueSize(mockChargingStation, -5)
const prefix = 'wss://example.com/'
const validValue = prefix + 'e'.repeat(300 - prefix.length) // 300 < default absolute max length and < ConnectionUrl maxLength
- const response = svc.handleRequestSetVariables(mockChargingStation, {
+ const response = testableService.handleRequestSetVariables(mockChargingStation, {
setVariableData: [
{
attributeValue: validValue,
},
],
}
- svc.handleRequestSetVariables(mockChargingStation, setRequest)
+ testableService.handleRequestSetVariables(mockChargingStation, setRequest)
const getResponse: { getVariableResult: OCPP20GetVariableResultType[] } =
- svc.handleRequestGetVariables(mockChargingStation, {
+ testableService.handleRequestGetVariables(mockChargingStation, {
getVariableData: [
{
attributeType: AttributeEnumType.Actual,
],
}
const response: { setVariableResult: OCPP20SetVariableResultType[] } =
- svc.handleRequestSetVariables(mockChargingStation, setRequest)
+ testableService.handleRequestSetVariables(mockChargingStation, setRequest)
expect(response.setVariableResult).toHaveLength(1)
const setResult = response.setVariableResult[0]
expect(setResult.attributeStatus).toBe(SetVariableStatusEnumType.Accepted)
expect(setResult.attributeStatusInfo).toBeUndefined()
const getResponse: { getVariableResult: OCPP20GetVariableResultType[] } =
- svc.handleRequestGetVariables(mockChargingStation, {
+ testableService.handleRequestGetVariables(mockChargingStation, {
getVariableData: [
{
attributeType: AttributeEnumType.Actual,
* @file Tests for OCPP20RequestService ISO15118
* @description Unit tests for OCPP 2.0 ISO 15118 certificate and EV communication
*/
-/* eslint-disable @typescript-eslint/no-unsafe-member-access */
-/* eslint-disable @typescript-eslint/no-unsafe-assignment */
-/* eslint-disable @typescript-eslint/no-unsafe-call */
-/* eslint-disable @typescript-eslint/no-explicit-any */
/* cspell:ignore Bvbn NQIF CBCYX */
import { expect } from '@std/expect'
-import { describe, it, mock } from 'node:test'
+import { describe, it } from 'node:test'
-import { OCPP20RequestService } from '../../../../src/charging-station/ocpp/2.0/OCPP20RequestService.js'
-import { OCPP20ResponseService } from '../../../../src/charging-station/ocpp/2.0/OCPP20ResponseService.js'
+import { createTestableRequestService } from '../../../../src/charging-station/ocpp/2.0/__testable__/index.js'
import {
CertificateActionEnumType,
GetCertificateStatusEnumType,
const MOCK_ISO15118_SCHEMA_VERSION = 'urn:iso:std:iso:15118:-20:AC'
const MOCK_OCSP_RESULT = 'TW9jayBPQ1NQIFJlc3VsdCBCYXNlNjQ='
-// Helper to create mock request service with mocked sendMessage
-const createMockRequestService = <T>(responseOverride?: Partial<T>) => {
- const mockResponseService = new OCPP20ResponseService()
- const requestService = new OCPP20RequestService(mockResponseService)
-
- // Mock sendMessage to return configured response
- ;(requestService as any).sendMessage = mock.fn(() =>
- Promise.resolve({
- ...responseOverride,
- })
- )
-
- return requestService
-}
-
// Mock OCSP request data for GetCertificateStatus tests
const createMockOCSPRequestData = (): OCSPRequestDataType => ({
hashAlgorithm: HashAlgorithmEnumType.SHA256,
await describe('EXI Install Action', async () => {
await it('Should forward EXI request unmodified for Install action', async () => {
- const requestService = createMockRequestService<OCPP20Get15118EVCertificateResponse>({
- exiResponse: MOCK_EXI_RESPONSE,
- status: Iso15118EVCertificateStatusEnumType.Accepted,
- })
-
- await (requestService as any).requestGet15118EVCertificate(
+ const { sendMessageMock, service } =
+ createTestableRequestService<OCPP20Get15118EVCertificateResponse>({
+ sendMessageResponse: {
+ exiResponse: MOCK_EXI_RESPONSE,
+ status: Iso15118EVCertificateStatusEnumType.Accepted,
+ },
+ })
+
+ await service.requestGet15118EVCertificate(
mockChargingStation,
MOCK_ISO15118_SCHEMA_VERSION,
CertificateActionEnumType.Install,
MOCK_EXI_REQUEST
)
- const sendMessageMock = (requestService as any).sendMessage
expect(sendMessageMock.mock.calls.length).toBe(1)
const sentPayload = sendMessageMock.mock.calls[0]
await describe('EXI Update Action', async () => {
await it('Should forward EXI request unmodified for Update action', async () => {
- const requestService = createMockRequestService<OCPP20Get15118EVCertificateResponse>({
- exiResponse: MOCK_EXI_RESPONSE,
- status: Iso15118EVCertificateStatusEnumType.Accepted,
- })
-
- await (requestService as any).requestGet15118EVCertificate(
+ const { sendMessageMock, service } =
+ createTestableRequestService<OCPP20Get15118EVCertificateResponse>({
+ sendMessageResponse: {
+ exiResponse: MOCK_EXI_RESPONSE,
+ status: Iso15118EVCertificateStatusEnumType.Accepted,
+ },
+ })
+
+ await service.requestGet15118EVCertificate(
mockChargingStation,
MOCK_ISO15118_SCHEMA_VERSION,
CertificateActionEnumType.Update,
MOCK_EXI_REQUEST
)
- const sendMessageMock = (requestService as any).sendMessage
const sentPayload = sendMessageMock.mock.calls[0]
.arguments[2] as OCPP20Get15118EVCertificateRequest
expect(sentPayload.exiRequest).toBe(MOCK_EXI_REQUEST)
await describe('CSMS Response Handling', async () => {
await it('Should return Accepted response with exiResponse from CSMS', async () => {
- const requestService = createMockRequestService<OCPP20Get15118EVCertificateResponse>({
- exiResponse: MOCK_EXI_RESPONSE,
- status: Iso15118EVCertificateStatusEnumType.Accepted,
+ const { service } = createTestableRequestService<OCPP20Get15118EVCertificateResponse>({
+ sendMessageResponse: {
+ exiResponse: MOCK_EXI_RESPONSE,
+ status: Iso15118EVCertificateStatusEnumType.Accepted,
+ },
})
- const response: OCPP20Get15118EVCertificateResponse = await (
- requestService as any
- ).requestGet15118EVCertificate(
+ const response = await service.requestGet15118EVCertificate(
mockChargingStation,
MOCK_ISO15118_SCHEMA_VERSION,
CertificateActionEnumType.Install,
})
await it('Should return Failed response from CSMS', async () => {
- const requestService = createMockRequestService<OCPP20Get15118EVCertificateResponse>({
- exiResponse: '',
- status: Iso15118EVCertificateStatusEnumType.Failed,
- statusInfo: {
- reasonCode: ReasonCodeEnumType.InvalidCertificate,
+ const { service } = createTestableRequestService<OCPP20Get15118EVCertificateResponse>({
+ sendMessageResponse: {
+ exiResponse: '',
+ status: Iso15118EVCertificateStatusEnumType.Failed,
+ statusInfo: {
+ reasonCode: ReasonCodeEnumType.InvalidCertificate,
+ },
},
})
- const response: OCPP20Get15118EVCertificateResponse = await (
- requestService as any
- ).requestGet15118EVCertificate(
+ const response = await service.requestGet15118EVCertificate(
mockChargingStation,
MOCK_ISO15118_SCHEMA_VERSION,
CertificateActionEnumType.Install,
await describe('Schema Version Parameter', async () => {
await it('Should pass schema version correctly', async () => {
- const requestService = createMockRequestService<OCPP20Get15118EVCertificateResponse>({
- exiResponse: MOCK_EXI_RESPONSE,
- status: Iso15118EVCertificateStatusEnumType.Accepted,
- })
-
- await (requestService as any).requestGet15118EVCertificate(
+ const { sendMessageMock, service } =
+ createTestableRequestService<OCPP20Get15118EVCertificateResponse>({
+ sendMessageResponse: {
+ exiResponse: MOCK_EXI_RESPONSE,
+ status: Iso15118EVCertificateStatusEnumType.Accepted,
+ },
+ })
+
+ await service.requestGet15118EVCertificate(
mockChargingStation,
MOCK_ISO15118_SCHEMA_VERSION,
CertificateActionEnumType.Install,
MOCK_EXI_REQUEST
)
- const sendMessageMock = (requestService as any).sendMessage
const sentPayload = sendMessageMock.mock.calls[0]
.arguments[2] as OCPP20Get15118EVCertificateRequest
expect(sentPayload.iso15118SchemaVersion).toBe(MOCK_ISO15118_SCHEMA_VERSION)
const complexBase64EXI =
'VGhpcyBpcyBhIG1vcmUgY29tcGxleCBFWEkgcGF5bG9hZCB3aXRoIHNwZWNpYWwgY2hhcmFjdGVycyArLz0='
- const requestService = createMockRequestService<OCPP20Get15118EVCertificateResponse>({
- exiResponse: MOCK_EXI_RESPONSE,
- status: Iso15118EVCertificateStatusEnumType.Accepted,
- })
+ const { sendMessageMock, service } =
+ createTestableRequestService<OCPP20Get15118EVCertificateResponse>({
+ sendMessageResponse: {
+ exiResponse: MOCK_EXI_RESPONSE,
+ status: Iso15118EVCertificateStatusEnumType.Accepted,
+ },
+ })
- await (requestService as any).requestGet15118EVCertificate(
+ await service.requestGet15118EVCertificate(
mockChargingStation,
MOCK_ISO15118_SCHEMA_VERSION,
CertificateActionEnumType.Install,
complexBase64EXI
)
- const sendMessageMock = (requestService as any).sendMessage
const sentPayload = sendMessageMock.mock.calls[0]
.arguments[2] as OCPP20Get15118EVCertificateRequest
// EXI should be passed through unchanged - no decoding/encoding
await describe('OCSP Request Data', async () => {
await it('Should send OCSP request data correctly', async () => {
- const requestService = createMockRequestService<OCPP20GetCertificateStatusResponse>({
- ocspResult: MOCK_OCSP_RESULT,
- status: GetCertificateStatusEnumType.Accepted,
- })
+ const { sendMessageMock, service } =
+ createTestableRequestService<OCPP20GetCertificateStatusResponse>({
+ sendMessageResponse: {
+ ocspResult: MOCK_OCSP_RESULT,
+ status: GetCertificateStatusEnumType.Accepted,
+ },
+ })
const ocspRequestData = createMockOCSPRequestData()
- await (requestService as any).requestGetCertificateStatus(
- mockChargingStation,
- ocspRequestData
- )
+ await service.requestGetCertificateStatus(mockChargingStation, ocspRequestData)
- const sendMessageMock = (requestService as any).sendMessage
expect(sendMessageMock.mock.calls.length).toBe(1)
const sentPayload = sendMessageMock.mock.calls[0]
await describe('CSMS Response Handling', async () => {
await it('Should return Accepted response with ocspResult from CSMS', async () => {
- const requestService = createMockRequestService<OCPP20GetCertificateStatusResponse>({
- ocspResult: MOCK_OCSP_RESULT,
- status: GetCertificateStatusEnumType.Accepted,
+ const { service } = createTestableRequestService<OCPP20GetCertificateStatusResponse>({
+ sendMessageResponse: {
+ ocspResult: MOCK_OCSP_RESULT,
+ status: GetCertificateStatusEnumType.Accepted,
+ },
})
- const response: OCPP20GetCertificateStatusResponse = await (
- requestService as any
- ).requestGetCertificateStatus(mockChargingStation, createMockOCSPRequestData())
+ const response = await service.requestGetCertificateStatus(
+ mockChargingStation,
+ createMockOCSPRequestData()
+ )
expect(response).toBeDefined()
expect(response.status).toBe(GetCertificateStatusEnumType.Accepted)
})
await it('Should return Failed response from CSMS', async () => {
- const requestService = createMockRequestService<OCPP20GetCertificateStatusResponse>({
- status: GetCertificateStatusEnumType.Failed,
- statusInfo: {
- reasonCode: ReasonCodeEnumType.InternalError,
+ const { service } = createTestableRequestService<OCPP20GetCertificateStatusResponse>({
+ sendMessageResponse: {
+ status: GetCertificateStatusEnumType.Failed,
+ statusInfo: {
+ reasonCode: ReasonCodeEnumType.InternalError,
+ },
},
})
- const response: OCPP20GetCertificateStatusResponse = await (
- requestService as any
- ).requestGetCertificateStatus(mockChargingStation, createMockOCSPRequestData())
+ const response = await service.requestGetCertificateStatus(
+ mockChargingStation,
+ createMockOCSPRequestData()
+ )
expect(response).toBeDefined()
expect(response.status).toBe(GetCertificateStatusEnumType.Failed)
// Response is stubbed/mocked at the sendMessage level
const stubOcspResult = 'U3R1YiBPQ1NQIFJlc3BvbnNlIERhdGE='
- const requestService = createMockRequestService<OCPP20GetCertificateStatusResponse>({
- ocspResult: stubOcspResult,
- status: GetCertificateStatusEnumType.Accepted,
- })
+ const { sendMessageMock, service } =
+ createTestableRequestService<OCPP20GetCertificateStatusResponse>({
+ sendMessageResponse: {
+ ocspResult: stubOcspResult,
+ status: GetCertificateStatusEnumType.Accepted,
+ },
+ })
- const response: OCPP20GetCertificateStatusResponse = await (
- requestService as any
- ).requestGetCertificateStatus(mockChargingStation, createMockOCSPRequestData())
+ const response = await service.requestGetCertificateStatus(
+ mockChargingStation,
+ createMockOCSPRequestData()
+ )
expect(response).toBeDefined()
expect(response.status).toBe(GetCertificateStatusEnumType.Accepted)
expect(response.ocspResult).toBe(stubOcspResult)
// Verify sendMessage was called (no real network call)
- const sendMessageMock = (requestService as any).sendMessage
expect(sendMessageMock.mock.calls.length).toBe(1)
})
})
})
await it('Should send GET_15118_EV_CERTIFICATE command name', async () => {
- const requestService = createMockRequestService<OCPP20Get15118EVCertificateResponse>({
- exiResponse: MOCK_EXI_RESPONSE,
- status: Iso15118EVCertificateStatusEnumType.Accepted,
- })
+ const { sendMessageMock, service } =
+ createTestableRequestService<OCPP20Get15118EVCertificateResponse>({
+ sendMessageResponse: {
+ exiResponse: MOCK_EXI_RESPONSE,
+ status: Iso15118EVCertificateStatusEnumType.Accepted,
+ },
+ })
- await (requestService as any).requestGet15118EVCertificate(
+ await service.requestGet15118EVCertificate(
mockChargingStation,
MOCK_ISO15118_SCHEMA_VERSION,
CertificateActionEnumType.Install,
MOCK_EXI_REQUEST
)
- const sendMessageMock = (requestService as any).sendMessage
const commandName = sendMessageMock.mock.calls[0].arguments[3]
expect(commandName).toBe(OCPP20RequestCommand.GET_15118_EV_CERTIFICATE)
})
await it('Should send GET_CERTIFICATE_STATUS command name', async () => {
- const requestService = createMockRequestService<OCPP20GetCertificateStatusResponse>({
- ocspResult: MOCK_OCSP_RESULT,
- status: GetCertificateStatusEnumType.Accepted,
- })
+ const { sendMessageMock, service } =
+ createTestableRequestService<OCPP20GetCertificateStatusResponse>({
+ sendMessageResponse: {
+ ocspResult: MOCK_OCSP_RESULT,
+ status: GetCertificateStatusEnumType.Accepted,
+ },
+ })
- await (requestService as any).requestGetCertificateStatus(
- mockChargingStation,
- createMockOCSPRequestData()
- )
+ await service.requestGetCertificateStatus(mockChargingStation, createMockOCSPRequestData())
- const sendMessageMock = (requestService as any).sendMessage
const commandName = sendMessageMock.mock.calls[0].arguments[3]
expect(commandName).toBe(OCPP20RequestCommand.GET_CERTIFICATE_STATUS)
})
* @file Tests for OCPP20RequestService NotifyReport
* @description Unit tests for OCPP 2.0 NotifyReport request building (B07/B08)
*/
-/* eslint-disable @typescript-eslint/no-unsafe-member-access */
-/* eslint-disable @typescript-eslint/no-unsafe-assignment */
-/* eslint-disable @typescript-eslint/no-unsafe-call */
-/* eslint-disable @typescript-eslint/no-explicit-any */
import { expect } from '@std/expect'
import { describe, it } from 'node:test'
+import {
+ createTestableRequestService,
+ type TestableOCPP20RequestService,
+} from '../../../../src/charging-station/ocpp/2.0/__testable__/index.js'
import { OCPP20RequestService } from '../../../../src/charging-station/ocpp/2.0/OCPP20RequestService.js'
import { OCPP20ResponseService } from '../../../../src/charging-station/ocpp/2.0/OCPP20ResponseService.js'
import {
} from '../../ChargingStationTestConstants.js'
await describe('B07/B08 - NotifyReport', async () => {
- const mockResponseService = new OCPP20ResponseService()
- const requestService = new OCPP20RequestService(mockResponseService)
+ const { service: testableService } = createTestableRequestService()
const mockChargingStation = createChargingStation({
baseName: TEST_CHARGING_STATION_BASE_NAME,
}
// Access the private buildRequestPayload method via type assertion
- const payload = (requestService as any).buildRequestPayload(
+ const payload = testableService.buildRequestPayload(
mockChargingStation,
OCPP20RequestCommand.NOTIFY_REPORT,
requestParams
- )
+ ) as OCPP20NotifyReportRequest
expect(payload).toBeDefined()
expect(payload.generatedAt).toBeInstanceOf(Date)
tbc: false,
}
- const payload = (requestService as any).buildRequestPayload(
+ const payload = testableService.buildRequestPayload(
mockChargingStation,
OCPP20RequestCommand.NOTIFY_REPORT,
requestParams
- )
+ ) as OCPP20NotifyReportRequest
expect(payload).toBeDefined()
expect(payload.generatedAt).toBeInstanceOf(Date)
tbc: true,
}
- const payload = (requestService as any).buildRequestPayload(
+ const payload = testableService.buildRequestPayload(
mockChargingStation,
OCPP20RequestCommand.NOTIFY_REPORT,
requestParams
- )
+ ) as OCPP20NotifyReportRequest
expect(payload).toBeDefined()
expect(payload.generatedAt).toBeInstanceOf(Date)
tbc: true, // Indicates more fragments to follow
}
- const payload = (requestService as any).buildRequestPayload(
+ const payload = testableService.buildRequestPayload(
mockChargingStation,
OCPP20RequestCommand.NOTIFY_REPORT,
requestParams
- )
+ ) as OCPP20NotifyReportRequest
expect(payload).toBeDefined()
expect(payload.generatedAt).toBeInstanceOf(Date)
tbc: false,
}
- const payload = (requestService as any).buildRequestPayload(
+ const payload = testableService.buildRequestPayload(
mockChargingStation,
OCPP20RequestCommand.NOTIFY_REPORT,
requestParams
- )
+ ) as OCPP20NotifyReportRequest
expect(payload).toBeDefined()
expect(payload.generatedAt).toBeInstanceOf(Date)
tbc: false,
}
- const payload = (requestService as any).buildRequestPayload(
+ const payload = testableService.buildRequestPayload(
mockChargingStation,
OCPP20RequestCommand.NOTIFY_REPORT,
requestParams
- )
+ ) as OCPP20NotifyReportRequest
expect(payload).toBeDefined()
expect(payload.reportData[0].variableAttribute[0].type).toBe(attributeType)
tbc: false,
}
- const payload = (requestService as any).buildRequestPayload(
+ const payload = testableService.buildRequestPayload(
mockChargingStation,
OCPP20RequestCommand.NOTIFY_REPORT,
requestParams
- )
+ ) as OCPP20NotifyReportRequest
expect(payload).toBeDefined()
expect(payload.reportData[0].variableCharacteristics.dataType).toBe(testCase.dataType)
tbc: false,
}
- const payload = (requestService as any).buildRequestPayload(
+ const payload = testableService.buildRequestPayload(
mockChargingStation,
OCPP20RequestCommand.NOTIFY_REPORT,
requestParams
- )
+ ) as OCPP20NotifyReportRequest
// Validate that the payload has the exact structure of OCPP20NotifyReportRequest
expect(typeof payload).toBe('object')
tbc: false,
}
- const payload = (requestService as any).buildRequestPayload(
+ const payload = testableService.buildRequestPayload(
mockChargingStation,
OCPP20RequestCommand.NOTIFY_REPORT,
requestParams
- )
+ ) as OCPP20NotifyReportRequest
expect(payload).toBeDefined()
expect(payload.reportData[0].variableAttribute).toHaveLength(1)
tbc: true,
}
- const payload = (requestService as any).buildRequestPayload(
+ const payload = testableService.buildRequestPayload(
mockChargingStation,
OCPP20RequestCommand.NOTIFY_REPORT,
requestParams
- )
+ ) as OCPP20NotifyReportRequest
// Verify all input properties are preserved exactly
expect(payload.generatedAt).toBe(testDate)
* @file Tests for OCPP20RequestService SignCertificate
* @description Unit tests for OCPP 2.0 SignCertificate request building
*/
-/* eslint-disable @typescript-eslint/no-unsafe-member-access */
-/* eslint-disable @typescript-eslint/no-unsafe-assignment */
-/* eslint-disable @typescript-eslint/no-unsafe-call */
-/* eslint-disable @typescript-eslint/no-explicit-any */
import { expect } from '@std/expect'
-import { describe, it, mock } from 'node:test'
+import { describe, it } from 'node:test'
-import { OCPP20RequestService } from '../../../../src/charging-station/ocpp/2.0/OCPP20RequestService.js'
-import { OCPP20ResponseService } from '../../../../src/charging-station/ocpp/2.0/OCPP20ResponseService.js'
+import { createTestableRequestService } from '../../../../src/charging-station/ocpp/2.0/__testable__/index.js'
import {
CertificateSigningUseEnumType,
GenericStatus,
const MOCK_ORGANIZATION_NAME = 'Test Organization Inc.'
-// Helper to create mock request service with mocked sendMessage
-const createMockRequestService = (responseOverride?: Partial<OCPP20SignCertificateResponse>) => {
- const mockResponseService = new OCPP20ResponseService()
- const requestService = new OCPP20RequestService(mockResponseService)
-
- // Mock sendMessage to return configured response
- ;(requestService as any).sendMessage = mock.fn(() =>
- Promise.resolve({
- status: GenericStatus.Accepted,
- ...responseOverride,
- })
- )
-
- return requestService
-}
-
await describe('I02 - SignCertificate Request', async () => {
const mockChargingStation = createChargingStation({
baseName: TEST_CHARGING_STATION_BASE_NAME,
await describe('CSR Generation', async () => {
await it('Should generate CSR with PKCS#10 PEM format', async () => {
- const requestService = createMockRequestService()
-
- const response = await (requestService as any).requestSignCertificate(
+ const { sendMessageMock, service } =
+ createTestableRequestService<OCPP20SignCertificateResponse>({
+ sendMessageResponse: {
+ status: GenericStatus.Accepted,
+ },
+ })
+
+ const response = await service.requestSignCertificate(
mockChargingStation,
CertificateSigningUseEnumType.ChargingStationCertificate
)
expect(response).toBeDefined()
expect(response.status).toBe(GenericStatus.Accepted)
- const sendMessageMock = (requestService as any).sendMessage
expect(sendMessageMock.mock.calls.length).toBeGreaterThan(0)
const sentPayload = sendMessageMock.mock.calls[0].arguments[2] as OCPP20SignCertificateRequest
})
await it('Should include OrganizationName from SecurityCtrlr config in CSR', async () => {
- const requestService = createMockRequestService()
-
- await (requestService as any).requestSignCertificate(
+ const { sendMessageMock, service } =
+ createTestableRequestService<OCPP20SignCertificateResponse>({
+ sendMessageResponse: {
+ status: GenericStatus.Accepted,
+ },
+ })
+
+ await service.requestSignCertificate(
mockChargingStation,
CertificateSigningUseEnumType.ChargingStationCertificate
)
- const sendMessageMock = (requestService as any).sendMessage
const sentPayload = sendMessageMock.mock.calls[0].arguments[2] as OCPP20SignCertificateRequest
expect(sentPayload.csr).toBeDefined()
expect(sentPayload.csr).toContain('-----BEGIN CERTIFICATE REQUEST-----')
await describe('ChargingStationCertificate Type', async () => {
await it('Should send SignCertificateRequest with ChargingStationCertificate type', async () => {
- const requestService = createMockRequestService()
-
- await (requestService as any).requestSignCertificate(
+ const { sendMessageMock, service } =
+ createTestableRequestService<OCPP20SignCertificateResponse>({
+ sendMessageResponse: {
+ status: GenericStatus.Accepted,
+ },
+ })
+
+ await service.requestSignCertificate(
mockChargingStation,
CertificateSigningUseEnumType.ChargingStationCertificate
)
- const sendMessageMock = (requestService as any).sendMessage
const sentPayload = sendMessageMock.mock.calls[0].arguments[2] as OCPP20SignCertificateRequest
expect(sentPayload.certificateType).toBe(
await describe('V2GCertificate Type', async () => {
await it('Should send SignCertificateRequest with V2GCertificate type', async () => {
- const requestService = createMockRequestService()
-
- await (requestService as any).requestSignCertificate(
+ const { sendMessageMock, service } =
+ createTestableRequestService<OCPP20SignCertificateResponse>({
+ sendMessageResponse: {
+ status: GenericStatus.Accepted,
+ },
+ })
+
+ await service.requestSignCertificate(
mockChargingStation,
CertificateSigningUseEnumType.V2GCertificate
)
- const sendMessageMock = (requestService as any).sendMessage
const sentPayload = sendMessageMock.mock.calls[0].arguments[2] as OCPP20SignCertificateRequest
expect(sentPayload.certificateType).toBe(CertificateSigningUseEnumType.V2GCertificate)
await describe('CSMS Response Handling', async () => {
await it('Should return Accepted response from CSMS', async () => {
- const requestService = createMockRequestService({
- status: GenericStatus.Accepted,
+ const { service } = createTestableRequestService<OCPP20SignCertificateResponse>({
+ sendMessageResponse: {
+ status: GenericStatus.Accepted,
+ },
})
- const response: OCPP20SignCertificateResponse = await (
- requestService as any
- ).requestSignCertificate(
+ const response = await service.requestSignCertificate(
mockChargingStation,
CertificateSigningUseEnumType.ChargingStationCertificate
)
})
await it('Should return Rejected response from CSMS', async () => {
- const requestService = createMockRequestService({
- status: GenericStatus.Rejected,
- statusInfo: {
- reasonCode: 'InvalidCSR',
+ const { service } = createTestableRequestService<OCPP20SignCertificateResponse>({
+ sendMessageResponse: {
+ status: GenericStatus.Rejected,
+ statusInfo: {
+ reasonCode: 'InvalidCSR',
+ },
},
})
- const response: OCPP20SignCertificateResponse = await (
- requestService as any
- ).requestSignCertificate(
+ const response = await service.requestSignCertificate(
mockChargingStation,
CertificateSigningUseEnumType.ChargingStationCertificate
)
await describe('Optional Certificate Type', async () => {
await it('Should send SignCertificateRequest without certificateType when omitted', async () => {
- const requestService = createMockRequestService()
+ const { sendMessageMock, service } =
+ createTestableRequestService<OCPP20SignCertificateResponse>({
+ sendMessageResponse: {
+ status: GenericStatus.Accepted,
+ },
+ })
- await (requestService as any).requestSignCertificate(mockChargingStation)
+ await service.requestSignCertificate(mockChargingStation)
- const sendMessageMock = (requestService as any).sendMessage
const sentPayload = sendMessageMock.mock.calls[0].arguments[2] as OCPP20SignCertificateRequest
expect(sentPayload.csr).toBeDefined()
await describe('Request Payload Validation', async () => {
await it('Should build valid OCPP20SignCertificateRequest payload', async () => {
- const requestService = createMockRequestService()
-
- await (requestService as any).requestSignCertificate(
+ const { sendMessageMock, service } =
+ createTestableRequestService<OCPP20SignCertificateResponse>({
+ sendMessageResponse: {
+ status: GenericStatus.Accepted,
+ },
+ })
+
+ await service.requestSignCertificate(
mockChargingStation,
CertificateSigningUseEnumType.ChargingStationCertificate
)
- const sendMessageMock = (requestService as any).sendMessage
expect(sendMessageMock.mock.calls.length).toBe(1)
const sentPayload = sendMessageMock.mock.calls[0].arguments[2] as OCPP20SignCertificateRequest
})
await it('Should send SIGN_CERTIFICATE command name', async () => {
- const requestService = createMockRequestService()
-
- await (requestService as any).requestSignCertificate(
+ const { sendMessageMock, service } =
+ createTestableRequestService<OCPP20SignCertificateResponse>({
+ sendMessageResponse: {
+ status: GenericStatus.Accepted,
+ },
+ })
+
+ await service.requestSignCertificate(
mockChargingStation,
CertificateSigningUseEnumType.ChargingStationCertificate
)
- const sendMessageMock = (requestService as any).sendMessage
const commandName = sendMessageMock.mock.calls[0].arguments[3]
expect(commandName).toBe(OCPP20RequestCommand.SIGN_CERTIFICATE)
],
}
- delete (stationWithoutCertManager as any).certificateManager
+ delete stationWithoutCertManager.certificateManager
- const requestService = createMockRequestService()
+ const { sendMessageMock, service } =
+ createTestableRequestService<OCPP20SignCertificateResponse>({
+ sendMessageResponse: {
+ status: GenericStatus.Accepted,
+ },
+ })
- const response = await (requestService as any).requestSignCertificate(
+ const response = await service.requestSignCertificate(
stationWithoutCertManager,
CertificateSigningUseEnumType.ChargingStationCertificate
)
expect(response).toBeDefined()
expect(response.status).toBe(GenericStatus.Accepted)
- const sendMessageMock = (requestService as any).sendMessage
const sentPayload = sendMessageMock.mock.calls[0].arguments[2] as OCPP20SignCertificateRequest
expect(sentPayload.csr).toBeDefined()
expect(sentPayload.csr).toContain('-----BEGIN CERTIFICATE REQUEST-----')
* @file Tests for OCPP20VariableManager
* @description Unit tests for OCPP 2.0 variable management and device model
*/
-/* eslint-disable @typescript-eslint/no-unsafe-member-access */
-/* eslint-disable @typescript-eslint/no-unsafe-assignment */
-/* eslint-disable @typescript-eslint/no-unsafe-call */
-/* eslint-disable @typescript-eslint/no-explicit-any */
import { expect } from '@std/expect'
import { millisecondsToSeconds } from 'date-fns'
deleteConfigurationKey,
getConfigurationKey,
} from '../../../../src/charging-station/ConfigurationKeyUtils.js'
+import { createTestableVariableManager } from '../../../../src/charging-station/ocpp/2.0/__testable__/index.js'
import { OCPP20VariableManager } from '../../../../src/charging-station/ocpp/2.0/OCPP20VariableManager.js'
import { VARIABLE_REGISTRY } from '../../../../src/charging-station/ocpp/2.0/OCPP20VariableRegistry.js'
import {
await it('Should handle invalid component gracefully', () => {
const request: OCPP20GetVariableDataType[] = [
{
- component: { name: 'InvalidComponent' as any },
- variable: { name: 'SomeVariable' as any },
+ component: { name: 'InvalidComponent' as unknown as OCPP20ComponentName },
+ variable: { name: 'SomeVariable' as unknown as OCPP20OptionalVariableName },
},
]
await describe('Component validation tests', async () => {
const manager = OCPP20VariableManager.getInstance()
+ const testable = createTestableVariableManager(manager)
await it('Should validate OCPPCommCtrlr component as always valid', () => {
// Behavior: Connector components are unsupported and isComponentValid returns false.
const component: ComponentType = { name: OCPP20ComponentName.OCPPCommCtrlr }
// Access private method through any casting for testing
- const isValid = (manager as any).isComponentValid(mockChargingStation, component)
+ const isValid = testable.isComponentValid(mockChargingStation, component)
expect(isValid).toBe(true)
})
await it('Should reject Connector component as unsupported even when connectors exist', () => {
const component: ComponentType = { instance: '1', name: OCPP20ComponentName.Connector }
- const isValid = (manager as any).isComponentValid(mockChargingStation, component)
+ const isValid = testable.isComponentValid(mockChargingStation, component)
expect(isValid).toBe(false)
})
await it('Should reject invalid connector instance', () => {
const component: ComponentType = { instance: '999', name: OCPP20ComponentName.Connector }
- const isValid = (manager as any).isComponentValid(mockChargingStation, component)
+ const isValid = testable.isComponentValid(mockChargingStation, component)
expect(isValid).toBe(false)
})
})
await describe('Variable support validation tests', async () => {
const manager = OCPP20VariableManager.getInstance()
+ const testable = createTestableVariableManager(manager)
await it('Should support standard HeartbeatInterval variable', () => {
const component: ComponentType = { name: OCPP20ComponentName.OCPPCommCtrlr }
const variable: VariableType = { name: OCPP20OptionalVariableName.HeartbeatInterval }
- const isSupported = (manager as any).isVariableSupported(component, variable)
+ const isSupported = testable.isVariableSupported(component, variable)
expect(isSupported).toBe(true)
})
const component: ComponentType = { name: OCPP20ComponentName.ChargingStation }
const variable: VariableType = { name: OCPP20OptionalVariableName.WebSocketPingInterval }
- const isSupported = (manager as any).isVariableSupported(component, variable)
+ const isSupported = testable.isVariableSupported(component, variable)
expect(isSupported).toBe(true)
})
await it('Should reject unknown variables', () => {
const component: ComponentType = { name: OCPP20ComponentName.OCPPCommCtrlr }
- const variable: VariableType = { name: 'UnknownVariable' as any }
+ const variable: VariableType = { name: 'UnknownVariable' as unknown as OCPP20OptionalVariableName }
- const isSupported = (manager as any).isVariableSupported(component, variable)
+ const isSupported = testable.isVariableSupported(component, variable)
expect(isSupported).toBe(false)
})
})