From 2bee4896149f6000ecd27feb118d39a2406b1194 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Sun, 19 Jul 2026 18:17:58 +0200 Subject: [PATCH] =?utf8?q?refactor:=20code-quality=20cleanup=20=E2=80=94?= =?utf8?q?=20utils=20usage,=20logic=20consolidation,=20export=20hygiene=20?= =?utf8?q?(#2036)?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit * refactor(ui-server): use isEmpty for outstanding-hashid checks * refactor(ui-server): extract releaseRequest for broadcast request release * refactor(ocpp): lift requestHandler into base template method * refactor(utils): add isOCPP20x helper and route version predicates * refactor(charging-station): single-source the request-statistics gate * refactor(utils): use isNotEmptyString/isNotEmptyArray predicates * refactor: drop dead exports and redundant re-export * refactor(auth): rename AuthorizationStatus enum to AuthResultStatus * refactor(ocpp): drop now-redundant logRequestHandlerError module-name param * docs(charging-station): document recordRequestStatistic public method * refactor(ocpp): route VariableMetadata mutability/persistence checks through predicates * refactor(ocpp): unexport in-file-only parseJsonSchemaFile --- src/charging-station/ChargingStation.ts | 25 ++-- src/charging-station/ConfigurationKeyUtils.ts | 7 +- src/charging-station/TemplateSchema.ts | 5 - .../ChargingStationWorkerBroadcastChannel.ts | 5 +- .../UIServiceWorkerBroadcastChannel.ts | 31 ++--- src/charging-station/index.ts | 2 +- .../ocpp/1.6/OCPP16RequestService.ts | 107 +++++------------- .../ocpp/2.0/OCPP20IncomingRequestService.ts | 6 +- .../ocpp/2.0/OCPP20RequestService.ts | 78 +------------ .../ocpp/2.0/OCPP20VariableManager.ts | 34 +++--- .../ocpp/2.0/OCPP20VariableRegistry.ts | 9 ++ .../ocpp/OCPPRequestService.ts | 102 +++++++++++++---- .../ocpp/OCPPServiceOperations.ts | 2 +- src/charging-station/ocpp/OCPPServiceUtils.ts | 2 +- .../ocpp/auth/adapters/OCPP16AuthAdapter.ts | 4 +- .../ocpp/auth/adapters/OCPP20AuthAdapter.ts | 8 +- .../ocpp/auth/cache/InMemoryAuthCache.ts | 10 +- src/charging-station/ocpp/auth/index.ts | 2 +- .../ocpp/auth/interfaces/OCPPAuthService.ts | 4 +- .../ocpp/auth/services/OCPPAuthServiceImpl.ts | 10 +- .../strategies/CertificateAuthStrategy.ts | 18 +-- .../ocpp/auth/strategies/LocalAuthStrategy.ts | 26 ++--- .../ocpp/auth/types/AuthTypes.ts | 92 +++++++-------- .../ocpp/auth/utils/AuthHelpers.ts | 40 ++++--- src/charging-station/ui-server/UIMCPServer.ts | 3 +- .../ui-server/UIServerAccessPolicy.ts | 3 +- .../ui-services/AbstractUIService.ts | 7 +- src/utils/Configuration.ts | 2 +- src/utils/ConfigurationSchema.ts | 17 +-- src/utils/Utils.ts | 4 + src/utils/index.ts | 1 + src/worker/WorkerTypes.ts | 9 +- src/worker/index.ts | 1 - .../ChargingStation-RequestStatistic.test.ts | 75 ++++++++++++ .../OCPP16RequestService-CallChain.test.ts | 25 ++++ .../ocpp/1.6/OCPP16ServiceUtils.test.ts | 10 +- ...estService-RequestStartTransaction.test.ts | 8 +- .../OCPP20ResponseService-CacheUpdate.test.ts | 30 ++--- .../2.0/OCPP20ServiceUtils-AuthCache.test.ts | 6 +- .../ocpp/2.0/OCPP20VariableRegistry.test.ts | 74 ++++++++++++ .../ocpp/OCPPServiceOperations.test.ts | 22 ++-- .../ocpp/auth/OCPPAuthIntegration.test.ts | 8 +- .../auth/adapters/OCPP16AuthAdapter.test.ts | 6 +- .../auth/adapters/OCPP20AuthAdapter.test.ts | 12 +- .../ocpp/auth/cache/InMemoryAuthCache.test.ts | 52 ++++----- .../ocpp/auth/helpers/MockFactories.ts | 20 ++-- .../auth/services/OCPPAuthServiceImpl.test.ts | 6 +- .../CertificateAuthStrategy.test.ts | 14 +-- ...lAuthStrategy-DisablePostAuthorize.test.ts | 12 +- .../auth/strategies/LocalAuthStrategy.test.ts | 8 +- .../strategies/RemoteAuthStrategy.test.ts | 22 ++-- .../ocpp/auth/types/AuthTypes.test.ts | 32 +++--- .../auth/utils/AuthConfigValidator.test.ts | 28 ++--- .../ocpp/auth/utils/AuthHelpers.test.ts | 78 ++++++------- tests/utils/Utils.test.ts | 10 +- ui/common/src/client/WebSocketClient.ts | 2 - ui/common/tests/WebSocketClient.test.ts | 3 +- 57 files changed, 680 insertions(+), 559 deletions(-) create mode 100644 tests/charging-station/ChargingStation-RequestStatistic.test.ts create mode 100644 tests/charging-station/ocpp/2.0/OCPP20VariableRegistry.test.ts diff --git a/src/charging-station/ChargingStation.ts b/src/charging-station/ChargingStation.ts index 36343f82..41fa2cf5 100644 --- a/src/charging-station/ChargingStation.ts +++ b/src/charging-station/ChargingStation.ts @@ -100,6 +100,7 @@ import { isEmpty, isNotEmptyArray, isNotEmptyString, + isOCPP20x, JSONStringify, logger, logPrefix, @@ -1047,6 +1048,21 @@ export class ChargingStation extends EventEmitter { this.wsConnection.on('pong', this.onPong.bind(this)) } + /** + * Records a request statistic for the given command, but only when statistics + * collection is enabled on the station (`stationInfo.enableStatistics`). + * @param command - OCPP command the statistic is recorded against. + * @param messageType - Message type of the recorded exchange. + */ + public recordRequestStatistic ( + command: IncomingRequestCommand | RequestCommand, + messageType: MessageType + ): void { + if (this.stationInfo?.enableStatistics === true) { + this.performanceStatistics?.addRequestStatistic(command, messageType) + } + } + /** * Removes a reservation and restores the connector to its previous status. * @param reservation - The reservation to remove @@ -1653,10 +1669,7 @@ export class ChargingStation extends EventEmitter { } private getReconnectDelay (): number { - if ( - this.stationInfo?.ocppVersion === OCPPVersion.VERSION_20 || - this.stationInfo?.ocppVersion === OCPPVersion.VERSION_201 - ) { + if (isOCPP20x(this.stationInfo?.ocppVersion)) { return OCPP20ServiceUtils.computeReconnectDelay(this, this.wsConnectionRetryCount) } return this.stationInfo?.reconnectExponentialDelay === true @@ -1863,9 +1876,7 @@ export class ChargingStation extends EventEmitter { commandPayload ) } - if (this.stationInfo?.enableStatistics === true) { - this.performanceStatistics?.addRequestStatistic(commandName, messageType) - } + this.recordRequestStatistic(commandName, messageType) logger.debug( `${this.logPrefix()} ${moduleName}.handleIncomingMessage: << Command '${commandName}' received request payload: ${JSON.stringify( request diff --git a/src/charging-station/ConfigurationKeyUtils.ts b/src/charging-station/ConfigurationKeyUtils.ts index 1807c796..000561c6 100644 --- a/src/charging-station/ConfigurationKeyUtils.ts +++ b/src/charging-station/ConfigurationKeyUtils.ts @@ -4,11 +4,10 @@ import { type ConfigurationKey, type ConfigurationKeyType, OCPP20ComponentName, - OCPPVersion, StandardParametersKey, VendorParametersKey, } from '../types/index.js' -import { logger } from '../utils/index.js' +import { isOCPP20x, logger } from '../utils/index.js' export const buildConfigKey = (component: string, variable: string, instance?: string): string => { const base = `${component}.${variable}` @@ -122,7 +121,7 @@ const resolveKey = ( key: ConfigurationKeyType ): ConfigurationKeyType => { const version = chargingStation.stationInfo?.ocppVersion - if (version === OCPPVersion.VERSION_20 || version === OCPPVersion.VERSION_201) { + if (isOCPP20x(version)) { return OCPP2_PARAMETER_KEY_MAP.get(key) ?? key } return key @@ -130,7 +129,7 @@ const resolveKey = ( export const warnOnOCPP16TemplateKeys = (chargingStation: ChargingStation): void => { const version = chargingStation.stationInfo?.ocppVersion - if (version !== OCPPVersion.VERSION_20 && version !== OCPPVersion.VERSION_201) { + if (!isOCPP20x(version)) { return } const keys = chargingStation.ocppConfiguration?.configurationKey diff --git a/src/charging-station/TemplateSchema.ts b/src/charging-station/TemplateSchema.ts index 1c1b9399..5f22dd66 100644 --- a/src/charging-station/TemplateSchema.ts +++ b/src/charging-station/TemplateSchema.ts @@ -305,11 +305,6 @@ export const TemplateSchema = BaseTemplateSchema.superRefine((template, ctx) => } }) -/** - * StrictTemplateSchema — rejects unknown keys. For CI strict mode. - */ -export const StrictTemplateSchema = BaseTemplateSchema.strict() - // --------------------------------------------------------------- // Exported sub-schemas for reuse // --------------------------------------------------------------- diff --git a/src/charging-station/broadcast-channel/ChargingStationWorkerBroadcastChannel.ts b/src/charging-station/broadcast-channel/ChargingStationWorkerBroadcastChannel.ts index 2e5035ed..45f8977a 100644 --- a/src/charging-station/broadcast-channel/ChargingStationWorkerBroadcastChannel.ts +++ b/src/charging-station/broadcast-channel/ChargingStationWorkerBroadcastChannel.ts @@ -46,6 +46,7 @@ import { getErrorMessage, isAsyncFunction, isEmpty, + isOCPP20x, logger, } from '../../utils/index.js' import { getConfigurationKey } from '../ConfigurationKeyUtils.js' @@ -414,9 +415,7 @@ export class ChargingStationWorkerBroadcastChannel extends WorkerBroadcastChanne ) } const transactionId = this.chargingStation.getConnectorStatus(connectorId)?.transactionId - const isOcpp2 = - this.chargingStation.stationInfo?.ocppVersion === OCPPVersion.VERSION_20 || - this.chargingStation.stationInfo?.ocppVersion === OCPPVersion.VERSION_201 + const isOcpp2 = isOCPP20x(this.chargingStation.stationInfo?.ocppVersion) const interval = isOcpp2 ? OCPP20ServiceUtils.getAlignedDataInterval(this.chargingStation) : (() => { diff --git a/src/charging-station/broadcast-channel/UIServiceWorkerBroadcastChannel.ts b/src/charging-station/broadcast-channel/UIServiceWorkerBroadcastChannel.ts index f0d26d1b..a5aa33eb 100644 --- a/src/charging-station/broadcast-channel/UIServiceWorkerBroadcastChannel.ts +++ b/src/charging-station/broadcast-channel/UIServiceWorkerBroadcastChannel.ts @@ -40,12 +40,7 @@ export class UIServiceWorkerBroadcastChannel extends WorkerBroadcastChannel { // Already completed and released by the normal response path. return } - try { - this.uiService.sendResponse(uuid, this.buildTimeoutResponsePayload(uuid)) - } finally { - this.responses.delete(uuid) - this.uiService.deleteBroadcastChannelRequest(uuid) - } + this.releaseRequest(uuid, this.buildTimeoutResponsePayload(uuid)) } /** @@ -115,13 +110,7 @@ export class UIServiceWorkerBroadcastChannel extends WorkerBroadcastChannel { } private completeRequest (uuid: UUIDv4): void { - // Always release aggregation state, even if downstream sendResponse throws. - try { - this.uiService.sendResponse(uuid, this.buildResponsePayload(uuid)) - } finally { - this.responses.delete(uuid) - this.uiService.deleteBroadcastChannelRequest(uuid) - } + this.releaseRequest(uuid, this.buildResponsePayload(uuid)) } private messageErrorHandler (messageEvent: MessageEvent): void { @@ -131,6 +120,22 @@ export class UIServiceWorkerBroadcastChannel extends WorkerBroadcastChannel { ) } + /** + * Sends the given response payload for a broadcast request and always + * releases its aggregation state afterwards, even if the downstream + * sendResponse throws. + * @param uuid - Request identifier. + * @param responsePayload - Aggregated response payload to send. + */ + private releaseRequest (uuid: UUIDv4, responsePayload: ResponsePayload): void { + try { + this.uiService.sendResponse(uuid, responsePayload) + } finally { + this.responses.delete(uuid) + this.uiService.deleteBroadcastChannelRequest(uuid) + } + } + private responseHandler (messageEvent: MessageEvent): void { const validatedMessageEvent = this.validateMessageEvent(messageEvent) if (validatedMessageEvent === false) { diff --git a/src/charging-station/index.ts b/src/charging-station/index.ts index 0c7fc720..d1e1bf54 100644 --- a/src/charging-station/index.ts +++ b/src/charging-station/index.ts @@ -45,5 +45,5 @@ export { IdTagsCache } from './IdTagsCache.js' export type { CoherentSession } from './meter-values/index.js' export { SharedLRUCache } from './SharedLRUCache.js' export { applyMigration, coerceVersion, CURRENT_SCHEMA_VERSION } from './TemplateMigrations.js' -export { StrictTemplateSchema, TemplateSchema } from './TemplateSchema.js' +export { TemplateSchema } from './TemplateSchema.js' export { TemplateValidationError, validateTemplate } from './TemplateValidation.js' diff --git a/src/charging-station/ocpp/1.6/OCPP16RequestService.ts b/src/charging-station/ocpp/1.6/OCPP16RequestService.ts index d03c7678..9da1a59d 100644 --- a/src/charging-station/ocpp/1.6/OCPP16RequestService.ts +++ b/src/charging-station/ocpp/1.6/OCPP16RequestService.ts @@ -14,12 +14,11 @@ import { type OCPP16StartTransactionRequest, type OCPP16StatusNotificationRequest, OCPPVersion, - type RequestParams, } from '../../../types/index.js' -import { assertIsJsonObject, generateUUID, logger } from '../../../utils/index.js' +import { assertIsJsonObject, logger } from '../../../utils/index.js' import { sendAndSetConnectorStatus } from '../OCPPConnectorStatusOperations.js' import { OCPPRequestService } from '../OCPPRequestService.js' -import { createPayloadValidatorMap, isRequestCommandSupported } from '../OCPPServiceUtils.js' +import { createPayloadValidatorMap } from '../OCPPServiceUtils.js' import { OCPP16Constants } from './OCPP16Constants.js' import { OCPP16ServiceUtils } from './OCPP16ServiceUtils.js' @@ -55,88 +54,12 @@ export class OCPP16RequestService extends OCPPRequestService { * @param ocppResponseService - The response service instance for handling responses */ public constructor (ocppResponseService: OCPPResponseService) { - super(OCPPVersion.VERSION_16, ocppResponseService) + super(OCPPVersion.VERSION_16, ocppResponseService, moduleName) this.payloadValidatorFunctions = createPayloadValidatorMap( OCPP16ServiceUtils.createRequestPayloadConfigs(), OCPP16ServiceUtils.createPayloadOptions(moduleName, 'constructor'), this.ajv ) - this.buildRequestPayload = this.buildRequestPayload.bind(this) - } - - /** - * Handles OCPP 1.6 request processing with full validation and error handling - * - * This method serves as the main entry point for all outgoing OCPP 1.6 requests. - * It performs the following operations: - * - Validates that the requested command is supported by the charging station - * - Builds and validates the request payload according to OCPP 1.6 schemas - * - Sends the request to the Central System with proper error handling - * - Processes responses with comprehensive logging and error recovery - * - * The method ensures type safety through generic type parameters while maintaining - * backward compatibility with the OCPP 1.6 specification. - * @template RequestType - The expected type of the request parameters - * @template ResponseType - The expected type of the response from the Central System - * @param chargingStation - The charging station instance making the request - * @param commandName - The OCPP 1.6 command to execute (e.g., 'StartTransaction', 'StopTransaction') - * @param commandParams - Optional parameters specific to the command being executed - * @param params - Optional request parameters for controlling request behavior - * @returns Promise resolving to the typed response from the Central System - * @throws {OCPPError} When the command is not supported or validation fails - */ - // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters - public async requestHandler( - chargingStation: ChargingStation, - commandName: OCPP16RequestCommand, - commandParams?: RequestType, - params?: RequestParams - ): Promise { - logger.debug( - `${chargingStation.logPrefix()} ${moduleName}.requestHandler: Processing '${commandName}' request` - ) - if (isRequestCommandSupported(chargingStation, commandName)) { - try { - logger.debug( - `${chargingStation.logPrefix()} ${moduleName}.requestHandler: Building request payload for '${commandName}'` - ) - const requestPayload = - params?.rawPayload === true - ? (commandParams as RequestType) - : this.buildRequestPayload(chargingStation, commandName, commandParams) - const messageId = generateUUID() - logger.debug( - `${chargingStation.logPrefix()} ${moduleName}.requestHandler: Sending '${commandName}' request with message ID '${messageId}'` - ) - // Pre request actions hook - switch (commandName) { - case OCPP16RequestCommand.START_TRANSACTION: - await sendAndSetConnectorStatus(chargingStation, { - connectorId: (commandParams as OCPP16StartTransactionRequest).connectorId, - status: OCPP16ChargePointStatus.Preparing, - }) - break - } - const response = (await this.sendMessage( - chargingStation, - messageId, - requestPayload, - commandName, - params - )) as ResponseType - logger.debug( - `${chargingStation.logPrefix()} ${moduleName}.requestHandler: '${commandName}' request completed successfully` - ) - return response - } catch (error) { - this.logRequestHandlerError(chargingStation, moduleName, commandName, error) - throw error - } - } - // OCPPError usage here is debatable: it's an error in the OCPP stack but not targeted to sendError(). - const errorMsg = `Unsupported OCPP command ${commandName}` - logger.error(`${chargingStation.logPrefix()} ${moduleName}.requestHandler: ${errorMsg}`) - throw new OCPPError(ErrorType.NOT_SUPPORTED, errorMsg, commandName, commandParams) } /** @@ -158,7 +81,7 @@ export class OCPP16RequestService extends OCPPRequestService { * @param commandParams - Optional parameters provided by the caller for payload construction * @returns The fully constructed and validated request payload ready for transmission */ - private buildRequestPayload ( + protected buildRequestPayload ( chargingStation: ChargingStation, commandName: OCPP16RequestCommand, commandParams?: JsonType @@ -283,4 +206,26 @@ export class OCPP16RequestService extends OCPPRequestService { } } } + + /** + * Runs the OCPP 1.6 pre-request actions hook: for StartTransaction it sends + * and sets the connector status to Preparing before the request is sent. + * @param chargingStation - The charging station instance making the request + * @param commandName - The OCPP 1.6 command being sent + * @param commandParams - Optional parameters provided by the caller + */ + protected override async preRequestHook ( + chargingStation: ChargingStation, + commandName: OCPP16RequestCommand, + commandParams?: JsonType + ): Promise { + switch (commandName) { + case OCPP16RequestCommand.START_TRANSACTION: + await sendAndSetConnectorStatus(chargingStation, { + connectorId: (commandParams as OCPP16StartTransactionRequest).connectorId, + status: OCPP16ChargePointStatus.Preparing, + }) + break + } + } } diff --git a/src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.ts b/src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.ts index 286e7007..a363d407 100644 --- a/src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.ts +++ b/src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.ts @@ -162,7 +162,7 @@ import { } from '../../index.js' import { AuthContext, - AuthorizationStatus, + AuthResultStatus, type DifferentialAuthEntry, type LocalAuthEntry, mapOCPP20TokenType, @@ -1158,13 +1158,13 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService( - chargingStation: ChargingStation, - commandName: OCPP20RequestCommand, - commandParams?: RequestType, - params?: RequestParams - ): Promise { - logger.debug( - `${chargingStation.logPrefix()} ${moduleName}.requestHandler: Processing '${commandName}' request` - ) - if (isRequestCommandSupported(chargingStation, commandName)) { - try { - logger.debug( - `${chargingStation.logPrefix()} ${moduleName}.requestHandler: Building request payload for '${commandName}'` - ) - const requestPayload = - params?.rawPayload === true - ? (commandParams as RequestType) - : this.buildRequestPayload(chargingStation, commandName, commandParams) - const messageId = generateUUID() - logger.debug( - `${chargingStation.logPrefix()} ${moduleName}.requestHandler: Sending '${commandName}' request with message ID '${messageId}'` - ) - const response = (await this.sendMessage( - chargingStation, - messageId, - requestPayload, - commandName, - params - )) as ResponseType - logger.debug( - `${chargingStation.logPrefix()} ${moduleName}.requestHandler: '${commandName}' request completed successfully` - ) - return response - } catch (error) { - this.logRequestHandlerError(chargingStation, moduleName, commandName, error) - throw error - } - } - // OCPPError usage here is debatable: it's an error in the OCPP stack but not targeted to sendError(). - const errorMsg = `Unsupported OCPP command ${commandName}` - logger.error(`${chargingStation.logPrefix()} ${moduleName}.requestHandler: ${errorMsg}`) - throw new OCPPError(ErrorType.NOT_SUPPORTED, errorMsg, commandName, commandParams) - } - - private buildRequestPayload ( + protected buildRequestPayload ( chargingStation: ChargingStation, commandName: OCPP20RequestCommand, commandParams?: JsonType diff --git a/src/charging-station/ocpp/2.0/OCPP20VariableManager.ts b/src/charging-station/ocpp/2.0/OCPP20VariableManager.ts index 0ebc9070..ccfb695f 100644 --- a/src/charging-station/ocpp/2.0/OCPP20VariableManager.ts +++ b/src/charging-station/ocpp/2.0/OCPP20VariableManager.ts @@ -6,7 +6,6 @@ import { type ComponentType, DataEnumType, GetVariableStatusEnumType, - MutabilityEnumType, OCPP20ComponentName, type OCPP20GetVariableDataType, type OCPP20GetVariableResultType, @@ -14,7 +13,6 @@ import { OCPP20RequiredVariableName, type OCPP20SetVariableDataType, type OCPP20SetVariableResultType, - PersistenceEnumType, ReasonCodeEnumType, SetVariableStatusEnumType, type VariableType, @@ -33,6 +31,10 @@ import { buildCaseInsensitiveCompositeKey, enforceReportingValueSize, getVariableMetadata, + isPersistent, + isReadOnly, + isVolatile, + isWriteOnly, resolveValue, validateValue, VARIABLE_REGISTRY, @@ -251,10 +253,10 @@ export class OCPP20VariableManager { for (const metaKey of Object.keys(VARIABLE_REGISTRY)) { const variableMetadata = VARIABLE_REGISTRY[metaKey] // Enforce persistent non-write-only variables across components - if (variableMetadata.persistence !== PersistenceEnumType.Persistent) { + if (!isPersistent(variableMetadata)) { continue } - if (variableMetadata.mutability === MutabilityEnumType.WriteOnly) { + if (isWriteOnly(variableMetadata)) { continue } // Instance-scoped persistent variables are also auto-created when defaultValue is defined @@ -387,7 +389,8 @@ export class OCPP20VariableManager { variable.instance ?? component.instance ) if ( - variableMetadata?.mutability === MutabilityEnumType.WriteOnly && + variableMetadata != null && + isWriteOnly(variableMetadata) && resolvedAttributeType === AttributeEnumType.Actual ) { return this.rejectGet( @@ -626,10 +629,7 @@ export class OCPP20VariableManager { let value = resolveValue(chargingStation, variableMetadata) - if ( - variableMetadata.persistence === PersistenceEnumType.Persistent && - variableMetadata.mutability !== MutabilityEnumType.WriteOnly - ) { + if (isPersistent(variableMetadata) && !isWriteOnly(variableMetadata)) { const configurationKeyName = computeConfigurationKeyName(variableMetadata) let cfg = getConfigurationKey(chargingStation, configurationKeyName) @@ -651,10 +651,7 @@ export class OCPP20VariableManager { } } - if ( - variableMetadata.persistence === PersistenceEnumType.Volatile && - variableMetadata.mutability !== MutabilityEnumType.ReadOnly - ) { + if (isVolatile(variableMetadata) && !isReadOnly(variableMetadata)) { const stationId = this.getStationId(chargingStation) const override = this.getRuntimeOverrides(stationId).get(compositeKey) if (override != null) { @@ -737,7 +734,7 @@ export class OCPP20VariableManager { variable.name ) if (invalidVariables.has(variableKey) && resolvedAttributeType === AttributeEnumType.Actual) { - if (variableMetadata.mutability !== MutabilityEnumType.WriteOnly) { + if (!isWriteOnly(variableMetadata)) { return this.rejectSet( variable, component, @@ -857,7 +854,7 @@ export class OCPP20VariableManager { } } - if (variableMetadata.mutability === MutabilityEnumType.ReadOnly) { + if (isReadOnly(variableMetadata)) { return this.rejectSet( variable, component, @@ -1001,10 +998,7 @@ export class OCPP20VariableManager { const configurationKeyName = computeConfigurationKeyName(variableMetadata) const previousValue = getConfigurationKey(chargingStation, configurationKeyName)?.value - if ( - variableMetadata.persistence === PersistenceEnumType.Persistent && - variableMetadata.mutability !== MutabilityEnumType.WriteOnly - ) { + if (isPersistent(variableMetadata) && !isWriteOnly(variableMetadata)) { const configKey = getConfigurationKey(chargingStation, configurationKeyName) if (configKey == null) { addConfigurationKey(chargingStation, configurationKeyName, attributeValue, undefined, { @@ -1034,7 +1028,7 @@ export class OCPP20VariableManager { chargingStation.restartWebSocketPing() } // Apply volatile runtime override generically (single location) - if (variableMetadata.persistence === PersistenceEnumType.Volatile) { + if (isVolatile(variableMetadata)) { this.getRuntimeOverrides(stationId).set(variableKey, attributeValue) } diff --git a/src/charging-station/ocpp/2.0/OCPP20VariableRegistry.ts b/src/charging-station/ocpp/2.0/OCPP20VariableRegistry.ts index 76369304..6694394c 100644 --- a/src/charging-station/ocpp/2.0/OCPP20VariableRegistry.ts +++ b/src/charging-station/ocpp/2.0/OCPP20VariableRegistry.ts @@ -2609,6 +2609,15 @@ export function isReadOnly (variableMetadata: VariableMetadata): boolean { return variableMetadata.mutability === MutabilityEnumType.ReadOnly } +/** + * Check if variable metadata is volatile. + * @param variableMetadata - Variable metadata entry. + * @returns `true` when persistence is Volatile. + */ +export function isVolatile (variableMetadata: VariableMetadata): boolean { + return variableMetadata.persistence === PersistenceEnumType.Volatile +} + /** * Check if variable metadata is write-only. * @param variableMetadata - Variable metadata entry. diff --git a/src/charging-station/ocpp/OCPPRequestService.ts b/src/charging-station/ocpp/OCPPRequestService.ts index 79329988..a93057d4 100644 --- a/src/charging-station/ocpp/OCPPRequestService.ts +++ b/src/charging-station/ocpp/OCPPRequestService.ts @@ -24,13 +24,19 @@ import { import { ensureError, formatDurationMilliSeconds, + generateUUID, getErrorMessage, getMessageTypeString, handleSendMessageError, logger, } from '../../utils/index.js' import { OCPPConstants } from './OCPPConstants.js' -import { type Ajv, createAjv, validatePayload } from './OCPPServiceUtils.js' +import { + type Ajv, + createAjv, + isRequestCommandSupported, + validatePayload, +} from './OCPPServiceUtils.js' const defaultRequestParams: RequestParams = { skipBufferingOnError: false, @@ -47,15 +53,22 @@ export abstract class OCPPRequestService { >() protected readonly ajv: Ajv + protected readonly moduleName: string protected abstract payloadValidatorFunctions: Map> private readonly ocppResponseService: OCPPResponseService private readonly version: OCPPVersion - protected constructor (version: OCPPVersion, ocppResponseService: OCPPResponseService) { + protected constructor ( + version: OCPPVersion, + ocppResponseService: OCPPResponseService, + moduleName: string + ) { this.version = version + this.moduleName = moduleName this.ajv = createAjv() this.ocppResponseService = ocppResponseService this.requestHandler = this.requestHandler.bind(this) + this.buildRequestPayload = this.buildRequestPayload.bind(this) this.sendMessage = this.sendMessage.bind(this) this.sendResponse = this.sendResponse.bind(this) this.sendError = this.sendError.bind(this) @@ -85,12 +98,50 @@ export abstract class OCPPRequestService { * @returns Response payload from the Central System. */ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters - public abstract requestHandler( + public async requestHandler( chargingStation: ChargingStation, commandName: RequestCommand, commandParams?: ReqType, params?: RequestParams - ): Promise + ): Promise { + logger.debug( + `${chargingStation.logPrefix()} ${this.moduleName}.requestHandler: Processing '${commandName}' request` + ) + if (isRequestCommandSupported(chargingStation, commandName)) { + try { + logger.debug( + `${chargingStation.logPrefix()} ${this.moduleName}.requestHandler: Building request payload for '${commandName}'` + ) + const requestPayload = + params?.rawPayload === true + ? (commandParams as ReqType) + : this.buildRequestPayload(chargingStation, commandName, commandParams) + const messageId = generateUUID() + logger.debug( + `${chargingStation.logPrefix()} ${this.moduleName}.requestHandler: Sending '${commandName}' request with message ID '${messageId}'` + ) + await this.preRequestHook(chargingStation, commandName, commandParams) + const response = (await this.sendMessage( + chargingStation, + messageId, + requestPayload, + commandName, + params + )) as ResType + logger.debug( + `${chargingStation.logPrefix()} ${this.moduleName}.requestHandler: '${commandName}' request completed successfully` + ) + return response + } catch (error) { + this.logRequestHandlerError(chargingStation, commandName, error) + throw error + } + } + // OCPPError usage here is debatable: it's an error in the OCPP stack but not targeted to sendError(). + const errorMsg = `Unsupported OCPP command ${commandName}` + logger.error(`${chargingStation.logPrefix()} ${this.moduleName}.requestHandler: ${errorMsg}`) + throw new OCPPError(ErrorType.NOT_SUPPORTED, errorMsg, commandName, commandParams) + } public async sendError ( chargingStation: ChargingStation, @@ -145,18 +196,39 @@ export abstract class OCPPRequestService { } } + protected abstract buildRequestPayload ( + chargingStation: ChargingStation, + commandName: RequestCommand, + commandParams?: JsonType + ): JsonType + protected logRequestHandlerError ( chargingStation: ChargingStation, - subclassModuleName: string, commandName: RequestCommand, error: unknown ): void { logger.error( - `${chargingStation.logPrefix()} ${subclassModuleName}.requestHandler: Error processing '${commandName}' request:`, + `${chargingStation.logPrefix()} ${this.moduleName}.requestHandler: Error processing '${commandName}' request:`, error ) } + /** + * Pre-request actions hook run after message id generation and before the + * request is sent. Base implementation is a no-op; overridden per OCPP + * version that needs a version-specific step (e.g. OCPP 1.6 StartTransaction). + * @param _chargingStation - Target charging station. + * @param _commandName - OCPP request command name. + * @param _commandParams - Optional request payload. + */ + protected preRequestHook ( + _chargingStation: ChargingStation, + _commandName: RequestCommand, + _commandParams?: JsonType + ): Promise | void { + /* No-op by default */ + } + protected async sendMessage ( chargingStation: ChargingStation, messageId: string, @@ -319,12 +391,7 @@ export abstract class OCPPRequestService { * @param requestPayload - The original request payload */ const responseCallback = (payload: JsonType, requestPayload: JsonType): void => { - if (chargingStation.stationInfo?.enableStatistics === true) { - chargingStation.performanceStatistics?.addRequestStatistic( - commandName, - MessageType.CALL_RESULT_MESSAGE - ) - } + chargingStation.recordRequestStatistic(commandName, MessageType.CALL_RESULT_MESSAGE) self.ocppResponseService .responseHandler( chargingStation, @@ -349,11 +416,8 @@ export abstract class OCPPRequestService { * @param requestStatistic - Whether to record request statistics */ const errorCallback = (ocppError: OCPPError, requestStatistic = true): void => { - if (requestStatistic && chargingStation.stationInfo?.enableStatistics === true) { - chargingStation.performanceStatistics?.addRequestStatistic( - commandName, - MessageType.CALL_ERROR_MESSAGE - ) + if (requestStatistic) { + chargingStation.recordRequestStatistic(commandName, MessageType.CALL_ERROR_MESSAGE) } logger.error( `${chargingStation.logPrefix()} Error occurred at ${getMessageTypeString( @@ -390,9 +454,7 @@ export abstract class OCPPRequestService { reject(ocppError) } - if (chargingStation.stationInfo?.enableStatistics === true) { - chargingStation.performanceStatistics?.addRequestStatistic(commandName, messageType) - } + chargingStation.recordRequestStatistic(commandName, messageType) const messageToSend = this.buildMessageToSend( chargingStation, messageId, diff --git a/src/charging-station/ocpp/OCPPServiceOperations.ts b/src/charging-station/ocpp/OCPPServiceOperations.ts index 44ffb050..4312f1df 100644 --- a/src/charging-station/ocpp/OCPPServiceOperations.ts +++ b/src/charging-station/ocpp/OCPPServiceOperations.ts @@ -17,7 +17,7 @@ import { OCPP20ServiceUtils } from './2.0/OCPP20ServiceUtils.js' import { AuthContext, AuthenticationMethod, - AuthorizationStatus as AuthStatus, + AuthResultStatus as AuthStatus, IdentifierType, OCPPAuthServiceFactory, } from './auth/index.js' diff --git a/src/charging-station/ocpp/OCPPServiceUtils.ts b/src/charging-station/ocpp/OCPPServiceUtils.ts index 65acfb2b..b0b9447c 100644 --- a/src/charging-station/ocpp/OCPPServiceUtils.ts +++ b/src/charging-station/ocpp/OCPPServiceUtils.ts @@ -1962,7 +1962,7 @@ export const PayloadValidatorOptions = ( * @returns Parsed JSON schema object * @throws {NodeJS.ErrnoException} If the schema file cannot be read or parsed */ -export function parseJsonSchemaFile ( +function parseJsonSchemaFile ( relativePath: string, ocppVersion: OCPPVersion, moduleName?: string, diff --git a/src/charging-station/ocpp/auth/adapters/OCPP16AuthAdapter.ts b/src/charging-station/ocpp/auth/adapters/OCPP16AuthAdapter.ts index c611a576..d0c9ad77 100644 --- a/src/charging-station/ocpp/auth/adapters/OCPP16AuthAdapter.ts +++ b/src/charging-station/ocpp/auth/adapters/OCPP16AuthAdapter.ts @@ -28,7 +28,7 @@ import { import { AuthContext, AuthenticationMethod, - AuthorizationStatus, + AuthResultStatus, IdentifierType, mapOCPP16Status, mapToOCPP16Status, @@ -118,7 +118,7 @@ export class OCPP16AuthAdapter implements OCPPAuthAdapter { }, isOffline: false, method: AuthenticationMethod.REMOTE_AUTHORIZATION, - status: AuthorizationStatus.INVALID, + status: AuthResultStatus.INVALID, timestamp: new Date(), } } diff --git a/src/charging-station/ocpp/auth/adapters/OCPP20AuthAdapter.ts b/src/charging-station/ocpp/auth/adapters/OCPP20AuthAdapter.ts index 51c796bc..7e6318a4 100644 --- a/src/charging-station/ocpp/auth/adapters/OCPP20AuthAdapter.ts +++ b/src/charging-station/ocpp/auth/adapters/OCPP20AuthAdapter.ts @@ -27,7 +27,7 @@ import { getErrorMessage, isEmpty, logger, truncateId } from '../../../../utils/ import { AuthContext, AuthenticationMethod, - AuthorizationStatus, + AuthResultStatus, IdentifierType, mapOCPP20AuthorizationStatus, mapOCPP20TokenType, @@ -77,7 +77,7 @@ export class OCPP20AuthAdapter implements OCPPAuthAdapter { }, isOffline: false, method: AuthenticationMethod.REMOTE_AUTHORIZATION, - status: AuthorizationStatus.INVALID, + status: AuthResultStatus.INVALID, timestamp: new Date(), } } @@ -94,7 +94,7 @@ export class OCPP20AuthAdapter implements OCPPAuthAdapter { }, isOffline: false, method: AuthenticationMethod.REMOTE_AUTHORIZATION, - status: AuthorizationStatus.INVALID, + status: AuthResultStatus.INVALID, timestamp: new Date(), } } @@ -147,7 +147,7 @@ export class OCPP20AuthAdapter implements OCPPAuthAdapter { }, isOffline: false, method: AuthenticationMethod.REMOTE_AUTHORIZATION, - status: AuthorizationStatus.INVALID, + status: AuthResultStatus.INVALID, timestamp: new Date(), } } diff --git a/src/charging-station/ocpp/auth/cache/InMemoryAuthCache.ts b/src/charging-station/ocpp/auth/cache/InMemoryAuthCache.ts index 29c1f5d7..b0de8b99 100644 --- a/src/charging-station/ocpp/auth/cache/InMemoryAuthCache.ts +++ b/src/charging-station/ocpp/auth/cache/InMemoryAuthCache.ts @@ -4,7 +4,7 @@ import type { AuthCache, CacheStats } from '../interfaces/OCPPAuthService.js' import type { AuthorizationResult } from '../types/AuthTypes.js' import { Constants, isEmpty, logger, roundTo, truncateId } from '../../../../utils/index.js' -import { AuthorizationStatus } from '../types/AuthTypes.js' +import { AuthResultStatus } from '../types/AuthTypes.js' const moduleName = 'InMemoryAuthCache' @@ -192,7 +192,7 @@ export class InMemoryAuthCache implements AuthCache { if (now >= authCacheEntry.expiresAt) { this.stats.expired++ // Transition to EXPIRED status instead of deleting (C10.FR.08) - authCacheEntry.result = { ...authCacheEntry.result, status: AuthorizationStatus.EXPIRED } + authCacheEntry.result = { ...authCacheEntry.result, status: AuthResultStatus.EXPIRED } // Apply absolute lifetime cap to expired-transition TTL refresh (default-TTL entries only) if (!authCacheEntry.hasExplicitTtl) { const absoluteDeadline = authCacheEntry.createdAt + this.maxAbsoluteLifetimeMs @@ -293,13 +293,13 @@ export class InMemoryAuthCache implements AuthCache { const now = Date.now() for (const [key, entry] of this.cache.entries()) { if (now >= entry.expiresAt) { - if (entry.result.status === AuthorizationStatus.EXPIRED) { + if (entry.result.status === AuthResultStatus.EXPIRED) { // Already transitioned by get() — delete on second expiration cycle this.cache.delete(key) this.lruOrder.delete(key) } else { // First expiration — transition to EXPIRED status (consistent with get() C10.FR.08 semantics) - entry.result = { ...entry.result, status: AuthorizationStatus.EXPIRED } + entry.result = { ...entry.result, status: AuthResultStatus.EXPIRED } if (!entry.hasExplicitTtl) { const absoluteDeadline = entry.createdAt + this.maxAbsoluteLifetimeMs if (absoluteDeadline > now) { @@ -439,7 +439,7 @@ export class InMemoryAuthCache implements AuthCache { for (const [identifier, accessTime] of this.lruOrder.entries()) { const authCacheEntry = this.cache.get(identifier) if ( - authCacheEntry?.result.status !== AuthorizationStatus.ACCEPTED && + authCacheEntry?.result.status !== AuthResultStatus.ACCEPTED && accessTime < candidateTime ) { candidateTime = accessTime diff --git a/src/charging-station/ocpp/auth/index.ts b/src/charging-station/ocpp/auth/index.ts index fd9cc060..6a7e0056 100644 --- a/src/charging-station/ocpp/auth/index.ts +++ b/src/charging-station/ocpp/auth/index.ts @@ -27,8 +27,8 @@ export { AuthenticationMethod, AuthErrorCode, type AuthorizationResult, - AuthorizationStatus, type AuthRequest, + AuthResultStatus, type CertificateHashData, type Identifier, IdentifierType, diff --git a/src/charging-station/ocpp/auth/interfaces/OCPPAuthService.ts b/src/charging-station/ocpp/auth/interfaces/OCPPAuthService.ts index 3d80b236..783a37cf 100644 --- a/src/charging-station/ocpp/auth/interfaces/OCPPAuthService.ts +++ b/src/charging-station/ocpp/auth/interfaces/OCPPAuthService.ts @@ -2,8 +2,8 @@ import type { JsonObject, OCPPVersion } from '../../../../types/index.js' import type { AuthConfiguration, AuthorizationResult, - AuthorizationStatus, AuthRequest, + AuthResultStatus, Identifier, IdentifierType, } from '../types/AuthTypes.js' @@ -483,7 +483,7 @@ export interface OCPPAuthService { */ updateCacheEntry( identifier: string, - status: AuthorizationStatus, + status: AuthResultStatus, expiryDate?: Date | string, identifierType?: IdentifierType ): void diff --git a/src/charging-station/ocpp/auth/services/OCPPAuthServiceImpl.ts b/src/charging-station/ocpp/auth/services/OCPPAuthServiceImpl.ts index aec52fda..c45ec094 100644 --- a/src/charging-station/ocpp/auth/services/OCPPAuthServiceImpl.ts +++ b/src/charging-station/ocpp/auth/services/OCPPAuthServiceImpl.ts @@ -28,8 +28,8 @@ import { AuthContext, AuthenticationMethod, type AuthorizationResult, - AuthorizationStatus, type AuthRequest, + AuthResultStatus, type Identifier, IdentifierType, } from '../types/AuthTypes.js' @@ -192,7 +192,7 @@ export class OCPPAuthServiceImpl implements OCPPAuthService { }, isOffline: false, method: AuthenticationMethod.NONE, - status: AuthorizationStatus.INVALID, + status: AuthResultStatus.INVALID, timestamp: new Date(), } } @@ -470,7 +470,7 @@ export class OCPPAuthServiceImpl implements OCPPAuthService { public updateCacheEntry ( identifier: string, - status: AuthorizationStatus, + status: AuthResultStatus, expiryDate?: Date | string, identifierType?: IdentifierType ): void { @@ -600,7 +600,7 @@ export class OCPPAuthServiceImpl implements OCPPAuthService { ocppVersion: this.chargingStation.stationInfo?.ocppVersion, offlineAuthorizationEnabled: true, remoteAuthorization: true, - unknownIdAuthorization: AuthorizationStatus.INVALID, + unknownIdAuthorization: AuthResultStatus.INVALID, } } @@ -680,7 +680,7 @@ export class OCPPAuthServiceImpl implements OCPPAuthService { ): void { this.metrics.totalResponseTime += duration - if (result.status === AuthorizationStatus.ACCEPTED) { + if (result.status === AuthResultStatus.ACCEPTED) { this.metrics.successfulAuth++ } else { this.metrics.failedAuth++ diff --git a/src/charging-station/ocpp/auth/strategies/CertificateAuthStrategy.ts b/src/charging-station/ocpp/auth/strategies/CertificateAuthStrategy.ts index 5cfc278e..2ad6c108 100644 --- a/src/charging-station/ocpp/auth/strategies/CertificateAuthStrategy.ts +++ b/src/charging-station/ocpp/auth/strategies/CertificateAuthStrategy.ts @@ -10,7 +10,7 @@ import type { import { OCPPVersion } from '../../../../types/index.js' import { isNotEmptyString, logger, sleep } from '../../../../utils/index.js' -import { AuthenticationMethod, AuthorizationStatus, IdentifierType } from '../types/AuthTypes.js' +import { AuthenticationMethod, AuthResultStatus, IdentifierType } from '../types/AuthTypes.js' const moduleName = 'CertificateAuthStrategy' @@ -67,7 +67,7 @@ export class CertificateAuthStrategy implements AuthStrategy { `${moduleName}: Certificate validation failed: ${String(certValidation.reason)}` ) return this.createFailureResult( - AuthorizationStatus.INVALID, + AuthResultStatus.INVALID, certValidation.reason ?? 'Certificate validation failed', request.identifier, startTime @@ -86,7 +86,7 @@ export class CertificateAuthStrategy implements AuthStrategy { // Unreachable when the `canHandle` contract holds; defensive fallback // for unsupported OCPP versions. return this.createFailureResult( - AuthorizationStatus.INVALID, + AuthResultStatus.INVALID, `Certificate authentication not supported for OCPP ${this.adapter.ocppVersion}`, request.identifier, startTime @@ -94,7 +94,7 @@ export class CertificateAuthStrategy implements AuthStrategy { } catch (error) { logger.error(`${moduleName}: Certificate authorization error:`, error) return this.createFailureResult( - AuthorizationStatus.INVALID, + AuthResultStatus.INVALID, 'Certificate authorization failed', request.identifier, startTime @@ -181,7 +181,7 @@ export class CertificateAuthStrategy implements AuthStrategy { * @returns Authorization result with failure status and diagnostic information */ private createFailureResult ( - status: AuthorizationStatus, + status: AuthResultStatus, reason: string, identifier: Identifier, startTime: number @@ -275,7 +275,7 @@ export class CertificateAuthStrategy implements AuthStrategy { * @param startTime - Request start timestamp for response time calculation */ private updateStatistics (result: AuthorizationResult, startTime: number): void { - if (result.status === AuthorizationStatus.ACCEPTED) { + if (result.status === AuthResultStatus.ACCEPTED) { this.stats.successfulAuths++ } else { this.stats.failedAuths++ @@ -375,7 +375,7 @@ export class CertificateAuthStrategy implements AuthStrategy { expiryDate: this.calculateCertificateExpiry(request.identifier), isOffline: false, method: AuthenticationMethod.CERTIFICATE_BASED, - status: AuthorizationStatus.ACCEPTED, + status: AuthResultStatus.ACCEPTED, timestamp: new Date(), } @@ -386,7 +386,7 @@ export class CertificateAuthStrategy implements AuthStrategy { return successResult } else { return this.createFailureResult( - AuthorizationStatus.BLOCKED, + AuthResultStatus.BLOCKED, 'Certificate validation failed', request.identifier, startTime @@ -395,7 +395,7 @@ export class CertificateAuthStrategy implements AuthStrategy { } catch (error) { logger.error(`${moduleName}: OCPP 2.0.1 certificate validation error:`, error) return this.createFailureResult( - AuthorizationStatus.INVALID, + AuthResultStatus.INVALID, 'Certificate validation error', request.identifier, startTime diff --git a/src/charging-station/ocpp/auth/strategies/LocalAuthStrategy.ts b/src/charging-station/ocpp/auth/strategies/LocalAuthStrategy.ts index 95766b7c..ad136b3c 100644 --- a/src/charging-station/ocpp/auth/strategies/LocalAuthStrategy.ts +++ b/src/charging-station/ocpp/auth/strategies/LocalAuthStrategy.ts @@ -12,7 +12,7 @@ import { AuthenticationError, AuthenticationMethod, AuthErrorCode, - AuthorizationStatus, + AuthResultStatus, enhanceAuthResult, } from '../types/AuthTypes.js' @@ -395,7 +395,7 @@ export class LocalAuthStrategy implements AuthStrategy { expiryDate: entry.expiryDate, isOffline: false, method: AuthenticationMethod.LOCAL_LIST, - status: AuthorizationStatus.EXPIRED, + status: AuthResultStatus.EXPIRED, timestamp: new Date(), } } @@ -446,14 +446,14 @@ export class LocalAuthStrategy implements AuthStrategy { additionalInfo: { reason: 'Transaction stop - offline mode' }, isOffline: true, method: AuthenticationMethod.OFFLINE_FALLBACK, - status: AuthorizationStatus.ACCEPTED, + status: AuthResultStatus.ACCEPTED, timestamp: new Date(), } } // For unknown IDs, check configuration if (config.allowOfflineTxForUnknownId) { - const status = config.unknownIdAuthorization ?? AuthorizationStatus.ACCEPTED + const status = config.unknownIdAuthorization ?? AuthResultStatus.ACCEPTED return { additionalInfo: { reason: 'Unknown ID allowed in offline mode' }, @@ -469,7 +469,7 @@ export class LocalAuthStrategy implements AuthStrategy { additionalInfo: { reason: 'Unknown ID not allowed in offline mode' }, isOffline: true, method: AuthenticationMethod.OFFLINE_FALLBACK, - status: AuthorizationStatus.INVALID, + status: AuthResultStatus.INVALID, timestamp: new Date(), } } @@ -479,26 +479,26 @@ export class LocalAuthStrategy implements AuthStrategy { * @param status - Status string from local auth list entry * @returns Authorization status corresponding to the entry status */ - private mapEntryStatus (status: string): AuthorizationStatus { + private mapEntryStatus (status: string): AuthResultStatus { switch (status.toLowerCase()) { case 'accepted': case 'authorized': case 'valid': - return AuthorizationStatus.ACCEPTED + return AuthResultStatus.ACCEPTED case 'blocked': case 'disabled': - return AuthorizationStatus.BLOCKED + return AuthResultStatus.BLOCKED case 'concurrent': case 'concurrent_tx': - return AuthorizationStatus.CONCURRENT_TX + return AuthResultStatus.CONCURRENT_TX case 'expired': - return AuthorizationStatus.EXPIRED + return AuthResultStatus.EXPIRED case 'invalid': case 'unauthorized': - return AuthorizationStatus.INVALID + return AuthResultStatus.INVALID default: logger.warn(`${moduleName}: Unknown entry status: ${status}, defaulting to INVALID`) - return AuthorizationStatus.INVALID + return AuthResultStatus.INVALID } } @@ -516,6 +516,6 @@ export class LocalAuthStrategy implements AuthStrategy { result: AuthorizationResult, config: AuthConfiguration ): boolean { - return result.status !== AuthorizationStatus.ACCEPTED && config.disablePostAuthorize !== true + return result.status !== AuthResultStatus.ACCEPTED && config.disablePostAuthorize !== true } } diff --git a/src/charging-station/ocpp/auth/types/AuthTypes.ts b/src/charging-station/ocpp/auth/types/AuthTypes.ts index db59df06..a95d8fa7 100644 --- a/src/charging-station/ocpp/auth/types/AuthTypes.ts +++ b/src/charging-station/ocpp/auth/types/AuthTypes.ts @@ -54,7 +54,7 @@ export enum AuthErrorCode { /** * Authorization status combining OCPP 1.6 and 2.0 statuses */ -export enum AuthorizationStatus { +export enum AuthResultStatus { // Common statuses across versions ACCEPTED = 'Accepted', BLOCKED = 'Blocked', @@ -154,7 +154,7 @@ export interface AuthConfiguration extends JsonObject { remoteAuthorization?: boolean /** Default authorization status for unknown IDs */ - unknownIdAuthorization?: AuthorizationStatus + unknownIdAuthorization?: AuthResultStatus } /** @@ -193,7 +193,7 @@ export interface AuthorizationResult { } /** Authorization status */ - readonly status: AuthorizationStatus + readonly status: AuthResultStatus /** Timestamp of authorization */ readonly timestamp: Date @@ -369,23 +369,23 @@ export const requiresAdditionalInfo = (type: IdentifierType): boolean => { * @example * ```typescript * const status = mapOCPP16Status(OCPP16AuthorizationStatus.ACCEPTED) - * // Returns: AuthorizationStatus.ACCEPTED + * // Returns: AuthResultStatus.ACCEPTED * ``` */ -export const mapOCPP16Status = (status: OCPP16AuthorizationStatus): AuthorizationStatus => { +export const mapOCPP16Status = (status: OCPP16AuthorizationStatus): AuthResultStatus => { switch (status) { case OCPP16AuthorizationStatus.ACCEPTED: - return AuthorizationStatus.ACCEPTED + return AuthResultStatus.ACCEPTED case OCPP16AuthorizationStatus.BLOCKED: - return AuthorizationStatus.BLOCKED + return AuthResultStatus.BLOCKED case OCPP16AuthorizationStatus.CONCURRENT_TX: - return AuthorizationStatus.CONCURRENT_TX + return AuthResultStatus.CONCURRENT_TX case OCPP16AuthorizationStatus.EXPIRED: - return AuthorizationStatus.EXPIRED + return AuthResultStatus.EXPIRED case OCPP16AuthorizationStatus.INVALID: - return AuthorizationStatus.INVALID + return AuthResultStatus.INVALID default: - return AuthorizationStatus.INVALID + return AuthResultStatus.INVALID } } @@ -396,35 +396,35 @@ export const mapOCPP16Status = (status: OCPP16AuthorizationStatus): Authorizatio * @example * ```typescript * const status = mapOCPP20AuthorizationStatus(OCPP20AuthorizationStatusEnumType.Accepted) - * // Returns: AuthorizationStatus.ACCEPTED + * // Returns: AuthResultStatus.ACCEPTED * ``` */ export const mapOCPP20AuthorizationStatus = ( status: OCPP20AuthorizationStatusEnumType -): AuthorizationStatus => { +): AuthResultStatus => { switch (status) { case OCPP20AuthorizationStatusEnumType.Accepted: - return AuthorizationStatus.ACCEPTED + return AuthResultStatus.ACCEPTED case OCPP20AuthorizationStatusEnumType.Blocked: - return AuthorizationStatus.BLOCKED + return AuthResultStatus.BLOCKED case OCPP20AuthorizationStatusEnumType.ConcurrentTx: - return AuthorizationStatus.CONCURRENT_TX + return AuthResultStatus.CONCURRENT_TX case OCPP20AuthorizationStatusEnumType.Expired: - return AuthorizationStatus.EXPIRED + return AuthResultStatus.EXPIRED case OCPP20AuthorizationStatusEnumType.Invalid: - return AuthorizationStatus.INVALID + return AuthResultStatus.INVALID case OCPP20AuthorizationStatusEnumType.NoCredit: - return AuthorizationStatus.NO_CREDIT + return AuthResultStatus.NO_CREDIT case OCPP20AuthorizationStatusEnumType.NotAllowedTypeEVSE: - return AuthorizationStatus.NOT_ALLOWED_TYPE_EVSE + return AuthResultStatus.NOT_ALLOWED_TYPE_EVSE case OCPP20AuthorizationStatusEnumType.NotAtThisLocation: - return AuthorizationStatus.NOT_AT_THIS_LOCATION + return AuthResultStatus.NOT_AT_THIS_LOCATION case OCPP20AuthorizationStatusEnumType.NotAtThisTime: - return AuthorizationStatus.NOT_AT_THIS_TIME + return AuthResultStatus.NOT_AT_THIS_TIME case OCPP20AuthorizationStatusEnumType.Unknown: - return AuthorizationStatus.UNKNOWN + return AuthResultStatus.UNKNOWN default: - return AuthorizationStatus.INVALID + return AuthResultStatus.INVALID } } @@ -467,25 +467,25 @@ export const mapOCPP20TokenType = (type: OCPP20IdTokenEnumType): IdentifierType * @returns OCPP 1.6 authorization status * @example * ```typescript - * const ocpp16Status = mapToOCPP16Status(AuthorizationStatus.ACCEPTED) + * const ocpp16Status = mapToOCPP16Status(AuthResultStatus.ACCEPTED) * // Returns: OCPP16AuthorizationStatus.ACCEPTED * ``` */ -export const mapToOCPP16Status = (status: AuthorizationStatus): OCPP16AuthorizationStatus => { +export const mapToOCPP16Status = (status: AuthResultStatus): OCPP16AuthorizationStatus => { switch (status) { - case AuthorizationStatus.ACCEPTED: + case AuthResultStatus.ACCEPTED: return OCPP16AuthorizationStatus.ACCEPTED - case AuthorizationStatus.BLOCKED: + case AuthResultStatus.BLOCKED: return OCPP16AuthorizationStatus.BLOCKED - case AuthorizationStatus.CONCURRENT_TX: + case AuthResultStatus.CONCURRENT_TX: return OCPP16AuthorizationStatus.CONCURRENT_TX - case AuthorizationStatus.EXPIRED: + case AuthResultStatus.EXPIRED: return OCPP16AuthorizationStatus.EXPIRED - case AuthorizationStatus.INVALID: - case AuthorizationStatus.NOT_AT_THIS_LOCATION: - case AuthorizationStatus.NOT_AT_THIS_TIME: - case AuthorizationStatus.PENDING: - case AuthorizationStatus.UNKNOWN: + case AuthResultStatus.INVALID: + case AuthResultStatus.NOT_AT_THIS_LOCATION: + case AuthResultStatus.NOT_AT_THIS_TIME: + case AuthResultStatus.PENDING: + case AuthResultStatus.UNKNOWN: default: return OCPP16AuthorizationStatus.INVALID } @@ -497,22 +497,22 @@ export const mapToOCPP16Status = (status: AuthorizationStatus): OCPP16Authorizat * @returns OCPP 2.0.1 RequestStartStopStatus * @example * ```typescript - * const ocpp20Status = mapToOCPP20Status(AuthorizationStatus.ACCEPTED) + * const ocpp20Status = mapToOCPP20Status(AuthResultStatus.ACCEPTED) * // Returns: RequestStartStopStatusEnumType.Accepted * ``` */ -export const mapToOCPP20Status = (status: AuthorizationStatus): RequestStartStopStatusEnumType => { +export const mapToOCPP20Status = (status: AuthResultStatus): RequestStartStopStatusEnumType => { switch (status) { - case AuthorizationStatus.ACCEPTED: + case AuthResultStatus.ACCEPTED: return RequestStartStopStatusEnumType.Accepted - case AuthorizationStatus.BLOCKED: - case AuthorizationStatus.CONCURRENT_TX: - case AuthorizationStatus.EXPIRED: - case AuthorizationStatus.INVALID: - case AuthorizationStatus.NOT_AT_THIS_LOCATION: - case AuthorizationStatus.NOT_AT_THIS_TIME: - case AuthorizationStatus.PENDING: - case AuthorizationStatus.UNKNOWN: + case AuthResultStatus.BLOCKED: + case AuthResultStatus.CONCURRENT_TX: + case AuthResultStatus.EXPIRED: + case AuthResultStatus.INVALID: + case AuthResultStatus.NOT_AT_THIS_LOCATION: + case AuthResultStatus.NOT_AT_THIS_TIME: + case AuthResultStatus.PENDING: + case AuthResultStatus.UNKNOWN: default: return RequestStartStopStatusEnumType.Rejected } diff --git a/src/charging-station/ocpp/auth/utils/AuthHelpers.ts b/src/charging-station/ocpp/auth/utils/AuthHelpers.ts index b49ced81..494cab44 100644 --- a/src/charging-station/ocpp/auth/utils/AuthHelpers.ts +++ b/src/charging-station/ocpp/auth/utils/AuthHelpers.ts @@ -9,7 +9,7 @@ import type { } from '../types/AuthTypes.js' import { isEmpty, truncateId } from '../../../../utils/index.js' -import { AuthorizationStatus } from '../types/AuthTypes.js' +import { AuthResultStatus } from '../types/AuthTypes.js' /** * @param expiryDate - Expiry timestamp to compute TTL from @@ -62,7 +62,7 @@ function createAuthRequest ( * @returns AuthorizationResult with isOffline=false */ function createRejectedResult ( - status: AuthorizationStatus, + status: AuthResultStatus, method: AuthenticationMethod, reason?: string ): AuthorizationResult { @@ -90,25 +90,25 @@ function formatAuthError (error: Error, identifier: Identifier): string { * @param status - Authorization status to describe * @returns Descriptive message for the status */ -function getStatusMessage (status: AuthorizationStatus): string { +function getStatusMessage (status: AuthResultStatus): string { switch (status) { - case AuthorizationStatus.ACCEPTED: + case AuthResultStatus.ACCEPTED: return 'Authorization accepted' - case AuthorizationStatus.BLOCKED: + case AuthResultStatus.BLOCKED: return 'Identifier is blocked' - case AuthorizationStatus.CONCURRENT_TX: + case AuthResultStatus.CONCURRENT_TX: return 'Concurrent transaction in progress' - case AuthorizationStatus.EXPIRED: + case AuthResultStatus.EXPIRED: return 'Authorization has expired' - case AuthorizationStatus.INVALID: + case AuthResultStatus.INVALID: return 'Invalid identifier' - case AuthorizationStatus.NOT_AT_THIS_LOCATION: + case AuthResultStatus.NOT_AT_THIS_LOCATION: return 'Not authorized at this location' - case AuthorizationStatus.NOT_AT_THIS_TIME: + case AuthResultStatus.NOT_AT_THIS_TIME: return 'Not authorized at this time' - case AuthorizationStatus.PENDING: + case AuthResultStatus.PENDING: return 'Authorization pending' - case AuthorizationStatus.UNKNOWN: + case AuthResultStatus.UNKNOWN: return 'Unknown authorization status' default: return 'Authorization failed' @@ -121,11 +121,9 @@ function getStatusMessage (status: AuthorizationStatus): string { * @returns `true` if BLOCKED, EXPIRED, or INVALID */ function isPermanentFailure (result: AuthorizationResult): boolean { - return [ - AuthorizationStatus.BLOCKED, - AuthorizationStatus.EXPIRED, - AuthorizationStatus.INVALID, - ].includes(result.status) + return [AuthResultStatus.BLOCKED, AuthResultStatus.EXPIRED, AuthResultStatus.INVALID].includes( + result.status + ) } /** @@ -134,7 +132,7 @@ function isPermanentFailure (result: AuthorizationResult): boolean { * @returns `true` if ACCEPTED and expiry date has not passed */ function isResultValid (result: AuthorizationResult): boolean { - if (result.status !== AuthorizationStatus.ACCEPTED) { + if (result.status !== AuthResultStatus.ACCEPTED) { return false } @@ -152,11 +150,11 @@ function isResultValid (result: AuthorizationResult): boolean { * @returns `true` if PENDING or UNKNOWN */ function isTemporaryFailure (result: AuthorizationResult): boolean { - if (result.status === AuthorizationStatus.PENDING) { + if (result.status === AuthResultStatus.PENDING) { return true } - if (result.status === AuthorizationStatus.UNKNOWN) { + if (result.status === AuthResultStatus.UNKNOWN) { return true } @@ -173,7 +171,7 @@ function mergeAuthResults (results: AuthorizationResult[]): AuthorizationResult return undefined } - const acceptedResult = results.find(r => r.status === AuthorizationStatus.ACCEPTED) + const acceptedResult = results.find(r => r.status === AuthResultStatus.ACCEPTED) if (acceptedResult) { return acceptedResult } diff --git a/src/charging-station/ui-server/UIMCPServer.ts b/src/charging-station/ui-server/UIMCPServer.ts index 078470ed..71168e5e 100644 --- a/src/charging-station/ui-server/UIMCPServer.ts +++ b/src/charging-station/ui-server/UIMCPServer.ts @@ -28,6 +28,7 @@ import { getErrorMessage, isEmpty, isNotEmptyArray, + isOCPP20x, JSONStringify, logger, } from '../../utils/index.js' @@ -188,7 +189,7 @@ export class UIMCPServer extends AbstractUIServer { if (expectedVersion === OCPPVersion.VERSION_16) { return s.version !== OCPPVersion.VERSION_16 } - return s.version !== OCPPVersion.VERSION_20 && s.version !== OCPPVersion.VERSION_201 + return !isOCPP20x(s.version) }) if (isNotEmptyArray(mismatched)) { const ids = mismatched.map(s => s.hashId).join(', ') diff --git a/src/charging-station/ui-server/UIServerAccessPolicy.ts b/src/charging-station/ui-server/UIServerAccessPolicy.ts index 97244d2b..39414951 100644 --- a/src/charging-station/ui-server/UIServerAccessPolicy.ts +++ b/src/charging-station/ui-server/UIServerAccessPolicy.ts @@ -7,6 +7,7 @@ import { isEmpty, isLoopback, isNotEmptyArray, + isNotEmptyString, normalizeHost, normalizeIPAddress, UI_SERVER_ACCESS_POLICY_DEFAULTS, @@ -320,7 +321,7 @@ const pickForwardedValue = ( } const nonEmpty = (value: string | undefined): string | undefined => - value == null || value.trim() === '' ? undefined : value + isNotEmptyString(value) ? value : undefined // RFC 7239 §6: "unknown" and obfuscated node identifiers ("_" + token chars). // Optional ":port" suffix is stripped before comparison. diff --git a/src/charging-station/ui-server/ui-services/AbstractUIService.ts b/src/charging-station/ui-server/ui-services/AbstractUIService.ts index b09f6a54..7d14cad9 100644 --- a/src/charging-station/ui-server/ui-services/AbstractUIService.ts +++ b/src/charging-station/ui-server/ui-services/AbstractUIService.ts @@ -27,6 +27,7 @@ import { Constants, ensureError, getErrorMessage, + isEmpty, isNotEmptyArray, JSONStringify, logger, @@ -193,7 +194,7 @@ export abstract class AbstractUIService { } if ( requestContext.outstandingHashIds.delete(hashId) && - requestContext.outstandingHashIds.size === 0 + isEmpty(requestContext.outstandingHashIds) ) { this.uiServiceWorkerBroadcastChannel.completeReconciledRequest(uuid) } @@ -212,7 +213,7 @@ export abstract class AbstractUIService { ) { return 'untracked' } - return requestContext.outstandingHashIds.size === 0 ? 'completed' : 'outstanding' + return isEmpty(requestContext.outstandingHashIds) ? 'completed' : 'outstanding' } public async requestHandler ( @@ -548,7 +549,7 @@ export abstract class AbstractUIService { ? payload.hashIds : this.uiServer.getChargingStationHashIds() ) - if (outstandingHashIds.size === 0) { + if (isEmpty(outstandingHashIds)) { throw new BaseError('No charging station is available to handle the broadcast request') } // Safety-net timeout: if not all expected worker responses arrive (e.g. a diff --git a/src/utils/Configuration.ts b/src/utils/Configuration.ts index 13a1a185..3399a47a 100644 --- a/src/utils/Configuration.ts +++ b/src/utils/Configuration.ts @@ -331,7 +331,7 @@ export class Configuration { } try { return watch(Configuration.configurationFile, (event, filename): void => { - if ((filename?.trim().length ?? 0) === 0 || event !== 'change') { + if (!isNotEmptyString(filename) || event !== 'change') { return } if (Configuration.configurationFileReloading) { diff --git a/src/utils/ConfigurationSchema.ts b/src/utils/ConfigurationSchema.ts index 9f79a488..040ec6d6 100644 --- a/src/utils/ConfigurationSchema.ts +++ b/src/utils/ConfigurationSchema.ts @@ -14,7 +14,7 @@ import { import { WorkerProcessType } from '../worker/index.js' import { CURRENT_CONFIGURATION_SCHEMA_VERSION } from './ConfigurationMigrations.js' import { isHostLiteralWithoutPort } from './HostUtils.js' -import { has } from './Utils.js' +import { has, isNotEmptyArray } from './Utils.js' // --------------------------------------------------------------- // Sub-schemas @@ -165,17 +165,10 @@ const UIServerAccessPolicySchema = z .optional(), }) .strict() - .refine( - value => - !( - value.allowLoopbackProxy === true && - (value.trustedProxies == null || value.trustedProxies.length === 0) - ), - { - message: "'allowLoopbackProxy' requires at least one entry in 'trustedProxies'", - path: ['trustedProxies'], - } - ) + .refine(value => !(value.allowLoopbackProxy === true && !isNotEmptyArray(value.trustedProxies)), { + message: "'allowLoopbackProxy' requires at least one entry in 'trustedProxies'", + path: ['trustedProxies'], + }) /** * UIServerListenOptionsObjectSchema — typed object layer for `node:net` diff --git a/src/utils/Utils.ts b/src/utils/Utils.ts index 37614447..a8ca2e8d 100644 --- a/src/utils/Utils.ts +++ b/src/utils/Utils.ts @@ -18,6 +18,7 @@ import { type JsonObject, MapStringifyFormat, MessageType, + OCPPVersion, type TimestampedData, type UUIDv4, WebSocketCloseEventStatusString, @@ -116,6 +117,9 @@ export const isEmpty = (value: unknown): boolean => { return false } +export const isOCPP20x = (version: OCPPVersion | undefined): boolean => + version === OCPPVersion.VERSION_20 || version === OCPPVersion.VERSION_201 + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters export const mergeDeepRight = (target: T, source: S): T => { const output: Record = { ...(target as Record) } diff --git a/src/utils/index.ts b/src/utils/index.ts index 17d02bfb..02d1f1cd 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -93,6 +93,7 @@ export { isJsonObject, isNotEmptyArray, isNotEmptyString, + isOCPP20x, isValidDate, isValidRandomIntBounds, JSONStringify, diff --git a/src/worker/WorkerTypes.ts b/src/worker/WorkerTypes.ts index a233d6df..e6324f5c 100644 --- a/src/worker/WorkerTypes.ts +++ b/src/worker/WorkerTypes.ts @@ -1,6 +1,6 @@ import type { Worker } from 'node:worker_threads' -import { type PoolEvent, PoolEvents, type ThreadPoolOptions } from 'poolifier' +import { type ThreadPoolOptions } from 'poolifier' export enum WorkerMessageEvents { addedWorkerElement = 'addedWorkerElement', @@ -50,13 +50,6 @@ export interface WorkerDataError extends WorkerData { stack?: string } -export const WorkerEvents = { - ...PoolEvents, - ...WorkerSetEvents, -} as const -// eslint-disable-next-line @typescript-eslint/no-redeclare -export type WorkerEvents = PoolEvent | WorkerSetEvents - export interface WorkerMessage { data: T event: WorkerMessageEvents diff --git a/src/worker/index.ts b/src/worker/index.ts index c0f9c655..07c9521c 100644 --- a/src/worker/index.ts +++ b/src/worker/index.ts @@ -10,7 +10,6 @@ export { WorkerFactory } from './WorkerFactory.js' export { type WorkerData, type WorkerDataError, - WorkerEvents, type WorkerMessage, WorkerMessageEvents, WorkerProcessType, diff --git a/tests/charging-station/ChargingStation-RequestStatistic.test.ts b/tests/charging-station/ChargingStation-RequestStatistic.test.ts new file mode 100644 index 00000000..dffb5805 --- /dev/null +++ b/tests/charging-station/ChargingStation-RequestStatistic.test.ts @@ -0,0 +1,75 @@ +/** + * @file Tests for ChargingStation.recordRequestStatistic + * @description Verifies the single-sourced `enableStatistics` gate applied before + * delegating to performanceStatistics.addRequestStatistic. + */ +import assert from 'node:assert/strict' +import { afterEach, describe, it, mock } from 'node:test' + +import type { ChargingStation } from '../../src/charging-station/index.js' + +import { ChargingStation as ChargingStationClass } from '../../src/charging-station/ChargingStation.js' +import { MessageType, RequestCommand } from '../../src/types/index.js' +import { standardCleanup } from '../helpers/TestLifecycleHelpers.js' + +interface RecordRequestStatisticContext { + performanceStatistics?: { addRequestStatistic: (...args: unknown[]) => void } + stationInfo?: { enableStatistics?: boolean } +} + +const callRecordRequestStatistic = ( + context: RecordRequestStatisticContext, + command: RequestCommand, + messageType: MessageType +): void => { + ChargingStationClass.prototype.recordRequestStatistic.call( + context as unknown as ChargingStation, + command, + messageType + ) +} + +await describe('ChargingStation.recordRequestStatistic', async () => { + afterEach(() => { + standardCleanup() + }) + + await it('should record the statistic when enableStatistics is true', () => { + const addRequestStatistic = mock.fn() + callRecordRequestStatistic( + { performanceStatistics: { addRequestStatistic }, stationInfo: { enableStatistics: true } }, + RequestCommand.HEARTBEAT, + MessageType.CALL_MESSAGE + ) + assert.strictEqual(addRequestStatistic.mock.callCount(), 1) + assert.deepStrictEqual(addRequestStatistic.mock.calls[0].arguments, [ + RequestCommand.HEARTBEAT, + MessageType.CALL_MESSAGE, + ]) + }) + + await it('should not record the statistic when enableStatistics is not true', () => { + const addRequestStatistic = mock.fn() + callRecordRequestStatistic( + { performanceStatistics: { addRequestStatistic }, stationInfo: { enableStatistics: false } }, + RequestCommand.HEARTBEAT, + MessageType.CALL_RESULT_MESSAGE + ) + callRecordRequestStatistic( + { performanceStatistics: { addRequestStatistic }, stationInfo: {} }, + RequestCommand.HEARTBEAT, + MessageType.CALL_RESULT_MESSAGE + ) + assert.strictEqual(addRequestStatistic.mock.callCount(), 0) + }) + + await it('should not throw when performanceStatistics is undefined and enableStatistics is true', () => { + assert.doesNotThrow(() => { + callRecordRequestStatistic( + { stationInfo: { enableStatistics: true } }, + RequestCommand.HEARTBEAT, + MessageType.CALL_ERROR_MESSAGE + ) + }) + }) +}) diff --git a/tests/charging-station/ocpp/1.6/OCPP16RequestService-CallChain.test.ts b/tests/charging-station/ocpp/1.6/OCPP16RequestService-CallChain.test.ts index 6cba65d5..5db6c797 100644 --- a/tests/charging-station/ocpp/1.6/OCPP16RequestService-CallChain.test.ts +++ b/tests/charging-station/ocpp/1.6/OCPP16RequestService-CallChain.test.ts @@ -113,6 +113,31 @@ await describe('OCPP 1.6 Request Call Chain — requestHandler → buildRequestP }) }) + await describe('preRequestHook — OCPP 1.6 StartTransaction connector status transition', async () => { + await it('should set the connector status to Preparing before sending StartTransaction', async () => { + const connectorStatus = station.getConnectorStatus(1) + assert.strictEqual(connectorStatus?.status, OCPP16ChargePointStatus.Available) + + await requestService.requestHandler(station, OCPP16RequestCommand.START_TRANSACTION, { + connectorId: 1, + idTag: 'TEST001', + }) + + assert.strictEqual(station.getConnectorStatus(1)?.status, OCPP16ChargePointStatus.Preparing) + }) + + await it('should not change the connector status for a command without a pre-request hook', async () => { + const connectorStatus = station.getConnectorStatus(1) + assert.strictEqual(connectorStatus?.status, OCPP16ChargePointStatus.Available) + + await requestService.requestHandler(station, OCPP16RequestCommand.AUTHORIZE, { + idTag: 'TEST001', + }) + + assert.strictEqual(station.getConnectorStatus(1)?.status, OCPP16ChargePointStatus.Available) + }) + }) + await describe('HEARTBEAT — no builder, empty payload', async () => { await it('should send empty payload for Heartbeat', async () => { await requestService.requestHandler(station, OCPP16RequestCommand.HEARTBEAT) diff --git a/tests/charging-station/ocpp/1.6/OCPP16ServiceUtils.test.ts b/tests/charging-station/ocpp/1.6/OCPP16ServiceUtils.test.ts index 94293e6c..0acdbe07 100644 --- a/tests/charging-station/ocpp/1.6/OCPP16ServiceUtils.test.ts +++ b/tests/charging-station/ocpp/1.6/OCPP16ServiceUtils.test.ts @@ -12,7 +12,7 @@ import { afterEach, describe, it } from 'node:test' import { OCPP16ServiceUtils } from '../../../../src/charging-station/ocpp/1.6/OCPP16ServiceUtils.js' import { - AuthorizationStatus, + AuthResultStatus, OCPPAuthServiceFactory, } from '../../../../src/charging-station/ocpp/auth/index.js' import { @@ -662,7 +662,7 @@ await describe('OCPP16ServiceUtils — pure functions', async () => { const authCache = getTestAuthCache(authService) const cached = authCache.get(TEST_ID_TAG) assert.ok(cached != null) - assert.strictEqual(cached.status, AuthorizationStatus.ACCEPTED) + assert.strictEqual(cached.status, AuthResultStatus.ACCEPTED) }) await it('should update auth cache with rejected status', () => { @@ -686,7 +686,7 @@ await describe('OCPP16ServiceUtils — pure functions', async () => { const authCache = getTestAuthCache(authService) const cached = authCache.get(TEST_ID_TAG) assert.ok(cached != null) - assert.strictEqual(cached.status, AuthorizationStatus.BLOCKED) + assert.strictEqual(cached.status, AuthResultStatus.BLOCKED) }) await it('should set TTL from expiryDate when in future', () => { @@ -712,7 +712,7 @@ await describe('OCPP16ServiceUtils — pure functions', async () => { const authCache = getTestAuthCache(authService) const cached = authCache.get(TEST_ID_TAG) assert.ok(cached != null, 'Cache entry should exist with future TTL') - assert.strictEqual(cached.status, AuthorizationStatus.ACCEPTED) + assert.strictEqual(cached.status, AuthResultStatus.ACCEPTED) }) await it('should skip caching when expiryDate is in the past', () => { @@ -761,7 +761,7 @@ await describe('OCPP16ServiceUtils — pure functions', async () => { const authCache = getTestAuthCache(authService) const cached = authCache.get(TEST_ID_TAG) assert.ok(cached != null, 'Cache entry should exist without TTL') - assert.strictEqual(cached.status, AuthorizationStatus.ACCEPTED) + assert.strictEqual(cached.status, AuthResultStatus.ACCEPTED) }) }) }) diff --git a/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-RequestStartTransaction.test.ts b/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-RequestStartTransaction.test.ts index d5df6670..e6746c01 100644 --- a/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-RequestStartTransaction.test.ts +++ b/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-RequestStartTransaction.test.ts @@ -23,7 +23,7 @@ import { OCPP20IncomingRequestService } from '../../../../src/charging-station/o import { OCPP20VariableManager } from '../../../../src/charging-station/ocpp/2.0/OCPP20VariableManager.js' import { AuthenticationMethod, - AuthorizationStatus, + AuthResultStatus, OCPPAuthServiceFactory, } from '../../../../src/charging-station/ocpp/auth/index.js' import { @@ -122,7 +122,7 @@ await describe('F01 & F02 - Remote Start Transaction', async () => { Promise.resolve( createMockAuthorizationResult({ method: AuthenticationMethod.REMOTE_AUTHORIZATION, - status: AuthorizationStatus.INVALID, + status: AuthResultStatus.INVALID, }) ), }) @@ -159,7 +159,7 @@ await describe('F01 & F02 - Remote Start Transaction', async () => { return Promise.resolve( createMockAuthorizationResult({ method: AuthenticationMethod.REMOTE_AUTHORIZATION, - status: AuthorizationStatus.ACCEPTED, + status: AuthResultStatus.ACCEPTED, }) ) } @@ -167,7 +167,7 @@ await describe('F01 & F02 - Remote Start Transaction', async () => { return Promise.resolve( createMockAuthorizationResult({ method: AuthenticationMethod.REMOTE_AUTHORIZATION, - status: AuthorizationStatus.BLOCKED, + status: AuthResultStatus.BLOCKED, }) ) }, diff --git a/tests/charging-station/ocpp/2.0/OCPP20ResponseService-CacheUpdate.test.ts b/tests/charging-station/ocpp/2.0/OCPP20ResponseService-CacheUpdate.test.ts index 48d067aa..bc454166 100644 --- a/tests/charging-station/ocpp/2.0/OCPP20ResponseService-CacheUpdate.test.ts +++ b/tests/charging-station/ocpp/2.0/OCPP20ResponseService-CacheUpdate.test.ts @@ -11,7 +11,7 @@ import type { ChargingStation } from '../../../../src/charging-station/index.js' import type { AuthCache } from '../../../../src/charging-station/ocpp/auth/interfaces/OCPPAuthService.js' import { - AuthorizationStatus, + AuthResultStatus, IdentifierType, OCPPAuthServiceFactory, } from '../../../../src/charging-station/ocpp/auth/index.js' @@ -56,7 +56,7 @@ await describe('C10 - TransactionEventResponse Cache Update', async () => { // Act authService.updateCacheEntry( TEST_TOKEN_ISO14443, - AuthorizationStatus.ACCEPTED, + AuthResultStatus.ACCEPTED, undefined, IdentifierType.ISO14443 ) @@ -64,7 +64,7 @@ await describe('C10 - TransactionEventResponse Cache Update', async () => { // Assert const cached = authCache.get(TEST_TOKEN_ISO14443) assert.ok(cached != null, 'Cache entry should exist') - assert.strictEqual(cached.status, AuthorizationStatus.ACCEPTED) + assert.strictEqual(cached.status, AuthResultStatus.ACCEPTED) }) await it('C10.FR.09 - should use cacheExpiryDateTime as TTL when present in idTokenInfo', () => { @@ -74,7 +74,7 @@ await describe('C10 - TransactionEventResponse Cache Update', async () => { // Act authService.updateCacheEntry( TEST_TOKEN_ISO14443, - AuthorizationStatus.ACCEPTED, + AuthResultStatus.ACCEPTED, futureDate, IdentifierType.ISO14443 ) @@ -82,14 +82,14 @@ await describe('C10 - TransactionEventResponse Cache Update', async () => { // Assert const cached = authCache.get(TEST_TOKEN_ISO14443) assert.ok(cached != null, 'Cache entry should exist with explicit TTL') - assert.strictEqual(cached.status, AuthorizationStatus.ACCEPTED) + assert.strictEqual(cached.status, AuthResultStatus.ACCEPTED) }) await it('C10.FR.08 - should use AuthCacheLifeTime as TTL when cacheExpiryDateTime absent', () => { // Act — no expiryDate, uses config.authorizationCacheLifetime authService.updateCacheEntry( TEST_TOKEN_ISO14443, - AuthorizationStatus.ACCEPTED, + AuthResultStatus.ACCEPTED, undefined, IdentifierType.ISO14443 ) @@ -97,14 +97,14 @@ await describe('C10 - TransactionEventResponse Cache Update', async () => { // Assert const cached = authCache.get(TEST_TOKEN_ISO14443) assert.ok(cached != null, 'Cache entry should exist with default TTL') - assert.strictEqual(cached.status, AuthorizationStatus.ACCEPTED) + assert.strictEqual(cached.status, AuthResultStatus.ACCEPTED) }) await it('C02.FR.03 - should NOT cache NoAuthorization token type', () => { // Act authService.updateCacheEntry( '', - AuthorizationStatus.ACCEPTED, + AuthResultStatus.ACCEPTED, undefined, IdentifierType.NO_AUTHORIZATION ) @@ -118,7 +118,7 @@ await describe('C10 - TransactionEventResponse Cache Update', async () => { // Act authService.updateCacheEntry( 'CENTRAL_TOKEN_001', - AuthorizationStatus.ACCEPTED, + AuthResultStatus.ACCEPTED, undefined, IdentifierType.CENTRAL ) @@ -132,13 +132,13 @@ await describe('C10 - TransactionEventResponse Cache Update', async () => { // Act authService.updateCacheEntry( 'BLOCKED_TOKEN', - AuthorizationStatus.BLOCKED, + AuthResultStatus.BLOCKED, undefined, IdentifierType.ISO14443 ) authService.updateCacheEntry( 'EXPIRED_TOKEN', - AuthorizationStatus.EXPIRED, + AuthResultStatus.EXPIRED, undefined, IdentifierType.ISO14443 ) @@ -146,11 +146,11 @@ await describe('C10 - TransactionEventResponse Cache Update', async () => { // Assert const cachedBlocked = authCache.get('BLOCKED_TOKEN') assert.ok(cachedBlocked != null, 'Blocked status should be cached') - assert.strictEqual(cachedBlocked.status, AuthorizationStatus.BLOCKED) + assert.strictEqual(cachedBlocked.status, AuthResultStatus.BLOCKED) const cachedExpired = authCache.get('EXPIRED_TOKEN') assert.ok(cachedExpired != null, 'Expired status should be cached') - assert.strictEqual(cachedExpired.status, AuthorizationStatus.EXPIRED) + assert.strictEqual(cachedExpired.status, AuthResultStatus.EXPIRED) }) await it('should skip caching when expiryDate is in the past', () => { @@ -160,7 +160,7 @@ await describe('C10 - TransactionEventResponse Cache Update', async () => { // Act authService.updateCacheEntry( TEST_TOKEN_ISO14443, - AuthorizationStatus.ACCEPTED, + AuthResultStatus.ACCEPTED, pastDate, IdentifierType.ISO14443 ) @@ -187,7 +187,7 @@ await describe('C10 - TransactionEventResponse Cache Update', async () => { // Act disabledService.updateCacheEntry( TEST_TOKEN_ISO14443, - AuthorizationStatus.ACCEPTED, + AuthResultStatus.ACCEPTED, undefined, IdentifierType.ISO14443 ) diff --git a/tests/charging-station/ocpp/2.0/OCPP20ServiceUtils-AuthCache.test.ts b/tests/charging-station/ocpp/2.0/OCPP20ServiceUtils-AuthCache.test.ts index 4ab7c67a..d8c0be5a 100644 --- a/tests/charging-station/ocpp/2.0/OCPP20ServiceUtils-AuthCache.test.ts +++ b/tests/charging-station/ocpp/2.0/OCPP20ServiceUtils-AuthCache.test.ts @@ -12,7 +12,7 @@ import type { AuthCache } from '../../../../src/charging-station/ocpp/auth/inter import { OCPP20ServiceUtils } from '../../../../src/charging-station/ocpp/2.0/OCPP20ServiceUtils.js' import { - AuthorizationStatus, + AuthResultStatus, OCPPAuthServiceFactory, } from '../../../../src/charging-station/ocpp/auth/index.js' import { OCPPAuthServiceImpl } from '../../../../src/charging-station/ocpp/auth/services/OCPPAuthServiceImpl.js' @@ -74,7 +74,7 @@ await describe('OCPP20ServiceUtils.updateAuthorizationCache', async () => { // Assert const cached = authCache.get(TEST_TOKEN_ISO14443) assert.ok(cached != null, 'AuthorizeResponse should update the cache') - assert.strictEqual(cached.status, AuthorizationStatus.ACCEPTED) + assert.strictEqual(cached.status, AuthResultStatus.ACCEPTED) }) await it('C10.FR.01 - should cache non-Accepted status through utility helper', () => { @@ -90,7 +90,7 @@ await describe('OCPP20ServiceUtils.updateAuthorizationCache', async () => { const cached = authCache.get('BLOCKED_TOKEN_001') assert.ok(cached != null, 'Blocked status should be cached per C10.FR.01') - assert.strictEqual(cached.status, AuthorizationStatus.BLOCKED) + assert.strictEqual(cached.status, AuthResultStatus.BLOCKED) }) await it('should handle auth service initialization failure gracefully', () => { diff --git a/tests/charging-station/ocpp/2.0/OCPP20VariableRegistry.test.ts b/tests/charging-station/ocpp/2.0/OCPP20VariableRegistry.test.ts new file mode 100644 index 00000000..71605b0e --- /dev/null +++ b/tests/charging-station/ocpp/2.0/OCPP20VariableRegistry.test.ts @@ -0,0 +1,74 @@ +/** + * @file Tests for OCPP 2.0 VariableMetadata predicates + * @description Unit tests for the isPersistent/isVolatile/isReadOnly/isWriteOnly + * predicates consumed by OCPP20VariableManager. + */ +import assert from 'node:assert/strict' +import { afterEach, describe, it } from 'node:test' + +import { + isPersistent, + isReadOnly, + isVolatile, + isWriteOnly, + type VariableMetadata, +} from '../../../../src/charging-station/ocpp/2.0/OCPP20VariableRegistry.js' +import { MutabilityEnumType, PersistenceEnumType } from '../../../../src/types/index.js' +import { standardCleanup } from '../../../helpers/TestLifecycleHelpers.js' + +const buildMetadata = ( + mutability: MutabilityEnumType, + persistence: PersistenceEnumType +): VariableMetadata => ({ mutability, persistence }) as unknown as VariableMetadata + +await describe('OCPP20VariableRegistry metadata predicates', async () => { + afterEach(() => { + standardCleanup() + }) + + await it('should detect persistent versus volatile persistence', () => { + assert.strictEqual( + isPersistent(buildMetadata(MutabilityEnumType.ReadWrite, PersistenceEnumType.Persistent)), + true + ) + assert.strictEqual( + isPersistent(buildMetadata(MutabilityEnumType.ReadWrite, PersistenceEnumType.Volatile)), + false + ) + assert.strictEqual( + isVolatile(buildMetadata(MutabilityEnumType.ReadWrite, PersistenceEnumType.Volatile)), + true + ) + assert.strictEqual( + isVolatile(buildMetadata(MutabilityEnumType.ReadWrite, PersistenceEnumType.Persistent)), + false + ) + }) + + await it('should detect read-only and write-only mutability', () => { + assert.strictEqual( + isReadOnly(buildMetadata(MutabilityEnumType.ReadOnly, PersistenceEnumType.Persistent)), + true + ) + assert.strictEqual( + isReadOnly(buildMetadata(MutabilityEnumType.ReadWrite, PersistenceEnumType.Persistent)), + false + ) + assert.strictEqual( + isReadOnly(buildMetadata(MutabilityEnumType.WriteOnly, PersistenceEnumType.Persistent)), + false + ) + assert.strictEqual( + isWriteOnly(buildMetadata(MutabilityEnumType.WriteOnly, PersistenceEnumType.Persistent)), + true + ) + assert.strictEqual( + isWriteOnly(buildMetadata(MutabilityEnumType.ReadOnly, PersistenceEnumType.Persistent)), + false + ) + assert.strictEqual( + isWriteOnly(buildMetadata(MutabilityEnumType.ReadWrite, PersistenceEnumType.Persistent)), + false + ) + }) +}) diff --git a/tests/charging-station/ocpp/OCPPServiceOperations.test.ts b/tests/charging-station/ocpp/OCPPServiceOperations.test.ts index 4f24efcc..c62fc6f6 100644 --- a/tests/charging-station/ocpp/OCPPServiceOperations.test.ts +++ b/tests/charging-station/ocpp/OCPPServiceOperations.test.ts @@ -11,7 +11,7 @@ import { afterEach, describe, it } from 'node:test' import { AuthContext, AuthenticationMethod, - AuthorizationStatus, + AuthResultStatus, OCPPAuthServiceFactory, } from '../../../src/charging-station/ocpp/auth/index.js' import { @@ -301,7 +301,7 @@ await describe('OCPPServiceOperations', async () => { }) injectMockAuthService(station, { authorize: () => - Promise.resolve(createMockAuthorizationResult({ status: AuthorizationStatus.INVALID })), + Promise.resolve(createMockAuthorizationResult({ status: AuthResultStatus.INVALID })), }) // Act @@ -319,7 +319,7 @@ await describe('OCPPServiceOperations', async () => { Promise.resolve( createMockAuthorizationResult({ method: AuthenticationMethod.LOCAL_LIST, - status: AuthorizationStatus.ACCEPTED, + status: AuthResultStatus.ACCEPTED, }) ), }) @@ -339,7 +339,7 @@ await describe('OCPPServiceOperations', async () => { Promise.resolve( createMockAuthorizationResult({ method: AuthenticationMethod.LOCAL_LIST, - status: AuthorizationStatus.ACCEPTED, + status: AuthResultStatus.ACCEPTED, }) ), }) @@ -362,7 +362,7 @@ await describe('OCPPServiceOperations', async () => { Promise.resolve( createMockAuthorizationResult({ method: AuthenticationMethod.CACHE, - status: AuthorizationStatus.ACCEPTED, + status: AuthResultStatus.ACCEPTED, }) ), }) @@ -387,7 +387,7 @@ await describe('OCPPServiceOperations', async () => { Promise.resolve( createMockAuthorizationResult({ method: AuthenticationMethod.REMOTE_AUTHORIZATION, - status: AuthorizationStatus.ACCEPTED, + status: AuthResultStatus.ACCEPTED, }) ), }) @@ -409,7 +409,7 @@ await describe('OCPPServiceOperations', async () => { Promise.resolve( createMockAuthorizationResult({ method: AuthenticationMethod.REMOTE_AUTHORIZATION, - status: AuthorizationStatus.ACCEPTED, + status: AuthResultStatus.ACCEPTED, }) ), }) @@ -434,7 +434,7 @@ await describe('OCPPServiceOperations', async () => { Promise.resolve( createMockAuthorizationResult({ method: AuthenticationMethod.REMOTE_AUTHORIZATION, - status: AuthorizationStatus.BLOCKED, + status: AuthResultStatus.BLOCKED, }) ), }) @@ -454,7 +454,7 @@ await describe('OCPPServiceOperations', async () => { Promise.resolve( createMockAuthorizationResult({ method: AuthenticationMethod.LOCAL_LIST, - status: AuthorizationStatus.ACCEPTED, + status: AuthResultStatus.ACCEPTED, }) ), }) @@ -476,7 +476,7 @@ await describe('OCPPServiceOperations', async () => { Promise.resolve( createMockAuthorizationResult({ method: AuthenticationMethod.OFFLINE_FALLBACK, - status: AuthorizationStatus.ACCEPTED, + status: AuthResultStatus.ACCEPTED, }) ), }) @@ -515,7 +515,7 @@ await describe('OCPPServiceOperations', async () => { return Promise.resolve( createMockAuthorizationResult({ method: AuthenticationMethod.LOCAL_LIST, - status: AuthorizationStatus.ACCEPTED, + status: AuthResultStatus.ACCEPTED, }) ) }, diff --git a/tests/charging-station/ocpp/auth/OCPPAuthIntegration.test.ts b/tests/charging-station/ocpp/auth/OCPPAuthIntegration.test.ts index f2a66496..0bdbf594 100644 --- a/tests/charging-station/ocpp/auth/OCPPAuthIntegration.test.ts +++ b/tests/charging-station/ocpp/auth/OCPPAuthIntegration.test.ts @@ -14,7 +14,7 @@ import { LocalAuthStrategy } from '../../../../src/charging-station/ocpp/auth/st import { AuthContext, AuthenticationMethod, - AuthorizationStatus, + AuthResultStatus, IdentifierType, } from '../../../../src/charging-station/ocpp/auth/types/AuthTypes.js' import { OCPPVersion } from '../../../../src/types/index.js' @@ -82,7 +82,7 @@ await describe('OCPP Authentication', async () => { assert.ok(result.timestamp instanceof Date) assert.strictEqual(typeof result.isOffline, 'boolean') // Status should be one of the valid authorization statuses - assert.ok(Object.values(AuthorizationStatus).includes(result.status)) + assert.ok(Object.values(AuthResultStatus).includes(result.status)) }) await it('should handle multiple auth contexts', async () => { @@ -137,7 +137,7 @@ await describe('OCPP Authentication', async () => { assert.notStrictEqual(result, undefined) assert.ok(result.timestamp instanceof Date) assert.strictEqual(typeof result.isOffline, 'boolean') - assert.ok(Object.values(AuthorizationStatus).includes(result.status)) + assert.ok(Object.values(AuthResultStatus).includes(result.status)) }) await it('should handle all auth contexts', async () => { @@ -183,7 +183,7 @@ await describe('OCPP Authentication', async () => { // Should return a result (not throw) with non-ACCEPTED status assert.notStrictEqual(result, undefined) - assert.notStrictEqual(result.status, AuthorizationStatus.ACCEPTED) + assert.notStrictEqual(result.status, AuthResultStatus.ACCEPTED) }) }) diff --git a/tests/charging-station/ocpp/auth/adapters/OCPP16AuthAdapter.test.ts b/tests/charging-station/ocpp/auth/adapters/OCPP16AuthAdapter.test.ts index 82ac7d53..4f2dda24 100644 --- a/tests/charging-station/ocpp/auth/adapters/OCPP16AuthAdapter.test.ts +++ b/tests/charging-station/ocpp/auth/adapters/OCPP16AuthAdapter.test.ts @@ -13,7 +13,7 @@ import { type AuthConfiguration, AuthContext, AuthenticationMethod, - AuthorizationStatus, + AuthResultStatus, IdentifierType, } from '../../../../../src/charging-station/ocpp/auth/types/AuthTypes.js' import { OCPP16AuthorizationStatus, OCPPVersion } from '../../../../../src/types/index.js' @@ -154,7 +154,7 @@ await describe('OCPP16AuthAdapter', async () => { const result = await adapter.authorizeRemote(identifier, 1, 123) - assert.strictEqual(result.status, AuthorizationStatus.ACCEPTED) + assert.strictEqual(result.status, AuthResultStatus.ACCEPTED) assert.notStrictEqual(result.method, undefined) assert.strictEqual(result.isOffline, false) assert.ok(result.timestamp instanceof Date) @@ -171,7 +171,7 @@ await describe('OCPP16AuthAdapter', async () => { const result = await adapter.authorizeRemote(identifier, 1) - assert.strictEqual(result.status, AuthorizationStatus.INVALID) + assert.strictEqual(result.status, AuthResultStatus.INVALID) assert.notStrictEqual(result.additionalInfo?.error, undefined) }) }) diff --git a/tests/charging-station/ocpp/auth/adapters/OCPP20AuthAdapter.test.ts b/tests/charging-station/ocpp/auth/adapters/OCPP20AuthAdapter.test.ts index b190b72e..7f80781c 100644 --- a/tests/charging-station/ocpp/auth/adapters/OCPP20AuthAdapter.test.ts +++ b/tests/charging-station/ocpp/auth/adapters/OCPP20AuthAdapter.test.ts @@ -13,7 +13,7 @@ import { type AuthConfiguration, AuthContext, AuthenticationMethod, - AuthorizationStatus, + AuthResultStatus, IdentifierType, } from '../../../../../src/charging-station/ocpp/auth/types/AuthTypes.js' import { @@ -222,7 +222,7 @@ await describe('OCPP20AuthAdapter', async () => { const result = await adapter.authorizeRemote(identifier, 1, 'tx_123') - assert.strictEqual(result.status, AuthorizationStatus.ACCEPTED) + assert.strictEqual(result.status, AuthResultStatus.ACCEPTED) assert.strictEqual(result.method, AuthenticationMethod.REMOTE_AUTHORIZATION) assert.strictEqual(result.isOffline, false) assert.ok(result.timestamp instanceof Date) @@ -233,7 +233,7 @@ await describe('OCPP20AuthAdapter', async () => { const result = await adapter.authorizeRemote(identifier, 1) - assert.strictEqual(result.status, AuthorizationStatus.INVALID) + assert.strictEqual(result.status, AuthResultStatus.INVALID) assert.notStrictEqual(result.additionalInfo?.error, undefined) }) }) @@ -344,9 +344,9 @@ await describe('OCPP20AuthAdapter', async () => { await it('should convert rejection statuses to OCPP 2.0 Rejected', () => { const statuses = [ - AuthorizationStatus.BLOCKED, - AuthorizationStatus.INVALID, - AuthorizationStatus.EXPIRED, + AuthResultStatus.BLOCKED, + AuthResultStatus.INVALID, + AuthResultStatus.EXPIRED, ] for (const status of statuses) { diff --git a/tests/charging-station/ocpp/auth/cache/InMemoryAuthCache.test.ts b/tests/charging-station/ocpp/auth/cache/InMemoryAuthCache.test.ts index 4b4b7cba..7e812105 100644 --- a/tests/charging-station/ocpp/auth/cache/InMemoryAuthCache.test.ts +++ b/tests/charging-station/ocpp/auth/cache/InMemoryAuthCache.test.ts @@ -10,7 +10,7 @@ import type { AuthorizationResult } from '../../../../../src/charging-station/oc import { InMemoryAuthCache } from '../../../../../src/charging-station/ocpp/auth/cache/InMemoryAuthCache.js' import { AuthenticationMethod, - AuthorizationStatus, + AuthResultStatus, } from '../../../../../src/charging-station/ocpp/auth/types/AuthTypes.js' import { standardCleanup, withMockTimers } from '../../../../helpers/TestLifecycleHelpers.js' import { createMockAuthorizationResult } from '../helpers/MockFactories.js' @@ -50,7 +50,7 @@ await describe('InMemoryAuthCache - OCPP 2.0.1 Authorization Cache Conformance', beforeEach(() => { mockResult = createMockAuthorizationResult({ - status: AuthorizationStatus.ACCEPTED, + status: AuthResultStatus.ACCEPTED, }) }) @@ -64,7 +64,7 @@ await describe('InMemoryAuthCache - OCPP 2.0.1 Authorization Cache Conformance', const cachedResult = cache.get(identifier) assert.notStrictEqual(cachedResult, undefined) - assert.strictEqual(cachedResult?.status, AuthorizationStatus.ACCEPTED) + assert.strictEqual(cachedResult?.status, AuthResultStatus.ACCEPTED) assert.deepStrictEqual(cachedResult.timestamp, mockResult.timestamp) }) @@ -175,7 +175,7 @@ await describe('InMemoryAuthCache - OCPP 2.0.1 Authorization Cache Conformance', const result = cache.get(identifier) assert.notStrictEqual(result, undefined) - assert.strictEqual(result?.status, AuthorizationStatus.EXPIRED) + assert.strictEqual(result?.status, AuthResultStatus.EXPIRED) }) }) @@ -209,7 +209,7 @@ await describe('InMemoryAuthCache - OCPP 2.0.1 Authorization Cache Conformance', const result = cacheWithShortTTL.get('token') assert.notStrictEqual(result, undefined) - assert.strictEqual(result?.status, AuthorizationStatus.EXPIRED) + assert.strictEqual(result?.status, AuthResultStatus.EXPIRED) }) }) @@ -507,7 +507,7 @@ await describe('InMemoryAuthCache - OCPP 2.0.1 Authorization Cache Conformance', const result = cache.get('token') assert.notStrictEqual(result, undefined) - assert.strictEqual(result?.status, AuthorizationStatus.EXPIRED) + assert.strictEqual(result?.status, AuthResultStatus.EXPIRED) }) await it('should handle very large TTL values', () => { @@ -523,25 +523,25 @@ await describe('InMemoryAuthCache - OCPP 2.0.1 Authorization Cache Conformance', await it('should cache ACCEPTED authorization results', () => { const mockResult = createMockAuthorizationResult({ method: AuthenticationMethod.REMOTE_AUTHORIZATION, - status: AuthorizationStatus.ACCEPTED, + status: AuthResultStatus.ACCEPTED, }) cache.set('valid-token', mockResult) const result = cache.get('valid-token') - assert.strictEqual(result?.status, AuthorizationStatus.ACCEPTED) + assert.strictEqual(result?.status, AuthResultStatus.ACCEPTED) assert.strictEqual(result.method, AuthenticationMethod.REMOTE_AUTHORIZATION) }) await it('should handle BLOCKED authorization results', () => { const mockResult = createMockAuthorizationResult({ - status: AuthorizationStatus.BLOCKED, + status: AuthResultStatus.BLOCKED, }) cache.set('blocked-token', mockResult) const result = cache.get('blocked-token') - assert.strictEqual(result?.status, AuthorizationStatus.BLOCKED) + assert.strictEqual(result?.status, AuthResultStatus.BLOCKED) }) await it('should preserve authorization result metadata', () => { @@ -550,7 +550,7 @@ await describe('InMemoryAuthCache - OCPP 2.0.1 Authorization Cache Conformance', customField: 'test-value', reason: 'test-reason', }, - status: AuthorizationStatus.ACCEPTED, + status: AuthResultStatus.ACCEPTED, }) cache.set('token', mockResult) @@ -570,7 +570,7 @@ await describe('InMemoryAuthCache - OCPP 2.0.1 Authorization Cache Conformance', const mockResult = createMockAuthorizationResult({ isOffline: true, method: AuthenticationMethod.OFFLINE_FALLBACK, - status: AuthorizationStatus.ACCEPTED, + status: AuthResultStatus.ACCEPTED, }) cache.set('offline-token', mockResult) @@ -592,8 +592,8 @@ await describe('InMemoryAuthCache - OCPP 2.0.1 Authorization Cache Conformance', rateLimit: { enabled: false }, }) - const accepted = createMockAuthorizationResult({ status: AuthorizationStatus.ACCEPTED }) - const blocked = createMockAuthorizationResult({ status: AuthorizationStatus.BLOCKED }) + const accepted = createMockAuthorizationResult({ status: AuthResultStatus.ACCEPTED }) + const blocked = createMockAuthorizationResult({ status: AuthResultStatus.BLOCKED }) lruCache.set('valid-token', accepted) lruCache.set('blocked-token', blocked) @@ -609,7 +609,7 @@ await describe('InMemoryAuthCache - OCPP 2.0.1 Authorization Cache Conformance', const newResult = lruCache.get('new-token') assert.notStrictEqual(validResult, undefined) - assert.strictEqual(validResult?.status, AuthorizationStatus.ACCEPTED) + assert.strictEqual(validResult?.status, AuthResultStatus.ACCEPTED) assert.strictEqual(blockedResult, undefined) assert.notStrictEqual(newResult, undefined) }) @@ -621,7 +621,7 @@ await describe('InMemoryAuthCache - OCPP 2.0.1 Authorization Cache Conformance', rateLimit: { enabled: false }, }) - const accepted = createMockAuthorizationResult({ status: AuthorizationStatus.ACCEPTED }) + const accepted = createMockAuthorizationResult({ status: AuthResultStatus.ACCEPTED }) lruCache.set('token-a', accepted) lruCache.set('token-b', accepted) @@ -651,18 +651,18 @@ await describe('InMemoryAuthCache - OCPP 2.0.1 Authorization Cache Conformance', rateLimit: { enabled: false }, }) - const accepted = createMockAuthorizationResult({ status: AuthorizationStatus.ACCEPTED }) + const accepted = createMockAuthorizationResult({ status: AuthResultStatus.ACCEPTED }) shortCache.set('token', accepted) t.mock.timers.tick(50) const midResult = shortCache.get('token') assert.notStrictEqual(midResult, undefined) - assert.strictEqual(midResult?.status, AuthorizationStatus.ACCEPTED) + assert.strictEqual(midResult?.status, AuthResultStatus.ACCEPTED) t.mock.timers.tick(50) const lateResult = shortCache.get('token') assert.notStrictEqual(lateResult, undefined) - assert.strictEqual(lateResult?.status, AuthorizationStatus.ACCEPTED) + assert.strictEqual(lateResult?.status, AuthResultStatus.ACCEPTED) }) }) @@ -674,13 +674,13 @@ await describe('InMemoryAuthCache - OCPP 2.0.1 Authorization Cache Conformance', rateLimit: { enabled: false }, }) - const accepted = createMockAuthorizationResult({ status: AuthorizationStatus.ACCEPTED }) + const accepted = createMockAuthorizationResult({ status: AuthResultStatus.ACCEPTED }) shortCache.set('token', accepted) t.mock.timers.tick(200) const result = shortCache.get('token') assert.notStrictEqual(result, undefined) - assert.strictEqual(result?.status, AuthorizationStatus.EXPIRED) + assert.strictEqual(result?.status, AuthResultStatus.EXPIRED) }) }) }) @@ -694,14 +694,14 @@ await describe('InMemoryAuthCache - OCPP 2.0.1 Authorization Cache Conformance', rateLimit: { enabled: false }, }) - const accepted = createMockAuthorizationResult({ status: AuthorizationStatus.ACCEPTED }) + const accepted = createMockAuthorizationResult({ status: AuthResultStatus.ACCEPTED }) shortCache.set('token', accepted) t.mock.timers.tick(10) const result = shortCache.get('token') assert.notStrictEqual(result, undefined) - assert.strictEqual(result?.status, AuthorizationStatus.EXPIRED) + assert.strictEqual(result?.status, AuthResultStatus.EXPIRED) }) }) @@ -713,19 +713,19 @@ await describe('InMemoryAuthCache - OCPP 2.0.1 Authorization Cache Conformance', rateLimit: { enabled: false }, }) - const accepted = createMockAuthorizationResult({ status: AuthorizationStatus.ACCEPTED }) + const accepted = createMockAuthorizationResult({ status: AuthResultStatus.ACCEPTED }) shortCache.set('token', accepted) t.mock.timers.tick(10) // First access transitions to EXPIRED const first = shortCache.get('token') - assert.strictEqual(first?.status, AuthorizationStatus.EXPIRED) + assert.strictEqual(first?.status, AuthResultStatus.EXPIRED) // Second access should still return the entry (now with refreshed TTL as EXPIRED) const second = shortCache.get('token') assert.notStrictEqual(second, undefined) - assert.strictEqual(second?.status, AuthorizationStatus.EXPIRED) + assert.strictEqual(second?.status, AuthResultStatus.EXPIRED) const stats = shortCache.getStats() assert.strictEqual(stats.totalEntries, 1) diff --git a/tests/charging-station/ocpp/auth/helpers/MockFactories.ts b/tests/charging-station/ocpp/auth/helpers/MockFactories.ts index cf5aeb05..2f5381aa 100644 --- a/tests/charging-station/ocpp/auth/helpers/MockFactories.ts +++ b/tests/charging-station/ocpp/auth/helpers/MockFactories.ts @@ -20,8 +20,8 @@ import { AuthContext, AuthenticationMethod, type AuthorizationResult, - AuthorizationStatus, type AuthRequest, + AuthResultStatus, type Identifier, IdentifierType, } from '../../../../../src/charging-station/ocpp/auth/types/AuthTypes.js' @@ -68,7 +68,7 @@ export const createMockAuthRequest = (overrides?: Partial): AuthReq /** * Create a mock AuthorizationResult with configurable properties. * - * Supports all AuthorizationStatus values: ACCEPTED, INVALID, BLOCKED, EXPIRED, CONCURRENT_TX. + * Supports all AuthResultStatus values: ACCEPTED, INVALID, BLOCKED, EXPIRED, CONCURRENT_TX. * @param overrides - Partial AuthorizationResult properties to override defaults * @returns Mock AuthorizationResult with specified properties (defaults to ACCEPTED from local list) * @example @@ -77,17 +77,17 @@ export const createMockAuthRequest = (overrides?: Partial): AuthReq * const accepted = createMockAuthorizationResult() * * // Rejected with INVALID status - * const rejected = createMockAuthorizationResult({ status: AuthorizationStatus.INVALID }) + * const rejected = createMockAuthorizationResult({ status: AuthResultStatus.INVALID }) * * // Blocked with custom method * const blocked = createMockAuthorizationResult({ - * status: AuthorizationStatus.BLOCKED, + * status: AuthResultStatus.BLOCKED, * method: AuthenticationMethod.REMOTE_AUTHORIZATION * }) * * // Expired with custom expiry date * const expired = createMockAuthorizationResult({ - * status: AuthorizationStatus.EXPIRED, + * status: AuthResultStatus.EXPIRED, * expiryDate: new Date(Date.now() - 1000) * }) * ``` @@ -95,14 +95,14 @@ export const createMockAuthRequest = (overrides?: Partial): AuthReq export const createMockAuthorizationResult = ( overrides?: Partial ): AuthorizationResult => { - const status = overrides?.status ?? AuthorizationStatus.ACCEPTED + const status = overrides?.status ?? AuthResultStatus.ACCEPTED return { isOffline: false, method: AuthenticationMethod.LOCAL_LIST, status, timestamp: new Date(), // For expired status, include a default expiryDate in the past - ...(status === AuthorizationStatus.EXPIRED && { expiryDate: new Date(Date.now() - 1000) }), + ...(status === AuthResultStatus.EXPIRED && { expiryDate: new Date(Date.now() - 1000) }), ...overrides, } } @@ -234,7 +234,7 @@ export const expectAcceptedAuthorization = ( result: AuthorizationResult, expectedMethod?: AuthenticationMethod ): void => { - assert.strictEqual(result.status, AuthorizationStatus.ACCEPTED) + assert.strictEqual(result.status, AuthResultStatus.ACCEPTED) assert.ok(result.timestamp instanceof Date) if (expectedMethod !== undefined) { assert.strictEqual(result.method, expectedMethod) @@ -248,10 +248,10 @@ export const expectAcceptedAuthorization = ( */ export const expectRejectedAuthorization = ( result: AuthorizationResult, - expectedStatus: AuthorizationStatus = AuthorizationStatus.INVALID + expectedStatus: AuthResultStatus = AuthResultStatus.INVALID ): void => { assert.strictEqual(result.status, expectedStatus) - assert.notStrictEqual(result.status, AuthorizationStatus.ACCEPTED) + assert.notStrictEqual(result.status, AuthResultStatus.ACCEPTED) assert.ok(result.timestamp instanceof Date) } diff --git a/tests/charging-station/ocpp/auth/services/OCPPAuthServiceImpl.test.ts b/tests/charging-station/ocpp/auth/services/OCPPAuthServiceImpl.test.ts index 554c36e4..7f139b9b 100644 --- a/tests/charging-station/ocpp/auth/services/OCPPAuthServiceImpl.test.ts +++ b/tests/charging-station/ocpp/auth/services/OCPPAuthServiceImpl.test.ts @@ -12,7 +12,7 @@ import { OCPPAuthServiceImpl } from '../../../../../src/charging-station/ocpp/au import { AuthContext, AuthenticationMethod, - AuthorizationStatus, + AuthResultStatus, type Identifier, IdentifierType, } from '../../../../../src/charging-station/ocpp/auth/types/AuthTypes.js' @@ -238,7 +238,7 @@ await describe('OCPPAuthServiceImpl', async () => { timestamp: new Date(), }) - assert.strictEqual(result.status, AuthorizationStatus.INVALID) + assert.strictEqual(result.status, AuthResultStatus.INVALID) assert.strictEqual(result.method, AuthenticationMethod.NONE) }) }) @@ -336,7 +336,7 @@ await describe('OCPPAuthServiceImpl', async () => { timestamp: new Date(), }) - assert.strictEqual(result.status, AuthorizationStatus.INVALID) + assert.strictEqual(result.status, AuthResultStatus.INVALID) }) }) diff --git a/tests/charging-station/ocpp/auth/strategies/CertificateAuthStrategy.test.ts b/tests/charging-station/ocpp/auth/strategies/CertificateAuthStrategy.test.ts index 5fe4c621..011fc191 100644 --- a/tests/charging-station/ocpp/auth/strategies/CertificateAuthStrategy.test.ts +++ b/tests/charging-station/ocpp/auth/strategies/CertificateAuthStrategy.test.ts @@ -12,7 +12,7 @@ import { CertificateAuthStrategy } from '../../../../../src/charging-station/ocp import { AuthenticationMethod, type AuthorizationResult, - AuthorizationStatus, + AuthResultStatus, IdentifierType, } from '../../../../../src/charging-station/ocpp/auth/types/AuthTypes.js' import { OCPPVersion } from '../../../../../src/types/index.js' @@ -191,7 +191,7 @@ await describe('CertificateAuthStrategy', async () => { const result = await strategy.authenticate(request, config) assert.notStrictEqual(result, undefined) - assert.strictEqual(result?.status, AuthorizationStatus.ACCEPTED) + assert.strictEqual(result?.status, AuthResultStatus.ACCEPTED) assert.strictEqual(result.method, AuthenticationMethod.CERTIFICATE_BASED) }) @@ -213,7 +213,7 @@ await describe('CertificateAuthStrategy', async () => { const result = await strategy.authenticate(request, config) assert.notStrictEqual(result, undefined) - assert.strictEqual(result?.status, AuthorizationStatus.BLOCKED) + assert.strictEqual(result?.status, AuthResultStatus.BLOCKED) }) await it('should reject revoked certificates', async () => { @@ -234,7 +234,7 @@ await describe('CertificateAuthStrategy', async () => { const result = await strategy.authenticate(request, config) assert.notStrictEqual(result, undefined) - assert.strictEqual(result?.status, AuthorizationStatus.BLOCKED) + assert.strictEqual(result?.status, AuthResultStatus.BLOCKED) }) await it('should handle missing certificate data', async () => { @@ -249,7 +249,7 @@ await describe('CertificateAuthStrategy', async () => { const result = await strategy.authenticate(request, config) assert.notStrictEqual(result, undefined) - assert.strictEqual(result?.status, AuthorizationStatus.INVALID) + assert.strictEqual(result?.status, AuthResultStatus.INVALID) }) await it('should handle invalid hash algorithm', async () => { @@ -270,7 +270,7 @@ await describe('CertificateAuthStrategy', async () => { const result = await strategy.authenticate(request, config) assert.notStrictEqual(result, undefined) - assert.strictEqual(result?.status, AuthorizationStatus.INVALID) + assert.strictEqual(result?.status, AuthResultStatus.INVALID) }) await it('should handle invalid hash format', async () => { @@ -291,7 +291,7 @@ await describe('CertificateAuthStrategy', async () => { const result = await strategy.authenticate(request, config) assert.notStrictEqual(result, undefined) - assert.strictEqual(result?.status, AuthorizationStatus.INVALID) + assert.strictEqual(result?.status, AuthResultStatus.INVALID) }) }) diff --git a/tests/charging-station/ocpp/auth/strategies/LocalAuthStrategy-DisablePostAuthorize.test.ts b/tests/charging-station/ocpp/auth/strategies/LocalAuthStrategy-DisablePostAuthorize.test.ts index 4bf4134c..be39959a 100644 --- a/tests/charging-station/ocpp/auth/strategies/LocalAuthStrategy-DisablePostAuthorize.test.ts +++ b/tests/charging-station/ocpp/auth/strategies/LocalAuthStrategy-DisablePostAuthorize.test.ts @@ -8,7 +8,7 @@ import { afterEach, describe, it } from 'node:test' import { LocalAuthStrategy } from '../../../../../src/charging-station/ocpp/auth/strategies/LocalAuthStrategy.js' import { AuthenticationMethod, - AuthorizationStatus, + AuthResultStatus, IdentifierType, } from '../../../../../src/charging-station/ocpp/auth/types/AuthTypes.js' import { standardCleanup } from '../../../../helpers/TestLifecycleHelpers.js' @@ -33,7 +33,7 @@ await describe('LocalAuthStrategy - DisablePostAuthorize', async () => { // Arrange const blockedResult = createMockAuthorizationResult({ method: AuthenticationMethod.CACHE, - status: AuthorizationStatus.BLOCKED, + status: AuthResultStatus.BLOCKED, }) const mockAuthCache = createMockAuthCache({ get: () => blockedResult, @@ -53,7 +53,7 @@ await describe('LocalAuthStrategy - DisablePostAuthorize', async () => { // Assert assert.notStrictEqual(result, undefined) - assert.strictEqual(result?.status, AuthorizationStatus.BLOCKED) + assert.strictEqual(result?.status, AuthResultStatus.BLOCKED) assert.strictEqual(result.method, AuthenticationMethod.CACHE) }) @@ -61,7 +61,7 @@ await describe('LocalAuthStrategy - DisablePostAuthorize', async () => { // Arrange const blockedResult = createMockAuthorizationResult({ method: AuthenticationMethod.CACHE, - status: AuthorizationStatus.BLOCKED, + status: AuthResultStatus.BLOCKED, }) const mockAuthCache = createMockAuthCache({ get: () => blockedResult, @@ -105,7 +105,7 @@ await describe('LocalAuthStrategy - DisablePostAuthorize', async () => { const result = strategy.authenticate(request, config) assert.notStrictEqual(result, undefined) - assert.strictEqual(result?.status, AuthorizationStatus.BLOCKED) + assert.strictEqual(result?.status, AuthResultStatus.BLOCKED) assert.strictEqual(result.method, AuthenticationMethod.LOCAL_LIST) }) }) @@ -115,7 +115,7 @@ await describe('LocalAuthStrategy - DisablePostAuthorize', async () => { // Arrange const blockedResult = createMockAuthorizationResult({ method: AuthenticationMethod.CACHE, - status: AuthorizationStatus.BLOCKED, + status: AuthResultStatus.BLOCKED, }) const mockAuthCache = createMockAuthCache({ get: () => blockedResult, diff --git a/tests/charging-station/ocpp/auth/strategies/LocalAuthStrategy.test.ts b/tests/charging-station/ocpp/auth/strategies/LocalAuthStrategy.test.ts index 5d85ff4f..0fe78be9 100644 --- a/tests/charging-station/ocpp/auth/strategies/LocalAuthStrategy.test.ts +++ b/tests/charging-station/ocpp/auth/strategies/LocalAuthStrategy.test.ts @@ -14,7 +14,7 @@ import { LocalAuthStrategy } from '../../../../../src/charging-station/ocpp/auth import { AuthContext, AuthenticationMethod, - AuthorizationStatus, + AuthResultStatus, IdentifierType, } from '../../../../../src/charging-station/ocpp/auth/types/AuthTypes.js' import { standardCleanup } from '../../../../helpers/TestLifecycleHelpers.js' @@ -121,7 +121,7 @@ await describe('LocalAuthStrategy', async () => { const result = strategy.authenticate(request, config) assert.notStrictEqual(result, undefined) - assert.strictEqual(result?.status, AuthorizationStatus.ACCEPTED) + assert.strictEqual(result?.status, AuthResultStatus.ACCEPTED) assert.strictEqual(result.method, AuthenticationMethod.LOCAL_LIST) }) @@ -141,7 +141,7 @@ await describe('LocalAuthStrategy', async () => { const result = strategy.authenticate(request, config) assert.notStrictEqual(result, undefined) - assert.strictEqual(result?.status, AuthorizationStatus.ACCEPTED) + assert.strictEqual(result?.status, AuthResultStatus.ACCEPTED) assert.strictEqual(result.method, AuthenticationMethod.CACHE) }) @@ -156,7 +156,7 @@ await describe('LocalAuthStrategy', async () => { const result = strategy.authenticate(request, config) assert.notStrictEqual(result, undefined) - assert.strictEqual(result?.status, AuthorizationStatus.ACCEPTED) + assert.strictEqual(result?.status, AuthResultStatus.ACCEPTED) assert.strictEqual(result.method, AuthenticationMethod.OFFLINE_FALLBACK) assert.strictEqual(result.isOffline, true) }) diff --git a/tests/charging-station/ocpp/auth/strategies/RemoteAuthStrategy.test.ts b/tests/charging-station/ocpp/auth/strategies/RemoteAuthStrategy.test.ts index 6f6520b4..5c09d90c 100644 --- a/tests/charging-station/ocpp/auth/strategies/RemoteAuthStrategy.test.ts +++ b/tests/charging-station/ocpp/auth/strategies/RemoteAuthStrategy.test.ts @@ -15,7 +15,7 @@ import { RemoteAuthStrategy } from '../../../../../src/charging-station/ocpp/aut import { AuthenticationMethod, type AuthorizationResult, - AuthorizationStatus, + AuthResultStatus, IdentifierType, } from '../../../../../src/charging-station/ocpp/auth/types/AuthTypes.js' import { OCPPVersion } from '../../../../../src/types/index.js' @@ -123,7 +123,7 @@ await describe('RemoteAuthStrategy', async () => { const result = await strategy.authenticate(request, config) assert.notStrictEqual(result, undefined) - assert.strictEqual(result?.status, AuthorizationStatus.ACCEPTED) + assert.strictEqual(result?.status, AuthResultStatus.ACCEPTED) assert.strictEqual(result.method, AuthenticationMethod.REMOTE_AUTHORIZATION) }) @@ -147,7 +147,7 @@ await describe('RemoteAuthStrategy', async () => { await strategy.authenticate(request, config) assert.strictEqual(cachedKey, 'CACHE_TAG') - assert.strictEqual(cachedValue?.status, AuthorizationStatus.ACCEPTED) + assert.strictEqual(cachedValue?.status, AuthResultStatus.ACCEPTED) assert.strictEqual(cachedTtl, 300) }) @@ -165,7 +165,7 @@ await describe('RemoteAuthStrategy', async () => { resolve( createMockAuthorizationResult({ method: AuthenticationMethod.REMOTE_AUTHORIZATION, - status: AuthorizationStatus.BLOCKED, + status: AuthResultStatus.BLOCKED, }) ) }) @@ -180,7 +180,7 @@ await describe('RemoteAuthStrategy', async () => { await strategy.authenticate(request, config) assert.strictEqual(cachedKey, 'BLOCKED_TAG') - assert.strictEqual(cachedValue?.status, AuthorizationStatus.BLOCKED) + assert.strictEqual(cachedValue?.status, AuthResultStatus.BLOCKED) assert.strictEqual(cachedTtl, 300) }) @@ -198,7 +198,7 @@ await describe('RemoteAuthStrategy', async () => { resolve( createMockAuthorizationResult({ method: AuthenticationMethod.REMOTE_AUTHORIZATION, - status: AuthorizationStatus.EXPIRED, + status: AuthResultStatus.EXPIRED, }) ) }) @@ -213,7 +213,7 @@ await describe('RemoteAuthStrategy', async () => { await strategy.authenticate(request, config) assert.strictEqual(cachedKey, 'EXPIRED_TAG') - assert.strictEqual(cachedValue?.status, AuthorizationStatus.EXPIRED) + assert.strictEqual(cachedValue?.status, AuthResultStatus.EXPIRED) assert.strictEqual(cachedTtl, 300) }) @@ -231,7 +231,7 @@ await describe('RemoteAuthStrategy', async () => { resolve( createMockAuthorizationResult({ method: AuthenticationMethod.REMOTE_AUTHORIZATION, - status: AuthorizationStatus.INVALID, + status: AuthResultStatus.INVALID, }) ) }) @@ -246,7 +246,7 @@ await describe('RemoteAuthStrategy', async () => { await strategy.authenticate(request, config) assert.strictEqual(cachedKey, TEST_ID_TAG_INVALID) - assert.strictEqual(cachedValue?.status, AuthorizationStatus.INVALID) + assert.strictEqual(cachedValue?.status, AuthResultStatus.INVALID) assert.strictEqual(cachedTtl, 300) }) @@ -270,7 +270,7 @@ await describe('RemoteAuthStrategy', async () => { await strategy.authenticate(request, config) assert.strictEqual(cachedKey, 'ACCEPTED_TAG') - assert.strictEqual(cachedValue?.status, AuthorizationStatus.ACCEPTED) + assert.strictEqual(cachedValue?.status, AuthResultStatus.ACCEPTED) assert.strictEqual(cachedTtl, 300) }) @@ -331,7 +331,7 @@ await describe('RemoteAuthStrategy', async () => { const result = await strategy.authenticate(request, config) assert.notStrictEqual(result, undefined) - assert.strictEqual(result?.status, AuthorizationStatus.ACCEPTED) + assert.strictEqual(result?.status, AuthResultStatus.ACCEPTED) }) await it('C13.FR.01.T01 - should not cache identifier that is in local auth list', async () => { diff --git a/tests/charging-station/ocpp/auth/types/AuthTypes.test.ts b/tests/charging-station/ocpp/auth/types/AuthTypes.test.ts index 5564dc15..22dc29ba 100644 --- a/tests/charging-station/ocpp/auth/types/AuthTypes.test.ts +++ b/tests/charging-station/ocpp/auth/types/AuthTypes.test.ts @@ -9,7 +9,7 @@ import { AuthContext, AuthenticationError, AuthErrorCode, - AuthorizationStatus, + AuthResultStatus, type Identifier, IdentifierType, isCertificateBased, @@ -68,27 +68,27 @@ await describe('AuthTypes', async () => { await describe('OCPP 1.6 Status Mapping', async () => { await it('should map OCPP 1.6 ACCEPTED to ACCEPTED', () => { const result = mapOCPP16Status(OCPP16AuthorizationStatus.ACCEPTED) - assert.strictEqual(result, AuthorizationStatus.ACCEPTED) + assert.strictEqual(result, AuthResultStatus.ACCEPTED) }) await it('should map OCPP 1.6 BLOCKED to BLOCKED', () => { const result = mapOCPP16Status(OCPP16AuthorizationStatus.BLOCKED) - assert.strictEqual(result, AuthorizationStatus.BLOCKED) + assert.strictEqual(result, AuthResultStatus.BLOCKED) }) await it('should map OCPP 1.6 EXPIRED to EXPIRED', () => { const result = mapOCPP16Status(OCPP16AuthorizationStatus.EXPIRED) - assert.strictEqual(result, AuthorizationStatus.EXPIRED) + assert.strictEqual(result, AuthResultStatus.EXPIRED) }) await it('should map OCPP 1.6 INVALID to INVALID', () => { const result = mapOCPP16Status(OCPP16AuthorizationStatus.INVALID) - assert.strictEqual(result, AuthorizationStatus.INVALID) + assert.strictEqual(result, AuthResultStatus.INVALID) }) await it('should map OCPP 1.6 CONCURRENT_TX to CONCURRENT_TX', () => { const result = mapOCPP16Status(OCPP16AuthorizationStatus.CONCURRENT_TX) - assert.strictEqual(result, AuthorizationStatus.CONCURRENT_TX) + assert.strictEqual(result, AuthResultStatus.CONCURRENT_TX) }) }) @@ -121,31 +121,31 @@ await describe('AuthTypes', async () => { await describe('Auth to OCPP 1.6 Status Mapping', async () => { await it('should map ACCEPTED to OCPP 1.6 ACCEPTED', () => { - const result = mapToOCPP16Status(AuthorizationStatus.ACCEPTED) + const result = mapToOCPP16Status(AuthResultStatus.ACCEPTED) assert.strictEqual(result, OCPP16AuthorizationStatus.ACCEPTED) }) await it('should map BLOCKED to OCPP 1.6 BLOCKED', () => { - const result = mapToOCPP16Status(AuthorizationStatus.BLOCKED) + const result = mapToOCPP16Status(AuthResultStatus.BLOCKED) assert.strictEqual(result, OCPP16AuthorizationStatus.BLOCKED) }) await it('should map EXPIRED to OCPP 1.6 EXPIRED', () => { - const result = mapToOCPP16Status(AuthorizationStatus.EXPIRED) + const result = mapToOCPP16Status(AuthResultStatus.EXPIRED) assert.strictEqual(result, OCPP16AuthorizationStatus.EXPIRED) }) await it('should map unsupported statuses to OCPP 1.6 INVALID', () => { assert.strictEqual( - mapToOCPP16Status(AuthorizationStatus.PENDING), + mapToOCPP16Status(AuthResultStatus.PENDING), OCPP16AuthorizationStatus.INVALID ) assert.strictEqual( - mapToOCPP16Status(AuthorizationStatus.UNKNOWN), + mapToOCPP16Status(AuthResultStatus.UNKNOWN), OCPP16AuthorizationStatus.INVALID ) assert.strictEqual( - mapToOCPP16Status(AuthorizationStatus.NOT_AT_THIS_LOCATION), + mapToOCPP16Status(AuthResultStatus.NOT_AT_THIS_LOCATION), OCPP16AuthorizationStatus.INVALID ) }) @@ -153,21 +153,21 @@ await describe('AuthTypes', async () => { await describe('Auth to OCPP 2.0 Status Mapping', async () => { await it('should map ACCEPTED to OCPP 2.0 Accepted', () => { - const result = mapToOCPP20Status(AuthorizationStatus.ACCEPTED) + const result = mapToOCPP20Status(AuthResultStatus.ACCEPTED) assert.strictEqual(result, RequestStartStopStatusEnumType.Accepted) }) await it('should map rejection statuses to OCPP 2.0 Rejected', () => { assert.strictEqual( - mapToOCPP20Status(AuthorizationStatus.BLOCKED), + mapToOCPP20Status(AuthResultStatus.BLOCKED), RequestStartStopStatusEnumType.Rejected ) assert.strictEqual( - mapToOCPP20Status(AuthorizationStatus.INVALID), + mapToOCPP20Status(AuthResultStatus.INVALID), RequestStartStopStatusEnumType.Rejected ) assert.strictEqual( - mapToOCPP20Status(AuthorizationStatus.EXPIRED), + mapToOCPP20Status(AuthResultStatus.EXPIRED), RequestStartStopStatusEnumType.Rejected ) }) diff --git a/tests/charging-station/ocpp/auth/utils/AuthConfigValidator.test.ts b/tests/charging-station/ocpp/auth/utils/AuthConfigValidator.test.ts index fe2e625a..b48ac649 100644 --- a/tests/charging-station/ocpp/auth/utils/AuthConfigValidator.test.ts +++ b/tests/charging-station/ocpp/auth/utils/AuthConfigValidator.test.ts @@ -9,7 +9,7 @@ import { afterEach, describe, it } from 'node:test' import { type AuthConfiguration, AuthenticationError, - AuthorizationStatus, + AuthResultStatus, } from '../../../../../src/charging-station/ocpp/auth/types/AuthTypes.js' import { AuthConfigValidator } from '../../../../../src/charging-station/ocpp/auth/utils/AuthConfigValidator.js' import { standardCleanup } from '../../../../helpers/TestLifecycleHelpers.js' @@ -32,7 +32,7 @@ await describe('AuthConfigValidator', async () => { localPreAuthorize: false, maxCacheEntries: 1000, offlineAuthorizationEnabled: true, - unknownIdAuthorization: AuthorizationStatus.INVALID, + unknownIdAuthorization: AuthResultStatus.INVALID, } assert.doesNotThrow(() => { @@ -53,7 +53,7 @@ await describe('AuthConfigValidator', async () => { localPreAuthorize: false, maxCacheEntries: 1000, offlineAuthorizationEnabled: true, - unknownIdAuthorization: AuthorizationStatus.INVALID, + unknownIdAuthorization: AuthResultStatus.INVALID, } assert.throws(() => { @@ -74,7 +74,7 @@ await describe('AuthConfigValidator', async () => { localPreAuthorize: false, maxCacheEntries: 1000, offlineAuthorizationEnabled: true, - unknownIdAuthorization: AuthorizationStatus.INVALID, + unknownIdAuthorization: AuthResultStatus.INVALID, } assert.throws(() => { @@ -95,7 +95,7 @@ await describe('AuthConfigValidator', async () => { localPreAuthorize: false, maxCacheEntries: 1000, offlineAuthorizationEnabled: true, - unknownIdAuthorization: AuthorizationStatus.INVALID, + unknownIdAuthorization: AuthResultStatus.INVALID, } assert.throws(() => { @@ -116,7 +116,7 @@ await describe('AuthConfigValidator', async () => { localPreAuthorize: false, maxCacheEntries: -1, offlineAuthorizationEnabled: true, - unknownIdAuthorization: AuthorizationStatus.INVALID, + unknownIdAuthorization: AuthResultStatus.INVALID, } assert.throws(() => { @@ -137,7 +137,7 @@ await describe('AuthConfigValidator', async () => { localPreAuthorize: false, maxCacheEntries: 0, offlineAuthorizationEnabled: true, - unknownIdAuthorization: AuthorizationStatus.INVALID, + unknownIdAuthorization: AuthResultStatus.INVALID, } assert.throws(() => { @@ -158,7 +158,7 @@ await describe('AuthConfigValidator', async () => { localPreAuthorize: false, maxCacheEntries: 1000.5, offlineAuthorizationEnabled: true, - unknownIdAuthorization: AuthorizationStatus.INVALID, + unknownIdAuthorization: AuthResultStatus.INVALID, } assert.throws(() => { @@ -179,7 +179,7 @@ await describe('AuthConfigValidator', async () => { localPreAuthorize: false, maxCacheEntries: 1000, offlineAuthorizationEnabled: true, - unknownIdAuthorization: AuthorizationStatus.INVALID, + unknownIdAuthorization: AuthResultStatus.INVALID, } assert.throws(() => { @@ -200,7 +200,7 @@ await describe('AuthConfigValidator', async () => { localPreAuthorize: false, maxCacheEntries: 1000, offlineAuthorizationEnabled: true, - unknownIdAuthorization: AuthorizationStatus.INVALID, + unknownIdAuthorization: AuthResultStatus.INVALID, } assert.throws(() => { @@ -221,7 +221,7 @@ await describe('AuthConfigValidator', async () => { localPreAuthorize: false, maxCacheEntries: 1000, offlineAuthorizationEnabled: true, - unknownIdAuthorization: AuthorizationStatus.INVALID, + unknownIdAuthorization: AuthResultStatus.INVALID, } assert.throws(() => { @@ -242,7 +242,7 @@ await describe('AuthConfigValidator', async () => { localPreAuthorize: false, maxCacheEntries: 1000, offlineAuthorizationEnabled: true, - unknownIdAuthorization: AuthorizationStatus.INVALID, + unknownIdAuthorization: AuthResultStatus.INVALID, } assert.doesNotThrow(() => { @@ -263,7 +263,7 @@ await describe('AuthConfigValidator', async () => { localPreAuthorize: false, maxCacheEntries: 1, offlineAuthorizationEnabled: true, - unknownIdAuthorization: AuthorizationStatus.INVALID, + unknownIdAuthorization: AuthResultStatus.INVALID, } assert.doesNotThrow(() => { @@ -284,7 +284,7 @@ await describe('AuthConfigValidator', async () => { localPreAuthorize: false, maxCacheEntries: 10000, offlineAuthorizationEnabled: true, - unknownIdAuthorization: AuthorizationStatus.INVALID, + unknownIdAuthorization: AuthResultStatus.INVALID, } assert.doesNotThrow(() => { diff --git a/tests/charging-station/ocpp/auth/utils/AuthHelpers.test.ts b/tests/charging-station/ocpp/auth/utils/AuthHelpers.test.ts index e3c6091b..a3441420 100644 --- a/tests/charging-station/ocpp/auth/utils/AuthHelpers.test.ts +++ b/tests/charging-station/ocpp/auth/utils/AuthHelpers.test.ts @@ -9,7 +9,7 @@ import { AuthContext, AuthenticationMethod, type AuthorizationResult, - AuthorizationStatus, + AuthResultStatus, type Identifier, IdentifierType, } from '../../../../../src/charging-station/ocpp/auth/types/AuthTypes.js' @@ -98,11 +98,11 @@ await describe('AuthHelpers', async () => { await describe('createRejectedResult', async () => { await it('should create rejected result without reason', () => { const result = AuthHelpers.createRejectedResult( - AuthorizationStatus.BLOCKED, + AuthResultStatus.BLOCKED, AuthenticationMethod.LOCAL_LIST ) - assert.strictEqual(result.status, AuthorizationStatus.BLOCKED) + assert.strictEqual(result.status, AuthResultStatus.BLOCKED) assert.strictEqual(result.method, AuthenticationMethod.LOCAL_LIST) assert.strictEqual(result.isOffline, false) assert.ok(result.timestamp instanceof Date) @@ -111,12 +111,12 @@ await describe('AuthHelpers', async () => { await it('should create rejected result with reason', () => { const result = AuthHelpers.createRejectedResult( - AuthorizationStatus.EXPIRED, + AuthResultStatus.EXPIRED, AuthenticationMethod.REMOTE_AUTHORIZATION, 'Token expired on 2024-01-01' ) - assert.strictEqual(result.status, AuthorizationStatus.EXPIRED) + assert.strictEqual(result.status, AuthResultStatus.EXPIRED) assert.strictEqual(result.method, AuthenticationMethod.REMOTE_AUTHORIZATION) assert.deepStrictEqual(result.additionalInfo, { reason: 'Token expired on 2024-01-01' }) }) @@ -156,70 +156,70 @@ await describe('AuthHelpers', async () => { await describe('getStatusMessage', async () => { await it('should return message for ACCEPTED status', () => { assert.strictEqual( - AuthHelpers.getStatusMessage(AuthorizationStatus.ACCEPTED), + AuthHelpers.getStatusMessage(AuthResultStatus.ACCEPTED), 'Authorization accepted' ) }) await it('should return message for BLOCKED status', () => { assert.strictEqual( - AuthHelpers.getStatusMessage(AuthorizationStatus.BLOCKED), + AuthHelpers.getStatusMessage(AuthResultStatus.BLOCKED), 'Identifier is blocked' ) }) await it('should return message for EXPIRED status', () => { assert.strictEqual( - AuthHelpers.getStatusMessage(AuthorizationStatus.EXPIRED), + AuthHelpers.getStatusMessage(AuthResultStatus.EXPIRED), 'Authorization has expired' ) }) await it('should return message for INVALID status', () => { assert.strictEqual( - AuthHelpers.getStatusMessage(AuthorizationStatus.INVALID), + AuthHelpers.getStatusMessage(AuthResultStatus.INVALID), 'Invalid identifier' ) }) await it('should return message for CONCURRENT_TX status', () => { assert.strictEqual( - AuthHelpers.getStatusMessage(AuthorizationStatus.CONCURRENT_TX), + AuthHelpers.getStatusMessage(AuthResultStatus.CONCURRENT_TX), 'Concurrent transaction in progress' ) }) await it('should return message for NOT_AT_THIS_LOCATION status', () => { assert.strictEqual( - AuthHelpers.getStatusMessage(AuthorizationStatus.NOT_AT_THIS_LOCATION), + AuthHelpers.getStatusMessage(AuthResultStatus.NOT_AT_THIS_LOCATION), 'Not authorized at this location' ) }) await it('should return message for NOT_AT_THIS_TIME status', () => { assert.strictEqual( - AuthHelpers.getStatusMessage(AuthorizationStatus.NOT_AT_THIS_TIME), + AuthHelpers.getStatusMessage(AuthResultStatus.NOT_AT_THIS_TIME), 'Not authorized at this time' ) }) await it('should return message for PENDING status', () => { assert.strictEqual( - AuthHelpers.getStatusMessage(AuthorizationStatus.PENDING), + AuthHelpers.getStatusMessage(AuthResultStatus.PENDING), 'Authorization pending' ) }) await it('should return message for UNKNOWN status', () => { assert.strictEqual( - AuthHelpers.getStatusMessage(AuthorizationStatus.UNKNOWN), + AuthHelpers.getStatusMessage(AuthResultStatus.UNKNOWN), 'Unknown authorization status' ) }) await it('should return generic message for unknown status', () => { assert.strictEqual( - AuthHelpers.getStatusMessage(AuthorizationStatus.NO_CREDIT), + AuthHelpers.getStatusMessage(AuthResultStatus.NO_CREDIT), 'Authorization failed' ) }) @@ -230,7 +230,7 @@ await describe('AuthHelpers', async () => { const result: AuthorizationResult = { isOffline: false, method: AuthenticationMethod.LOCAL_LIST, - status: AuthorizationStatus.BLOCKED, + status: AuthResultStatus.BLOCKED, timestamp: new Date(), } @@ -241,7 +241,7 @@ await describe('AuthHelpers', async () => { const result: AuthorizationResult = { isOffline: false, method: AuthenticationMethod.LOCAL_LIST, - status: AuthorizationStatus.EXPIRED, + status: AuthResultStatus.EXPIRED, timestamp: new Date(), } @@ -252,7 +252,7 @@ await describe('AuthHelpers', async () => { const result: AuthorizationResult = { isOffline: false, method: AuthenticationMethod.LOCAL_LIST, - status: AuthorizationStatus.INVALID, + status: AuthResultStatus.INVALID, timestamp: new Date(), } @@ -263,7 +263,7 @@ await describe('AuthHelpers', async () => { const result: AuthorizationResult = { isOffline: false, method: AuthenticationMethod.LOCAL_LIST, - status: AuthorizationStatus.ACCEPTED, + status: AuthResultStatus.ACCEPTED, timestamp: new Date(), } @@ -274,7 +274,7 @@ await describe('AuthHelpers', async () => { const result: AuthorizationResult = { isOffline: false, method: AuthenticationMethod.LOCAL_LIST, - status: AuthorizationStatus.PENDING, + status: AuthResultStatus.PENDING, timestamp: new Date(), } @@ -287,7 +287,7 @@ await describe('AuthHelpers', async () => { const result: AuthorizationResult = { isOffline: false, method: AuthenticationMethod.LOCAL_LIST, - status: AuthorizationStatus.BLOCKED, + status: AuthResultStatus.BLOCKED, timestamp: new Date(), } @@ -298,7 +298,7 @@ await describe('AuthHelpers', async () => { const result: AuthorizationResult = { isOffline: false, method: AuthenticationMethod.LOCAL_LIST, - status: AuthorizationStatus.ACCEPTED, + status: AuthResultStatus.ACCEPTED, timestamp: new Date(), } @@ -310,7 +310,7 @@ await describe('AuthHelpers', async () => { expiryDate: new Date(Date.now() - 1000), isOffline: false, method: AuthenticationMethod.LOCAL_LIST, - status: AuthorizationStatus.ACCEPTED, + status: AuthResultStatus.ACCEPTED, timestamp: new Date(), } @@ -322,7 +322,7 @@ await describe('AuthHelpers', async () => { expiryDate: new Date(Date.now() + 10000), isOffline: false, method: AuthenticationMethod.LOCAL_LIST, - status: AuthorizationStatus.ACCEPTED, + status: AuthResultStatus.ACCEPTED, timestamp: new Date(), } @@ -335,7 +335,7 @@ await describe('AuthHelpers', async () => { const result: AuthorizationResult = { isOffline: false, method: AuthenticationMethod.LOCAL_LIST, - status: AuthorizationStatus.PENDING, + status: AuthResultStatus.PENDING, timestamp: new Date(), } @@ -346,7 +346,7 @@ await describe('AuthHelpers', async () => { const result: AuthorizationResult = { isOffline: false, method: AuthenticationMethod.LOCAL_LIST, - status: AuthorizationStatus.UNKNOWN, + status: AuthResultStatus.UNKNOWN, timestamp: new Date(), } @@ -357,7 +357,7 @@ await describe('AuthHelpers', async () => { const result: AuthorizationResult = { isOffline: false, method: AuthenticationMethod.LOCAL_LIST, - status: AuthorizationStatus.BLOCKED, + status: AuthResultStatus.BLOCKED, timestamp: new Date(), } @@ -368,7 +368,7 @@ await describe('AuthHelpers', async () => { const result: AuthorizationResult = { isOffline: false, method: AuthenticationMethod.LOCAL_LIST, - status: AuthorizationStatus.ACCEPTED, + status: AuthResultStatus.ACCEPTED, timestamp: new Date(), } @@ -387,25 +387,25 @@ await describe('AuthHelpers', async () => { { isOffline: false, method: AuthenticationMethod.LOCAL_LIST, - status: AuthorizationStatus.BLOCKED, + status: AuthResultStatus.BLOCKED, timestamp: new Date(), }, { isOffline: false, method: AuthenticationMethod.REMOTE_AUTHORIZATION, - status: AuthorizationStatus.ACCEPTED, + status: AuthResultStatus.ACCEPTED, timestamp: new Date(), }, { isOffline: false, method: AuthenticationMethod.CERTIFICATE_BASED, - status: AuthorizationStatus.ACCEPTED, + status: AuthResultStatus.ACCEPTED, timestamp: new Date(), }, ] const merged = AuthHelpers.mergeAuthResults(results) - assert.strictEqual(merged?.status, AuthorizationStatus.ACCEPTED) + assert.strictEqual(merged?.status, AuthResultStatus.ACCEPTED) assert.strictEqual(merged.method, AuthenticationMethod.REMOTE_AUTHORIZATION) }) @@ -414,19 +414,19 @@ await describe('AuthHelpers', async () => { { isOffline: false, method: AuthenticationMethod.LOCAL_LIST, - status: AuthorizationStatus.BLOCKED, + status: AuthResultStatus.BLOCKED, timestamp: new Date(), }, { isOffline: true, method: AuthenticationMethod.REMOTE_AUTHORIZATION, - status: AuthorizationStatus.EXPIRED, + status: AuthResultStatus.EXPIRED, timestamp: new Date(), }, ] const merged = AuthHelpers.mergeAuthResults(results) - assert.strictEqual(merged?.status, AuthorizationStatus.BLOCKED) + assert.strictEqual(merged?.status, AuthResultStatus.BLOCKED) assert.strictEqual(merged.method, AuthenticationMethod.LOCAL_LIST) assert.strictEqual(merged.isOffline, true) assert.deepStrictEqual(merged.additionalInfo, { @@ -447,7 +447,7 @@ await describe('AuthHelpers', async () => { content: 'Welcome', format: OCPP20MessageFormatEnumType.ASCII, }, - status: AuthorizationStatus.ACCEPTED, + status: AuthResultStatus.ACCEPTED, timestamp: new Date('2024-01-01T00:00:00Z'), } @@ -459,7 +459,7 @@ await describe('AuthHelpers', async () => { hasPersonalMessage: true, isOffline: false, method: AuthenticationMethod.LOCAL_LIST, - status: AuthorizationStatus.ACCEPTED, + status: AuthResultStatus.ACCEPTED, timestamp: '2024-01-01T00:00:00.000Z', }) }) @@ -468,7 +468,7 @@ await describe('AuthHelpers', async () => { const result: AuthorizationResult = { isOffline: true, method: AuthenticationMethod.REMOTE_AUTHORIZATION, - status: AuthorizationStatus.BLOCKED, + status: AuthResultStatus.BLOCKED, timestamp: new Date('2024-06-15T12:30:45Z'), } @@ -480,7 +480,7 @@ await describe('AuthHelpers', async () => { hasPersonalMessage: false, isOffline: true, method: AuthenticationMethod.REMOTE_AUTHORIZATION, - status: AuthorizationStatus.BLOCKED, + status: AuthResultStatus.BLOCKED, timestamp: '2024-06-15T12:30:45.000Z', }) }) diff --git a/tests/utils/Utils.test.ts b/tests/utils/Utils.test.ts index d92d3910..83f195c4 100644 --- a/tests/utils/Utils.test.ts +++ b/tests/utils/Utils.test.ts @@ -14,7 +14,7 @@ import { satisfies } from 'semver' import type { TimestampedData } from '../../src/types/index.js' import { JSRuntime, runtime } from '../../scripts/runtime.js' -import { MapStringifyFormat, MessageType } from '../../src/types/index.js' +import { MapStringifyFormat, MessageType, OCPPVersion } from '../../src/types/index.js' import { Constants } from '../../src/utils/index.js' import { clampToSafeTimerValue, @@ -42,6 +42,7 @@ import { isEmpty, isNotEmptyArray, isNotEmptyString, + isOCPP20x, isValidDate, JSONStringify, logPrefix, @@ -469,6 +470,13 @@ await describe('Utils', async () => { assert.strictEqual(isEmpty(new WeakSet()), false) }) + await it('should detect OCPP 2.0.x versions', () => { + assert.strictEqual(isOCPP20x(OCPPVersion.VERSION_20), true) + assert.strictEqual(isOCPP20x(OCPPVersion.VERSION_201), true) + assert.strictEqual(isOCPP20x(OCPPVersion.VERSION_16), false) + assert.strictEqual(isOCPP20x(undefined), false) + }) + await it('should detect non-empty strings correctly', () => { assert.strictEqual(isNotEmptyString(''), false) assert.strictEqual(isNotEmptyString(' '), false) diff --git a/ui/common/src/client/WebSocketClient.ts b/ui/common/src/client/WebSocketClient.ts index 1109c48f..b0a2329d 100644 --- a/ui/common/src/client/WebSocketClient.ts +++ b/ui/common/src/client/WebSocketClient.ts @@ -8,8 +8,6 @@ import { AuthenticationType, ResponseStatus } from '../types/UIProtocol.js' import { randomUUID, validateUUID } from '../utils/UUID.js' import { WebSocketReadyState } from './types.js' -export { ServerFailureError } from '../errors.js' - export class WebSocketClient { public get connected (): boolean { return this.ws?.readyState === WebSocketReadyState.OPEN diff --git a/ui/common/tests/WebSocketClient.test.ts b/ui/common/tests/WebSocketClient.test.ts index 12c63bbf..8e0ca5f9 100644 --- a/ui/common/tests/WebSocketClient.test.ts +++ b/ui/common/tests/WebSocketClient.test.ts @@ -6,8 +6,9 @@ import { describe, it } from 'node:test' import type { WebSocketFactory } from '../src/client/types.js' import type { ResponsePayload } from '../src/types/UIProtocol.js' -import { ServerFailureError, WebSocketClient } from '../src/client/WebSocketClient.js' +import { WebSocketClient } from '../src/client/WebSocketClient.js' import { DEFAULT_HOST, DEFAULT_PORT } from '../src/constants.js' +import { ServerFailureError } from '../src/errors.js' import { AuthenticationType, ProcedureName, -- 2.53.0