...neostandard({
ts: true,
}),
+ {
+ files: ['**/*.ts', '**/*.tsx', '**/*.mts', '**/*.cts', '**/*.vue'],
+ rules: {
+ '@typescript-eslint/no-unused-vars': [
+ 'error',
+ {
+ args: 'none',
+ caughtErrors: 'none',
+ ignoreRestSiblings: true,
+ vars: 'all',
+ varsIgnorePattern: '^_',
+ },
+ ],
+ },
+ },
{
files: [
'src/charging-station/Bootstrap.ts',
chargingStation: ChargingStation,
commandName: IncomingRequestCommand
): boolean {
- return isIncomingRequestCommandSupported(
- chargingStation,
- commandName as OCPP16IncomingRequestCommand
- )
+ return isIncomingRequestCommandSupported(chargingStation, commandName)
}
private composeCompositeSchedule (
import {
ChargePointErrorCode,
ErrorType,
- type JsonObject,
type JsonType,
OCPP16ChargePointStatus,
type OCPP16MeterValue,
OCPPVersion,
type RequestParams,
} from '../../../types/index.js'
-import { generateUUID, logger } from '../../../utils/index.js'
+import { assertIsJsonObject, generateUUID, logger } from '../../../utils/index.js'
import { sendAndSetConnectorStatus } from '../OCPPConnectorStatusOperations.js'
import { OCPPRequestService } from '../OCPPRequestService.js'
import { createPayloadValidatorMap, isRequestCommandSupported } from '../OCPPServiceUtils.js'
): Request {
let connectorId: number | undefined
let energyActiveImportRegister: number
- commandParams = commandParams as JsonObject
logger.debug(
`${chargingStation.logPrefix()} ${moduleName}.buildRequestPayload: Building '${commandName}' payload`
)
switch (commandName) {
- case OCPP16RequestCommand.AUTHORIZE:
- return {
- idTag: OCPP16Constants.OCPP_DEFAULT_IDTAG,
- ...commandParams,
- } as unknown as Request
case OCPP16RequestCommand.BOOT_NOTIFICATION:
case OCPP16RequestCommand.DATA_TRANSFER:
case OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION:
return commandParams as unknown as Request
case OCPP16RequestCommand.HEARTBEAT:
return OCPP16Constants.OCPP_REQUEST_EMPTY as unknown as Request
+ }
+ assertIsJsonObject(
+ commandParams,
+ new OCPPError(
+ ErrorType.PROTOCOL_ERROR,
+ `'${commandName}' command requires object parameters`,
+ commandName
+ )
+ )
+ const params = commandParams
+ switch (commandName) {
+ case OCPP16RequestCommand.AUTHORIZE:
+ return {
+ idTag: OCPP16Constants.OCPP_DEFAULT_IDTAG,
+ ...params,
+ } as unknown as Request
case OCPP16RequestCommand.START_TRANSACTION:
return {
idTag: OCPP16Constants.OCPP_DEFAULT_IDTAG,
meterStart: chargingStation.getEnergyActiveImportRegisterByConnectorId(
- commandParams.connectorId as number,
+ params.connectorId as number,
true
),
timestamp: new Date(),
...(OCPP16ServiceUtils.hasReservation(
chargingStation,
- commandParams.connectorId as number,
- commandParams.idTag as string
+ params.connectorId as number,
+ params.idTag as string
) && {
reservationId: chargingStation.getReservationBy(
'connectorId',
chargingStation.getConnectorStatus(0)?.status === OCPP16ChargePointStatus.Reserved
? 0
- : (commandParams.connectorId as number)
+ : (params.connectorId as number)
)?.reservationId,
}),
- ...commandParams,
+ ...params,
} as unknown as Request
case OCPP16RequestCommand.STATUS_NOTIFICATION:
return OCPP16ServiceUtils.buildStatusNotificationRequest({
errorCode: ChargePointErrorCode.NO_ERROR,
- ...commandParams,
- } as unknown as OCPP16StatusNotificationRequest) as unknown as Request
+ ...params,
+ } as OCPP16StatusNotificationRequest) as unknown as Request
case OCPP16RequestCommand.STOP_TRANSACTION:
;(chargingStation.stationInfo?.transactionDataMeterValues === true ||
OCPP16ServiceUtils.isSigningEnabled(chargingStation)) &&
(connectorId = chargingStation.getConnectorIdByTransactionId(
- commandParams.transactionId as number
+ params.transactionId as number
))
energyActiveImportRegister = chargingStation.getEnergyActiveImportRegisterByTransactionId(
- commandParams.transactionId as number,
+ params.transactionId as number,
true
)
{
}
}
return {
- idTag: chargingStation.getTransactionIdTag(commandParams.transactionId as number),
+ idTag: chargingStation.getTransactionIdTag(params.transactionId as number),
meterStop: energyActiveImportRegister,
timestamp: new Date(),
...(transactionData != null && { transactionData }),
- ...commandParams,
+ ...params,
} as unknown as Request
}
default: {
logger.error(
`${chargingStation.logPrefix()} ${moduleName}.buildRequestPayload: ${errorMsg}`
)
- throw new OCPPError(ErrorType.NOT_SUPPORTED, errorMsg, commandName, commandParams)
+ throw new OCPPError(ErrorType.NOT_SUPPORTED, errorMsg, commandName, params)
}
}
}
chargingStation: ChargingStation,
commandName: RequestCommand
): boolean {
- return isRequestCommandSupported(chargingStation, commandName as OCPP16RequestCommand)
+ return isRequestCommandSupported(chargingStation, commandName)
}
private handleResponseAuthorize (
connectorId,
meterValue: [transactionEndMeterValue],
transactionId,
- } as MeterValuesRequest)
+ })
}
return await chargingStation.ocppRequestService.requestHandler<
Partial<StopTransactionRequest>,
}
return schedulePeriod
}),
- duration: differenceInSeconds(
- chargingScheduleInterval.end,
- compositeInterval.start as Date
- ),
+ duration: differenceInSeconds(chargingScheduleInterval.end, compositeInterval.start),
startSchedule: compositeInterval.start as Date,
}
}
compositeInterval
)
),
- duration: differenceInSeconds(
- compositeInterval.end as Date,
- chargingScheduleInterval.start
- ),
+ duration: differenceInSeconds(compositeInterval.end, chargingScheduleInterval.start),
}
}
return chargingSchedule
issuerPublicKeyDer = issuerCert.publicKey.export({
format: 'der',
type: 'spki',
- }) as Buffer
+ })
} else if (this.isSelfSignedCertificate(x509)) {
// Self-signed certificate: issuer = subject, use the certificate's own public key
issuerPublicKeyDer = x509.publicKey.export({
format: 'der',
type: 'spki',
- }) as Buffer
+ })
} else {
// Non-self-signed without issuer cert: use subject's public key as fallback
// This is technically incorrect per RFC 6960 but maintains backward compatibility
issuerPublicKeyDer = x509.publicKey.export({
format: 'der',
type: 'spki',
- }) as Buffer
+ })
}
const issuerKeyHash = hash(algorithmName, issuerPublicKeyDer, 'hex')
chargingStation: ChargingStation,
commandName: IncomingRequestCommand
): boolean {
- return isIncomingRequestCommandSupported(
- chargingStation,
- commandName as OCPP20IncomingRequestCommand
- )
+ return isIncomingRequestCommandSupported(chargingStation, commandName)
}
private async authorizeToken (
// L01.FR.04: Simulate signature verification
const simulateFailure = OCPP20ServiceUtils.readVariableAsBoolean(
chargingStation,
- OCPP20ComponentName.FirmwareCtrlr as string,
- OCPP20VendorVariableName.SimulateSignatureVerificationFailure as string,
+ OCPP20ComponentName.FirmwareCtrlr,
+ OCPP20VendorVariableName.SimulateSignatureVerificationFailure,
false
)
commandName: OCPP20RequestCommand,
commandParams?: JsonType
): Request {
- commandParams = commandParams as JsonObject
logger.debug(
`${chargingStation.logPrefix()} ${moduleName}.buildRequestPayload: Building '${commandName}' payload`
)
chargingStation: ChargingStation,
commandName: RequestCommand
): boolean {
- return isRequestCommandSupported(chargingStation, commandName as OCPP20RequestCommand)
+ return isRequestCommandSupported(chargingStation, commandName)
}
private handleResponseAuthorize (
}
return VARIABLE_REGISTRY_LOOKUP_CI[
buildCaseInsensitiveCompositeKey(component, undefined, variable)
- ] as undefined | VariableMetadata
+ ]
}
/**
// Create typed wrapper for the mock
const sendMessageMock: SendMessageMock = {
- fn: mockFn as unknown as SendMessageFn,
+ fn: mockFn,
mock: mockFn.mock as unknown as SendMessageMock['mock'],
}
if (isAsyncFunction(responseHandler)) {
await responseHandler(chargingStation, payload, requestPayload)
} else {
- ;(
- responseHandler as (
- chargingStation: ChargingStation,
- payload: JsonType,
- requestPayload?: JsonType
- ) => void
- )(chargingStation, payload, requestPayload)
+ const _syncResult = responseHandler(chargingStation, payload, requestPayload)
}
logger.debug(
`${chargingStation.logPrefix()} ${this.moduleName}.responseHandler: '${commandName}' response processed successfully`
const item = value[i]
if (isDate(item)) {
try {
- value[i] = item.toISOString() as unknown as typeof item
+ value[i] = item.toISOString()
} catch {
// Ignore date conversion error
}
const isMeasurandSupported = (measurand: MeterValueMeasurand): boolean => {
const supportedMeasurands = OCPPConstants.OCPP_MEASURANDS_SUPPORTED as readonly string[]
- return supportedMeasurands.includes(measurand as string)
+ return supportedMeasurands.includes(measurand)
}
/**
value: number
} => {
const sampledValueMeasurand =
- (sampledValueTemplate.measurand as MeterValueMeasurand | undefined) ??
- MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
+ sampledValueTemplate.measurand ?? MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
return {
- context:
- context ??
- (sampledValueTemplate.context as MeterValueContext | undefined) ??
- MeterValueContext.SAMPLE_PERIODIC,
- location:
- (sampledValueTemplate.location as MeterValueLocation | undefined) ??
- getMeasurandDefaultLocation(sampledValueMeasurand),
+ context: context ?? sampledValueTemplate.context ?? MeterValueContext.SAMPLE_PERIODIC,
+ location: sampledValueTemplate.location ?? getMeasurandDefaultLocation(sampledValueMeasurand),
measurand: sampledValueMeasurand,
- phase: phase ?? (sampledValueTemplate.phase as MeterValuePhase | undefined),
+ phase: phase ?? sampledValueTemplate.phase,
unit:
(sampledValueTemplate.unit as MeterValueUnit | undefined) ??
getMeasurandDefaultUnit(sampledValueMeasurand),
return {
additionalIdToken: value,
type: 'string',
- } as AdditionalInfoType
+ }
}
})
: undefined
.collection<Statistics>(Constants.PERFORMANCE_RECORDS_TABLE)
.replaceOne(
{ id: performanceStatistics.id },
- this.serializePerformanceStatistics(performanceStatistics) as unknown as Statistics,
+ this.serializePerformanceStatistics(performanceStatistics),
{ upsert: true }
)
} catch (error) {
this.resolveQueue = new Queue<ResolveType>()
}
- public static async runExclusive<T>(type: AsyncLockType, fn: () => Promise<T> | T): Promise<T> {
+ public static async runExclusive<T>(
+ type: AsyncLockType,
+ fn: (() => Promise<T>) | (() => T)
+ ): Promise<T> {
try {
await AsyncLock.acquire(type)
if (isAsyncFunction(fn)) {
return await fn()
} else {
- return fn() as T
+ return fn()
}
} finally {
AsyncLock.release(type)
{
...evseStatusRest,
connectorsStatus,
- } as EvseStatusConfiguration,
+ },
] as [number, EvseStatusConfiguration]
})
.toArray()
import { env } from 'node:process'
import {
+ type JsonObject,
type JsonType,
MapStringifyFormat,
MessageType,
if (object == null || (typeof object !== 'object' && typeof object !== 'function')) {
return false
}
- return Object.hasOwn(object as Record<PropertyKey, unknown>, property)
+ return Object.hasOwn(object, property)
}
const type = (value: unknown): string => {
return type(value) === 'Object'
}
+export const isJsonObject = (value: unknown): value is JsonObject => {
+ return value != null && typeof value === 'object' && !Array.isArray(value)
+}
+
+/**
+ * Asserts that the given value is a JSON object (non-null, non-array object).
+ * @param value - Value to assert.
+ * @param error - Optional custom error or context message.
+ * @throws {Error | TypeError} The provided error, or a TypeError with the context message.
+ */
+export function assertIsJsonObject (
+ value: unknown,
+ error?: Error | string
+): asserts value is JsonObject {
+ if (value == null || typeof value !== 'object' || Array.isArray(value)) {
+ if (error instanceof Error) {
+ throw error
+ }
+ throw new TypeError(
+ error != null ? `Expected a JSON object: ${error}` : 'Expected a JSON object'
+ )
+ }
+}
+
export const isEmpty = (value: unknown): boolean => {
if (
value == null ||
const output: Record<string, unknown> = { ...(target as Record<string, unknown>) }
if (isObject(target) && isObject(source)) {
- Object.keys(source as Record<string, unknown>).forEach(key => {
+ Object.keys(source).forEach(key => {
const sourceValue = (source as Record<string, unknown>)[key]
const targetValue = (target as Record<string, unknown>)[key]
if (isObject(sourceValue) && isObject(targetValue)) {
return '(For applications)'
}
}
- if (
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
- WebSocketCloseEventStatusString[code as keyof typeof WebSocketCloseEventStatusString] != null
- ) {
- return WebSocketCloseEventStatusString[code as keyof typeof WebSocketCloseEventStatusString]
+ const statusString = (
+ WebSocketCloseEventStatusString as Readonly<Record<number, string | undefined>>
+ )[code]
+ if (statusString != null) {
+ return statusString
}
return '(Unknown)'
}
} from './MessageChannelUtils.js'
export { average, max, median, min, percentile, std } from './StatisticUtils.js'
export {
+ assertIsJsonObject,
clampToSafeTimerValue,
clone,
computeExponentialBackOffDelay,
isArraySorted,
isAsyncFunction,
isEmpty,
+ isJsonObject,
isNotEmptyArray,
isNotEmptyString,
isValidDate,
// Set up a heartbeat timer (simulated)
station.heartbeatSetInterval = setInterval(() => {
/* empty */
- }, TEST_HEARTBEAT_INTERVAL_MS) as unknown as NodeJS.Timeout
+ }, TEST_HEARTBEAT_INTERVAL_MS)
// Act - Cleanup station
cleanupChargingStation(station)
import assert from 'node:assert/strict'
import { afterEach, describe, it } from 'node:test'
-import type { ChargingStationOcppConfiguration } from '../../src/types/index.js'
-
import {
addConfigurationKey,
buildConfigKey,
// Arrange
const { station: cs } = createMockChargingStation()
// Simulate missing configurationKey array
- cs.ocppConfiguration = {} as Partial<ChargingStationOcppConfiguration>
+ cs.ocppConfiguration = {}
// Act & Assert
assert.strictEqual(getConfigurationKey(cs, TEST_KEY_1), undefined)
// Arrange
const { station: cs } = createMockChargingStation()
// Simulate missing configurationKey array
- cs.ocppConfiguration = {} as Partial<ChargingStationOcppConfiguration>
+ cs.ocppConfiguration = {}
// Act
addConfigurationKey(cs, TEST_KEY_1, VALUE_A)
// Arrange
const { station: cs } = createMockChargingStation()
// Simulate missing configurationKey array
- cs.ocppConfiguration = {} as Partial<ChargingStationOcppConfiguration>
+ cs.ocppConfiguration = {}
const errorMock = t.mock.method(logger, 'error')
// Act
// Arrange
const { station: cs } = createMockChargingStation()
// Simulate missing configurationKey array
- cs.ocppConfiguration = {} as Partial<ChargingStationOcppConfiguration>
+ cs.ocppConfiguration = {}
// Act
const res = deleteConfigurationKey(cs, TEST_KEY_1)
AvailabilityType,
type ChargingProfile,
ChargingProfilePurposeType,
- type ChargingStationConfiguration,
type ChargingStationInfo,
type ChargingStationOptions,
type ChargingStationTemplate,
type ConnectorStatus,
ConnectorStatusEnum,
- type MeterValue,
OCPPVersion,
type Reservation,
type SampledValueTemplate,
})
// Helper to create test reservations with configurable expiry
- const createTestReservation = (expired = false): Reservation =>
- ({
- connectorId: 1,
- expiryDate: new Date(Date.now() + (expired ? -60000 : 60000)),
- idTag: 'tag1',
- reservationId: 1,
- }) as Reservation
+ const createTestReservation = (expired = false): Reservation => ({
+ connectorId: 1,
+ expiryDate: new Date(Date.now() + (expired ? -60000 : 60000)),
+ idTag: 'tag1',
+ reservationId: 1,
+ })
await it('should return formatted charging station ID with index', () => {
assert.strictEqual(
assert.strictEqual(errorMock.mock.calls.length, 1)
assert.throws(
() => {
- checkConfiguration({} as ChargingStationConfiguration, 'log prefix |', 'configuration.json')
+ checkConfiguration({}, 'log prefix |', 'configuration.json')
},
{ message: /Empty charging station configuration from file configuration\.json/ }
)
const connectorStatus: ConnectorStatus = {
availability: AvailabilityType.Operative,
MeterValues: [],
- transactionBeginMeterValue: { sampledValue: [], timestamp: new Date() } as MeterValue,
+ transactionBeginMeterValue: { sampledValue: [], timestamp: new Date() },
transactionDeauthorized: true,
transactionDeauthorizedEnergyWh: 500,
transactionEnergyActiveImportRegisterValue: 1234,
chargePointModel: 'test-model',
chargePointVendor: 'test-vendor',
templateHash: hash,
- } as ChargingStationTemplate
+ }
}
/**
transactionRemoteStarted: false,
transactionStart: undefined,
transactionStarted: false,
- } as unknown as ConnectorStatus
+ }
}
/**
await requestService.requestHandler(station, OCPP16RequestCommand.STATUS_NOTIFICATION, {
connectorId: 1,
status: OCPP16ChargePointStatus.Available,
- } as unknown as JsonType)
+ })
assert.strictEqual(sendMessageMock.mock.callCount(), 1)
const sentPayload = sendMessageMock.mock.calls[0]
await requestService.requestHandler(station, OCPP16RequestCommand.START_TRANSACTION, {
connectorId: 1,
idTag: 'TEST001',
- } as unknown as JsonType)
+ })
assert.strictEqual(sendMessageMock.mock.callCount(), 1)
const sentPayload = sendMessageMock.mock.calls[0]
await requestService.requestHandler(station, OCPP16RequestCommand.STOP_TRANSACTION, {
transactionId: 12345,
- } as unknown as JsonType)
+ })
assert.strictEqual(sendMessageMock.mock.callCount(), 1)
const sentPayload = sendMessageMock.mock.calls[0].arguments[2] as OCPP16StopTransactionRequest
},
websocketPingInterval: Constants.DEFAULT_WS_PING_INTERVAL_SECONDS,
})
- return station as MockChargingStation
+ return station
}
await describe('OCPP16ResponseService — SimpleHandlers', async () => {
const beginMeterValue: OCPP16MeterValue = {
sampledValue: [{ context: OCPP16MeterValueContext.TRANSACTION_BEGIN, value: '0' }],
timestamp: new Date('2025-01-01T00:00:00Z'),
- } as OCPP16MeterValue
+ }
const endMeterValue: OCPP16MeterValue = {
sampledValue: [{ context: OCPP16MeterValueContext.TRANSACTION_END, value: '100' }],
timestamp: new Date('2025-01-01T01:00:00Z'),
- } as OCPP16MeterValue
+ }
// Act
const result = OCPP16ServiceUtils.buildTransactionDataMeterValues(
const beginMeterValue: OCPP16MeterValue = {
sampledValue: [],
timestamp: new Date(),
- } as OCPP16MeterValue
+ }
const endMeterValue: OCPP16MeterValue = {
sampledValue: [],
timestamp: new Date(),
- } as OCPP16MeterValue
+ }
const result1 = OCPP16ServiceUtils.buildTransactionDataMeterValues(
beginMeterValue,
chargingSchedulePeriod: [{ limit: 1000, startPeriod: 0 }],
},
stackLevel,
- } as OCPP16ChargingProfile
+ }
}
await it('should return false for undefined profiles array', () => {
chargingSchedulePeriod: [{ limit, startPeriod: 0 }],
duration: durationSeconds,
startSchedule: start,
- } as OCPP16ChargingSchedule
+ }
}
await it('should return undefined when both schedules are undefined', () => {
import type {
ChargingStationInfo,
ConfigurationKey,
- IncomingRequestCommand,
JsonObject,
OCPP16ChargingProfile,
OCPP16ChargingSchedulePeriod,
OCPP16SampledValue,
- RequestCommand,
SampledValueTemplate,
} from '../../../../src/types/index.js'
outgoingCommands?: Record<string, boolean>
}): NonNullable<ChargingStationInfo['commandsSupport']> {
return {
- incomingCommands: (config.incomingCommands ?? {}) as unknown as Record<
- IncomingRequestCommand,
- boolean
- >,
+ incomingCommands: config.incomingCommands ?? {},
...(config.outgoingCommands != null && {
- outgoingCommands: config.outgoingCommands as unknown as Record<RequestCommand, boolean>,
+ outgoingCommands: config.outgoingCommands,
}),
}
}
* @returns The entries typed as `SampledValueTemplate[]`
*/
export function createMeterValuesTemplate (entries: OCPP16SampledValue[]): SampledValueTemplate[] {
- return entries as unknown as SampledValueTemplate[]
+ return entries
}
/**
websocketPingInterval: Constants.DEFAULT_WS_PING_INTERVAL_SECONDS,
})
- return station as MockChargingStation
+ return station
}
/**
payload: JsonObject,
requestPayload: JsonObject = {}
): Promise<void> {
- await responseService.responseHandler(
- station,
- command,
- payload as unknown as Parameters<OCPP16ResponseService['responseHandler']>[2],
- requestPayload as unknown as Parameters<OCPP16ResponseService['responseHandler']>[3]
- )
+ await responseService.responseHandler(station, command, payload, requestPayload)
}
/**
assert.strictEqual(response.statusInfo?.reasonCode, 'InvalidCertificate')
assert.ok(
response.statusInfo.additionalInfo?.includes(
- OCPP20OptionalVariableName.MaxCertificateChainSize as string
+ OCPP20OptionalVariableName.MaxCertificateChainSize
)
)
})
await it('should not include ongoingIndicator when active transaction exists but no transactionId (E14.FR.06)', () => {
const transactionId = 'txn-12345'
setupConnectorWithTransaction(station, 1, {
- transactionId: transactionId as unknown as number,
+ transactionId,
})
const response = testableService.handleRequestGetTransactionStatus(station, {})
await it('should return ongoingIndicator true when specific transactionId exists', () => {
const transactionId = 'txn-67890'
setupConnectorWithTransaction(station, 2, {
- transactionId: transactionId as unknown as number,
+ transactionId,
})
const response = testableService.handleRequestGetTransactionStatus(station, {
// Given: Request without evseId (undefined)
const request: OCPP20RequestStartTransactionRequest = {
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any -- testing invalid undefined input
- evseId: undefined as any,
+ evseId: undefined,
idToken: {
idToken: 'VALID_TOKEN_006',
type: OCPP20IdTokenEnumType.ISO14443,
},
websocketPingInterval: Constants.DEFAULT_WS_PING_INTERVAL_SECONDS,
})
- return { mockStation: station as MockChargingStation, requestHandlerMock }
+ return { mockStation: station, requestHandlerMock }
}
await describe('F05 - UnlockConnector', async () => {
{
attributeType: AttributeEnumType.Actual,
attributeValue: 'false',
- component: { name: OCPP20ComponentName.ChargingStation as string },
+ component: { name: OCPP20ComponentName.ChargingStation },
variable: { name: 'AllowNewSessionsPendingFirmwareUpdate' },
},
])
{
attributeType: AttributeEnumType.Actual,
attributeValue: 'true',
- component: { name: OCPP20ComponentName.FirmwareCtrlr as string },
+ component: { name: OCPP20ComponentName.FirmwareCtrlr },
variable: { name: 'SimulateSignatureVerificationFailure' },
},
])
{
attributeType: AttributeEnumType.Actual,
attributeValue: 'false',
- component: { name: OCPP20ComponentName.FirmwareCtrlr as string },
+ component: { name: OCPP20ComponentName.FirmwareCtrlr },
variable: { name: 'SimulateSignatureVerificationFailure' },
},
])
await service.requestHandler(
station,
OCPP20RequestCommand.TRANSACTION_EVENT,
- preBuiltPayload as unknown as OCPP20TransactionEventRequest,
+ preBuiltPayload,
{ rawPayload: true }
)
// Verify no additional properties are added
const expectedKeys = ['generatedAt', 'reportData', 'requestId', 'seqNo', 'tbc']
- const actualKeys = Object.keys(payload as object).sort()
+ const actualKeys = Object.keys(payload).sort()
expectedKeys.sort()
assert.deepStrictEqual(actualKeys, expectedKeys)
})
},
websocketPingInterval: Constants.DEFAULT_WS_PING_INTERVAL_SECONDS,
})
- return station as MockChargingStation
+ return station
}
await describe('B01 - BootNotificationResponse handler', async () => {
await responseService.responseHandler(
mockStation,
OCPP20RequestCommand.BOOT_NOTIFICATION,
- payload as unknown as Parameters<typeof responseService.responseHandler>[2],
- {} as Parameters<typeof responseService.responseHandler>[3]
+ payload,
+ {}
)
}
},
websocketPingInterval: Constants.DEFAULT_WS_PING_INTERVAL_SECONDS,
})
- return station as MockChargingStation
+ return station
}
await describe('Simple response handlers', async () => {
await it('should handle Heartbeat response without throwing', async () => {
const payload: OCPP20HeartbeatResponse = { currentTime: new Date() }
await assert.doesNotReject(
- responseService.responseHandler(
- mockStation,
- OCPP20RequestCommand.HEARTBEAT,
- payload as unknown as Parameters<typeof responseService.responseHandler>[2],
- {} as Parameters<typeof responseService.responseHandler>[3]
- )
+ responseService.responseHandler(mockStation, OCPP20RequestCommand.HEARTBEAT, payload, {})
)
})
})
responseService.responseHandler(
mockStation,
OCPP20RequestCommand.NOTIFY_REPORT,
- payload as unknown as Parameters<typeof responseService.responseHandler>[2],
- {} as Parameters<typeof responseService.responseHandler>[3]
+ payload,
+ {}
)
)
})
responseService.responseHandler(
mockStation,
OCPP20RequestCommand.STATUS_NOTIFICATION,
- payload as unknown as Parameters<typeof responseService.responseHandler>[2],
- {} as Parameters<typeof responseService.responseHandler>[3]
+ payload,
+ {}
)
)
})
const result = createMockChargingStation({
connectorsCount: 1,
ocppRequestService: {
- requestHandler: requestHandler as (...args: unknown[]) => Promise<unknown>,
+ requestHandler,
},
ocppVersion: OCPPVersion.VERSION_20,
started: true,
await it('should handle invalid component gracefully', () => {
const request: OCPP20GetVariableDataType[] = [
{
- component: { name: 'InvalidComponent' as unknown as OCPP20ComponentName },
- variable: { name: 'SomeVariable' as unknown as OCPP20OptionalVariableName },
+ component: { name: 'InvalidComponent' },
+ variable: { name: 'SomeVariable' },
},
]
await it('should reject unknown variables', () => {
const component: ComponentType = { name: OCPP20ComponentName.OCPPCommCtrlr }
const variable: VariableType = {
- name: 'UnknownVariable' as unknown as OCPP20OptionalVariableName,
+ name: 'UnknownVariable',
}
const isSupported = testable.isVariableSupported(component, variable)
const request: OCPP20SetVariableDataType[] = [
{
attributeValue: '20',
- component: { name: 'InvalidComponent' as unknown as OCPP20ComponentName },
+ component: { name: 'InvalidComponent' },
variable: { name: OCPP20OptionalVariableName.WebSocketPingInterval },
},
]
{
attributeValue: '10',
component: { name: OCPP20ComponentName.OCPPCommCtrlr },
- variable: { name: 'UnknownVariable' as unknown as VariableType['name'] },
+ variable: { name: 'UnknownVariable' },
},
]
{
attributeValue: '10',
component: { name: OCPP20ComponentName.OCPPCommCtrlr },
- variable: { name: 'InvalidVariable' as unknown as VariableType['name'] },
+ variable: { name: 'InvalidVariable' },
},
{
attributeType: AttributeEnumType.Target,
requestHandler.mock.calls.length >= 1,
'request handler should have been called at least once'
)
- assert.strictEqual(requestHandler.mock.calls[0].arguments[1] as string, 'StopTransaction')
+ assert.strictEqual(requestHandler.mock.calls[0].arguments[1], 'StopTransaction')
})
await it('should send TransactionEvent(Ended) for OCPP 2.0 stations and return accepted: true', async () => {
requestHandler.mock.calls.length >= 1,
'request handler should have been called at least once'
)
- assert.strictEqual(requestHandler.mock.calls[0].arguments[1] as string, 'TransactionEvent')
+ assert.strictEqual(requestHandler.mock.calls[0].arguments[1], 'TransactionEvent')
})
await it('should throw OCPPError for unsupported OCPP version in stopTransactionOnConnector', async () => {
requestHandler.mock.calls.length >= 1,
'request handler should have been called at least once'
)
- assert.strictEqual(requestHandler.mock.calls[0].arguments[1] as string, 'StartTransaction')
+ assert.strictEqual(requestHandler.mock.calls[0].arguments[1], 'StartTransaction')
})
await it('should send TransactionEvent(Started) for OCPP 2.0 stations and return accepted: true', async () => {
requestHandler.mock.calls.length >= 1,
'request handler should have been called at least once'
)
- assert.strictEqual(requestHandler.mock.calls[0].arguments[1] as string, 'TransactionEvent')
+ assert.strictEqual(requestHandler.mock.calls[0].arguments[1], 'TransactionEvent')
})
await it('should generate transactionId for OCPP 2.0 when not pre-populated', async () => {
const obj = { nested: { deep: { created: date } } } as unknown as JsonType
convertDateToISOString(obj)
assert.deepStrictEqual(
- ((obj as Record<string, unknown>).nested as Record<string, unknown>).deep as Record<
- string,
- unknown
- >,
+ ((obj as Record<string, unknown>).nested as Record<string, unknown>).deep,
{ created: '2025-06-01T12:00:00.000Z' }
)
})
}
public getAcceptsGzip (): Map<UUIDv4, boolean> {
- return Reflect.get(this, 'acceptsGzip') as Map<UUIDv4, boolean>
+ return Reflect.get(this, 'acceptsGzip')
}
public getResponseHandlersSize (): number {
timeout: ReturnType<typeof setTimeout>
}
> {
- return Reflect.get(this, 'pendingMcpRequests') as Map<
- string,
- {
- reject: (error: Error) => void
- resolve: (payload: ResponsePayload) => void
- timeout: ReturnType<typeof setTimeout>
- }
- >
+ return Reflect.get(this, 'pendingMcpRequests')
}
public getPendingMcpRequestsSize (): number {
stats.statisticsData.set('StatusNotification', {
requestCount: 50,
responseCount: 50,
- } as unknown as Record<string, unknown>)
+ })
// Act
await storage.storePerformanceStatistics(stats)
}
public getOpened (): boolean {
- return Reflect.get(this, 'opened') as boolean
+ return Reflect.get(this, 'opened')
}
public setClient (client: MockMongoClient): void {
stats.statisticsData.set('StatusNotification', {
requestCount: 50,
responseCount: 50,
- } as unknown as Record<string, unknown>)
+ })
// Act
await storage.storePerformanceStatistics(stats)
const failingClient: MockMongoClient = {
close: async () => Promise.resolve(),
connect: async () => Promise.resolve(),
- db: () => ({ collection: () => failingCollection }) as unknown as MockDb,
+ db: () => ({ collection: () => failingCollection }),
}
storage.setClient(failingClient)
storage.setOpened(true)
name: name ?? `cs-${id}`,
statisticsData: statsData,
uri: 'ws://localhost:8080',
- } as unknown as Statistics
+ }
}
internals.connectors.set(0, {
availability: AvailabilityType.Operative,
MeterValues: [],
- } as ConnectorStatus)
+ })
internals.connectors.set(1, {
availability: AvailabilityType.Operative,
bootStatus: 'Available',
MeterValues: [],
transactionEndedMeterValues: [{ sampledValue: [], timestamp: new Date() }],
- transactionEndedMeterValuesSetInterval: interval2 as unknown as NodeJS.Timeout,
+ transactionEndedMeterValuesSetInterval: interval2,
transactionEventQueue: [],
- transactionUpdatedMeterValuesSetInterval: interval1 as unknown as NodeJS.Timeout,
+ transactionUpdatedMeterValuesSetInterval: interval1,
} as unknown as ConnectorStatus)
const result = buildConnectorsStatus(station)
internals.connectors.set(0, {
availability: AvailabilityType.Operative,
MeterValues: [],
- } as ConnectorStatus)
+ })
internals.connectors.set(3, {
availability: AvailabilityType.Inoperative,
MeterValues: [],
- } as ConnectorStatus)
+ })
const result = buildConnectorsStatus(station)
MeterValues: [],
transactionEventQueue: [],
transactionUpdatedMeterValuesSetInterval: undefined,
- } as unknown as ConnectorStatus)
+ })
internals.evses.set(0, {
availability: AvailabilityType.Operative,
transactionEndedMeterValuesSetInterval: undefined,
transactionEventQueue: [],
transactionUpdatedMeterValuesSetInterval: undefined,
- } as unknown as ConnectorStatus)
+ })
internals.evses.set(0, {
availability: AvailabilityType.Operative,
evseConnectors.set(1, {
availability: AvailabilityType.Operative,
MeterValues: [],
- } as ConnectorStatus)
+ })
evseConnectors.set(2, {
availability: AvailabilityType.Inoperative,
MeterValues: [],
- } as ConnectorStatus)
+ })
const evse0Connectors = new Map<number, ConnectorStatus>()
evse0Connectors.set(0, {
availability: AvailabilityType.Operative,
MeterValues: [],
- } as ConnectorStatus)
+ })
internals.evses.set(0, {
availability: AvailabilityType.Operative,
internals.connectors.set(0, {
availability: AvailabilityType.Operative,
MeterValues: [],
- } as ConnectorStatus)
+ })
internals.connectors.set(1, {
availability: AvailabilityType.Operative,
MeterValues: [],
transactionEndedMeterValuesSetInterval: undefined,
transactionEventQueue: [],
transactionUpdatedMeterValuesSetInterval: undefined,
- } as unknown as ConnectorStatus)
+ })
const result = buildConnectorEntries(station)
internals.connectors.set(0, {
availability: AvailabilityType.Operative,
MeterValues: [],
- } as ConnectorStatus)
+ })
internals.connectors.set(3, {
availability: AvailabilityType.Operative,
MeterValues: [],
- } as ConnectorStatus)
+ })
internals.connectors.set(7, {
availability: AvailabilityType.Inoperative,
MeterValues: [],
- } as ConnectorStatus)
+ })
const result = buildConnectorEntries(station)
transactionEndedMeterValuesSetInterval: undefined,
transactionEventQueue: [],
transactionUpdatedMeterValuesSetInterval: undefined,
- } as unknown as ConnectorStatus)
+ })
internals.evses.set(0, {
availability: AvailabilityType.Operative,
evse2Connectors.set(2, {
availability: AvailabilityType.Operative,
MeterValues: [],
- } as ConnectorStatus)
+ })
evse2Connectors.set(5, {
availability: AvailabilityType.Inoperative,
MeterValues: [],
- } as ConnectorStatus)
+ })
internals.evses.set(0, {
availability: AvailabilityType.Operative,
const originalData = internals.configurationData
internals.configurationData = {
stationTemplateUrls: [],
- } as ConfigurationData
+ }
try {
const distribution = Configuration.getSupervisionUrlDistribution()
return Buffer.from(data).toString('utf-8')
}
if (Array.isArray(data)) {
- return Buffer.concat(data as Buffer[]).toString('utf-8')
+ return Buffer.concat(data).toString('utf-8')
}
return data
}
.option('--connector-ids <ids>', 'comma-separated connector IDs', parseCommaSeparatedInts)
.action(async (hashIds: string[], options: { connectorIds?: number[] }) => {
const payload: RequestPayload = {
- ...(pickPresent(options as Record<string, unknown>, ['connectorIds']) as RequestPayload),
+ ...(pickPresent(options, ['connectorIds']) as RequestPayload),
...buildHashIdsPayload(hashIds),
}
await runAction(program, ProcedureName.START_AUTOMATIC_TRANSACTION_GENERATOR, payload)
.option('--connector-ids <ids>', 'comma-separated connector IDs', parseCommaSeparatedInts)
.action(async (hashIds: string[], options: { connectorIds?: number[] }) => {
const payload: RequestPayload = {
- ...(pickPresent(options as Record<string, unknown>, ['connectorIds']) as RequestPayload),
+ ...(pickPresent(options, ['connectorIds']) as RequestPayload),
...buildHashIdsPayload(hashIds),
}
await runAction(program, ProcedureName.STOP_AUTOMATIC_TRANSACTION_GENERATOR, payload)
options: { data?: string; messageId?: string; payload?: string; vendorId?: string }
) => {
const payload: RequestPayload = {
- ...pickDefined(options as Record<string, unknown>, {
+ ...pickDefined(options, {
data: 'data',
messageId: 'messageId',
vendorId: 'vendorId',
}) => {
const payload: RequestPayload = {
numberOfStations: options.count,
- options: pickDefined(options as Record<string, unknown>, {
+ options: pickDefined(options, {
autoStart: 'autoStart',
baseName: 'baseName',
fixedName: 'fixedName',
.option('--delete-config', 'delete station configuration files')
.action(async (hashIds: string[], options: { deleteConfig?: true }) => {
const payload: RequestPayload = {
- ...(pickDefined(options as Record<string, unknown>, {
+ ...(pickDefined(options, {
deleteConfig: 'deleteConfiguration',
}) as RequestPayload),
...buildHashIdsPayload(hashIds),
) => {
const payload: RequestPayload = {
url: options.supervisionUrl,
- ...pickDefined(options as Record<string, unknown>, {
+ ...pickDefined(options, {
supervisionPassword: 'supervisionPassword',
supervisionUser: 'supervisionUser',
}),
if (typeof uiServer !== 'object') {
throw new Error('Config uiServer must be an object')
}
- return uiServer as Partial<UIServerConfigurationSection>
+ return uiServer
}
throw new Error(`Config file '${targetPath}' must contain a JSON object`)
} catch (error: unknown) {
connectorId: 1,
connectorStatus: {
availability: OCPP16AvailabilityType.OPERATIVE,
- status: undefined as unknown as OCPP16ChargePointStatus,
+ status: undefined,
},
},
]
const target = stream === 'stdout' ? process.stdout : process.stderr
const chunks: string[] = []
const original = target.write.bind(target)
- target.write = ((chunk: string): boolean => {
+ target.write = (chunk: string): boolean => {
chunks.push(chunk)
return true
- }) as typeof target.write
+ }
try {
fn()
} finally {
adapter.onerror = event => {
receivedMessage = event.message
}
- mockWs.onerror?.(42 as unknown as Error)
+ mockWs.onerror?.(42)
assert.strictEqual(receivedMessage, 'Unknown error')
})
responsePayload == null ||
typeof responsePayload !== 'object' ||
!('status' in responsePayload) ||
- typeof (responsePayload as { status: unknown }).status !== 'string'
+ typeof responsePayload.status !== 'string'
) {
this.settleHandler(uuid, handler)
handler.reject(new Error('Server sent malformed response payload'))
},
get readyState (): WebSocketReadyState {
- return ws.readyState as WebSocketReadyState
+ return ws.readyState
},
send (data: string): void {
await describe('generic WebSocket adapter factory', async () => {
await it('should apply dataConverter to message data', () => {
const mockWs = createMockRawWs()
- const adapter = createWsAdapter(mockWs as never, {
+ const adapter = createWsAdapter(mockWs, {
dataConverter: data => `converted:${String(data)}`,
})
let receivedData: string | undefined
await it('should use custom errorDefault in onerror fallback', () => {
const mockWs = createMockRawWs()
- const adapter = createWsAdapter(mockWs as never, {
+ const adapter = createWsAdapter(mockWs, {
dataConverter: data => data as string,
errorDefault: 'Custom fallback',
})
await it('should default errorDefault to WebSocket error', () => {
const mockWs = createMockRawWs()
- const adapter = createWsAdapter(mockWs as never, {
+ const adapter = createWsAdapter(mockWs, {
dataConverter: data => data as string,
})
let receivedMessage: string | undefined
sentData = data
}
mockWs.readyState = WebSocketReadyState.CONNECTING
- const adapter = createWsAdapter(mockWs as never, {
+ const adapter = createWsAdapter(mockWs, {
dataConverter: data => data as string,
})
adapter.close(1000)
await it('should forward onclose and onopen events', () => {
const mockWs = createMockRawWs()
- const adapter = createWsAdapter(mockWs as never, {
+ const adapter = createWsAdapter(mockWs, {
dataConverter: data => data as string,
})
let closeCode: number | undefined
// Test: MessageEvent data extraction
await it('should extract data from MessageEvent in onmessage', () => {
const mockWs = createMockBrowserWs()
- const adapter = createBrowserWsAdapter(mockWs as never)
+ const adapter = createBrowserWsAdapter(mockWs)
let receivedData: string | undefined
adapter.onmessage = event => {
receivedData = event.data
// Test: onerror produces WebSocketLike error shape
await it('should produce error shape from browser Event in onerror', () => {
const mockWs = createMockBrowserWs()
- const adapter = createBrowserWsAdapter(mockWs as never)
+ const adapter = createBrowserWsAdapter(mockWs)
let receivedError: unknown
let receivedMessage: string | undefined
adapter.onerror = event => {
// Test: onclose extracts code and reason
await it('should extract code and reason from CloseEvent in onclose', () => {
const mockWs = createMockBrowserWs()
- const adapter = createBrowserWsAdapter(mockWs as never)
+ const adapter = createBrowserWsAdapter(mockWs)
let receivedCode: number | undefined
let receivedReason: string | undefined
adapter.onclose = event => {
// Test: onopen forwarded
await it('should forward onopen callback', () => {
const mockWs = createMockBrowserWs()
- const adapter = createBrowserWsAdapter(mockWs as never)
+ const adapter = createBrowserWsAdapter(mockWs)
let opened = false
adapter.onopen = () => {
opened = true
mockWs.send = (data: string) => {
sentData = data
}
- const adapter = createBrowserWsAdapter(mockWs as never)
+ const adapter = createBrowserWsAdapter(mockWs)
adapter.send('{"test":1}')
assert.strictEqual(sentData, '{"test":1}')
})
await it('should delegate readyState to browser WebSocket', () => {
const mockWs = createMockBrowserWs()
mockWs.readyState = WebSocketReadyState.CONNECTING
- const adapter = createBrowserWsAdapter(mockWs as never)
+ const adapter = createBrowserWsAdapter(mockWs)
assert.strictEqual(adapter.readyState, WebSocketReadyState.CONNECTING)
})
mockWs.close = (code?: number) => {
closedCode = code
}
- const adapter = createBrowserWsAdapter(mockWs as never)
+ const adapter = createBrowserWsAdapter(mockWs)
adapter.close(1000)
assert.strictEqual(closedCode, 1000)
})
it('should ignore invalid theme name', () => {
const { activeThemeId, switchTheme } = useTheme()
const before = activeThemeId.value
- const switchThemeUntyped = switchTheme as (name: string) => void
+ const switchThemeUntyped = switchTheme
switchThemeUntyped('nonexistent')
expect(activeThemeId.value).toBe(before)
})
})
it('should label "Stop ATG" when running', () => {
- wrapper = mountRow({ atgStatus: { start: true } as Status })
+ wrapper = mountRow({ atgStatus: { start: true } })
expect(wrapper.text()).toContain('Stop ATG')
})
})
it('should call stopAutomaticTransactionGenerator when stopping', async () => {
- wrapper = mountRow({ atgStatus: { start: true } as Status })
+ wrapper = mountRow({ atgStatus: { start: true } })
const chip = wrapper.find('.modern-btn--chip')
await chip.trigger('click')
await flushPromises()
},
],
status: ResponseStatus.FAILURE,
- } as never)
+ })
)
await wrapper.find('#modern-tx-idtag').setValue('BAD-TAG')
await wrapper.findAll('.stub-modal__foot button')[1].trigger('click')
},
],
status: ResponseStatus.FAILURE,
- } as never)
+ })
)
await wrapper.find('#modern-auth-tag').setValue('BAD')
await wrapper.findAll('.stub-modal__foot button')[1].trigger('click')