]> Piment Noir Git Repositories - e-mobility-charging-stations-simulator.git/commitdiff
refactor: convention alignment — helpers, constants, imports, tests (#1950)
authorJérôme Benoit <jerome.benoit@piment-noir.org>
Sun, 5 Jul 2026 17:20:58 +0000 (19:20 +0200)
committerGitHub <noreply@github.com>
Sun, 5 Jul 2026 17:20:58 +0000 (19:20 +0200)
Convention alignment refactor across the monorepo — no behavior change.

Helpers adoption:
- adopt isEmpty() helper for emptiness checks (9 sites); OCPPServiceUtils.ts:1113 preserved with anchor comment (already-trimmed input; isEmpty would redundantly re-trim)
- adopt Constants.UNIT_DIVIDER_KILO for kW→W conversion (4 sites)
- use JSONStringify for unknown MCP tool payload (Map/Set-safe replacer)

Constants promotion:
- promote inline literals to named constants (MAX_ITEMS_PER_REPORT_MESSAGE with OCPP 2.0.1 §2.1.16/§2.1.18 spec-anchor comment; AVG_ENTRY_SIZE_BYTES; DEFAULT_EXPONENTIAL_BACKOFF_BASE_DELAY_MS; DEFAULT_AUTH_CACHE_TTL_SECONDS; DEFAULT_PERSIST_STATE)
- coverage gap: isNotEmptyArray adoption at ConfigurationValidation.ts:124

Imports discipline:
- add .js extension to relative import in ui-web/vitest.config (ESM/NodeNext)
- use component barrels instead of deep imports in tests (10 sites)

Test literals:
- use DEFAULT_HOST/DEFAULT_PORT constants in ui-common tests (72 sites: 32 in config.test.ts + 40 in WebSocketClient.test.ts)

Documentation anchors:
- document Number.parseInt/parseFloat convention exceptions (3 comments covering 4 call sites where convertToInt/convertToFloat cannot substitute)

Quality gates: typecheck clean, lint clean, 2954/2960 tests pass, 0 fail.

Diff: +140/-113 across 28 files.

28 files changed:
src/charging-station/ChargingStation.ts
src/charging-station/CoherentMeterValuesManager.ts
src/charging-station/HelpersConfig.ts
src/charging-station/meter-values/CoherentMeterValueBuilder.ts
src/charging-station/meter-values/CoherentSession.ts
src/charging-station/meter-values/EvProfiles.ts
src/charging-station/ocpp/2.0/OCPP20CertificateManager.ts
src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.ts
src/charging-station/ocpp/OCPPServiceUtils.ts
src/charging-station/ocpp/auth/cache/InMemoryAuthCache.ts
src/charging-station/ocpp/auth/services/OCPPAuthServiceImpl.ts
src/charging-station/ui-server/UIMCPServer.ts
src/charging-station/ui-server/UIServerNet.ts
src/utils/Configuration.ts
src/utils/ConfigurationValidation.ts
src/utils/Constants.ts
tests/charging-station/helpers/TemplateFixtures.ts
tests/charging-station/ocpp/2.0/OCPP20CertSigningRetryManager.test.ts
tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-GetBaseReport.test.ts
tests/charging-station/ocpp/2.0/OCPP20ServiceUtils-TransactionEvent.test.ts
tests/charging-station/ocpp/2.0/OCPP20VariableManager.test.ts
tests/charging-station/ui-server/UIMCPServer.integration.test.ts
tests/charging-station/ui-server/UIMCPServer.test.ts
tests/charging-station/ui-server/UIWebSocketServer.test.ts
tests/charging-station/ui-server/ui-services/AbstractUIService.test.ts
ui/common/tests/WebSocketClient.test.ts
ui/common/tests/config.test.ts
ui/web/vitest.config.ts

index 774fc8706c6f82cd1ca044318bfac940ece39338..b974aed47b0d862dd8526a938cb1abae4b80f852 100644 (file)
@@ -1606,7 +1606,7 @@ export class ChargingStation extends EventEmitter {
     }
     return this.stationInfo?.reconnectExponentialDelay === true
       ? computeExponentialBackOffDelay({
-        baseDelayMs: 100,
+        baseDelayMs: Constants.DEFAULT_EXPONENTIAL_BACKOFF_BASE_DELAY_MS,
         jitterPercent: 0.2,
         retryNumber: this.wsConnectionRetryCount,
       })
@@ -1683,12 +1683,12 @@ export class ChargingStation extends EventEmitter {
       const powerArrayRandomIndex = Math.floor(secureRandom() * stationTemplate.power.length)
       stationInfo.maximumPower =
         stationTemplate.powerUnit === PowerUnits.KILO_WATT
-          ? stationTemplate.power[powerArrayRandomIndex] * 1000
+          ? stationTemplate.power[powerArrayRandomIndex] * Constants.UNIT_DIVIDER_KILO
           : stationTemplate.power[powerArrayRandomIndex]
     } else if (typeof stationTemplate.power === 'number') {
       stationInfo.maximumPower =
         stationTemplate.powerUnit === PowerUnits.KILO_WATT
-          ? stationTemplate.power * 1000
+          ? stationTemplate.power * Constants.UNIT_DIVIDER_KILO
           : stationTemplate.power
     }
     stationInfo.maximumAmperage = this.getMaximumAmperage(stationInfo)
@@ -1954,7 +1954,7 @@ export class ChargingStation extends EventEmitter {
         'hex'
       )
       const connectorsConfigChanged =
-        this.connectors.size !== 0 && this.connectorsConfigurationHash !== connectorsConfigHash
+        !isEmpty(this.connectors) && this.connectorsConfigurationHash !== connectorsConfigHash
       if (isEmpty(this.connectors) || connectorsConfigChanged) {
         connectorsConfigChanged && this.connectors.clear()
         this.connectorsConfigurationHash = connectorsConfigHash
@@ -2118,7 +2118,7 @@ export class ChargingStation extends EventEmitter {
         'hex'
       )
       const evsesConfigChanged =
-        this.evses.size !== 0 && this.evsesConfigurationHash !== evsesConfigHash
+        !isEmpty(this.evses) && this.evsesConfigurationHash !== evsesConfigHash
       if (isEmpty(this.evses) || evsesConfigChanged) {
         evsesConfigChanged && this.evses.clear()
         this.evsesConfigurationHash = evsesConfigHash
@@ -2786,7 +2786,7 @@ export class ChargingStation extends EventEmitter {
         // eslint-disable-next-line promise/no-promise-in-callback
         sleep(
           computeExponentialBackOffDelay({
-            baseDelayMs: 100,
+            baseDelayMs: Constants.DEFAULT_EXPONENTIAL_BACKOFF_BASE_DELAY_MS,
             jitterPercent: 0.2,
             retryNumber: messageIdx ?? 0,
           })
index 5986a0233eec62a7d9adf654ca2011d65259ffcd..5cdcd8ec37099cc2679f66bdce50879b39a32cfb 100644 (file)
@@ -13,7 +13,7 @@
 import type { ChargingStation } from './ChargingStation.js'
 
 import { BaseError } from '../exception/index.js'
-import { logger } from '../utils/index.js'
+import { isEmpty, logger } from '../utils/index.js'
 import { getEvProfilesFile } from './Helpers.js'
 import { disposeCoherentSessionRuntime } from './meter-values/CoherentSampleComputer.js'
 import {
@@ -154,7 +154,7 @@ export class CoherentMeterValuesManager {
     if (this.chargingStation.stationInfo?.coherentMeterValues !== true) {
       return undefined
     }
-    if (this.evProfiles == null || this.evProfiles.profiles.length === 0) {
+    if (this.evProfiles == null || isEmpty(this.evProfiles.profiles)) {
       return undefined
     }
     const session = createCoherentSession(this.chargingStation, {
index 409422a27b868a48caf171087380efbdf44a17a7..12a4d13ba019aa79e7ae27a8aa7f02ee293a4de3 100644 (file)
@@ -36,7 +36,7 @@ import {
   PowerUnits,
   Voltage,
 } from '../types/index.js'
-import { clone, isEmpty, isNotEmptyArray, logger, secureRandom } from '../utils/index.js'
+import { clone, Constants, isEmpty, isNotEmptyArray, logger, secureRandom } from '../utils/index.js'
 
 const moduleName = 'HelpersConfig'
 
@@ -170,12 +170,12 @@ export const getDefaultConnectorMaximumPower = (
     const powerArrayRandomIndex = Math.floor(secureRandom() * stationTemplate.power.length)
     maximumPower =
       stationTemplate.powerUnit === PowerUnits.KILO_WATT
-        ? stationTemplate.power[powerArrayRandomIndex] * 1000
+        ? stationTemplate.power[powerArrayRandomIndex] * Constants.UNIT_DIVIDER_KILO
         : stationTemplate.power[powerArrayRandomIndex]
   } else if (typeof stationTemplate.power === 'number') {
     maximumPower =
       stationTemplate.powerUnit === PowerUnits.KILO_WATT
-        ? stationTemplate.power * 1000
+        ? stationTemplate.power * Constants.UNIT_DIVIDER_KILO
         : stationTemplate.power
   }
   if (maximumPower == null) {
index 2a0a7c42dd853f9ad79704610fab4909d33dd232..7f7052290c2e51d27af29daa71acb05bc3531b66 100644 (file)
@@ -35,7 +35,7 @@ import {
   MeterValuePhase,
   MeterValueUnit,
 } from '../../types/index.js'
-import { Constants, isNotEmptyArray, logger, roundTo } from '../../utils/index.js'
+import { Constants, isEmpty, isNotEmptyArray, logger, roundTo } from '../../utils/index.js'
 import {
   advanceEnergyRegister,
   computeCoherentSample,
@@ -171,7 +171,7 @@ const applyRegisterValuesWithoutPhases = (
   const surviving: SampledValueTemplate[] = []
   for (const family of Map.groupBy(bucket, templateFamilyKey).values()) {
     const perPhaseLN = family.filter(isLineToNeutralTemplate)
-    if (perPhaseLN.length === 0) {
+    if (isEmpty(perPhaseLN)) {
       surviving.push(...family)
       continue
     }
index 1f3be68b60fdc7323f2830daffec419f6a4f3677..e8a77dc054f396a446bec5bb7ea0bc9e0006f71c 100644 (file)
@@ -19,7 +19,7 @@
 import type { CoherentSession, EvProfile, ICoherentContext } from './types.js'
 
 import { CurrentType, Voltage } from '../../types/index.js'
-import { Constants, logger } from '../../utils/index.js'
+import { Constants, isEmpty, logger } from '../../utils/index.js'
 import { selectEvProfile } from './EvProfiles.js'
 import { createStreamPrng, hashLabel } from './PRNG.js'
 
@@ -83,7 +83,7 @@ export const createCoherentSession = (
   context: ICoherentContext,
   options: CreateSessionOptions
 ): CoherentSession | undefined => {
-  if (options.profiles.length === 0) {
+  if (isEmpty(options.profiles)) {
     return undefined
   }
   const now = options.now ?? Date.now()
index 33325baaa6f32edc02a002c65cdb7f41d6a4c253..a7e4135dffa38f65072a0ad479d5550227d5a134 100644 (file)
@@ -12,7 +12,7 @@ import { readFileSync } from 'node:fs'
 import { z } from 'zod'
 
 import { BaseError } from '../../exception/index.js'
-import { getErrorMessage, logger } from '../../utils/index.js'
+import { getErrorMessage, isEmpty, logger } from '../../utils/index.js'
 import { type EvProfile, type EvProfilesFile, EvProfilesFileSchema } from './types.js'
 
 const moduleName = 'EvProfiles'
@@ -106,7 +106,7 @@ export const interpolateChargingCurve = (
   curve: { powerFraction: number; socPercent: number }[],
   socPercent: number
 ): number => {
-  if (curve.length === 0) {
+  if (isEmpty(curve)) {
     return 1
   }
   if (process.env.NODE_ENV !== 'production') {
index 72702640e51888b396db3674f01914e776efbc7c..ac66e21327d4f8ebcbdacf15549f5e8455817ee6 100644 (file)
@@ -474,7 +474,7 @@ export class OCPP20CertificateManager {
       const pemCertificates = pem.match(
         /-----BEGIN CERTIFICATE-----[\s\S]*?-----END CERTIFICATE-----/g
       )
-      if (pemCertificates == null || pemCertificates.length === 0) {
+      if (pemCertificates == null || isEmpty(pemCertificates)) {
         return { reason: 'No PEM certificate found', valid: false }
       }
 
index 3298f9c457d188462b1dfd697559177c66ae49ab..c49e8602a1f6f5f46cd6b88af44c9dca36091b4d 100644 (file)
@@ -191,6 +191,13 @@ import { getVariableMetadata, VARIABLE_REGISTRY } from './OCPP20VariableRegistry
 
 const moduleName = 'OCPP20IncomingRequestService'
 
+// GetBaseReport response chunking: conservative project default.
+// The actual per-device limits are reported at runtime by the Charging Station via
+// OCPP 2.0.1 §2.1.16 DeviceDataCtrlr.ItemsPerMessage(instance:GetReport) and
+// §2.1.18 DeviceDataCtrlr.BytesPerMessage(instance:GetReport); both are ReadOnly
+// device capability declarations with no spec-defined ceiling.
+const MAX_ITEMS_PER_REPORT_MESSAGE = 100 as const
+
 interface StationInfoReportField {
   property: 'chargePointModel' | 'chargePointSerialNumber' | 'chargePointVendor' | 'firmwareVersion'
   variable: OCPP20DeviceInfoVariableName
@@ -1340,7 +1347,7 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService {
         meterValues.push(meterValue)
       }
     }
-    if (meterValues.length === 0) {
+    if (isEmpty(meterValues)) {
       meterValues.push({
         sampledValue: [
           {
@@ -3512,10 +3519,9 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService {
     const cached = stationState.reportDataCache.get(requestId)
     const reportData = cached ?? this.buildReportData(chargingStation, reportBase)
 
-    const maxItemsPerMessage = 100
     const chunks = []
-    for (let i = 0; i < reportData.length; i += maxItemsPerMessage) {
-      chunks.push(reportData.slice(i, i + maxItemsPerMessage))
+    for (let i = 0; i < reportData.length; i += MAX_ITEMS_PER_REPORT_MESSAGE) {
+      chunks.push(reportData.slice(i, i + MAX_ITEMS_PER_REPORT_MESSAGE))
     }
 
     if (!isNotEmptyArray(chunks)) {
index 4e88b242ce4cc0f63b7378513e4120226c3f95f6..fc37e9b5ba48511c5b425838b0865bb9e9efbb55 100644 (file)
@@ -1110,6 +1110,9 @@ const resolveEnabledMeasurands = (
   const enabled = new Set<MeterValueMeasurand>()
   for (const entry of rawValue.split(',')) {
     const trimmed = entry.trim()
+    // Kept as `.length === 0`: entry is already trimmed above; `isEmpty()` here would be
+    // semantically identical (its string branch is `value.trim().length === 0`) — direct
+    // check avoids a redundant re-trim.
     if (trimmed.length === 0) {
       continue
     }
@@ -1486,6 +1489,8 @@ const getLimitFromSampledValueTemplateCustomValue = (
     },
     ...options,
   }
+  // Number.parseFloat preserved: NaN sentinel drives the POSITIVE_INFINITY fallback below;
+  // convertToFloat throws on NaN and would break the fallback branch.
   const parsedValue = Number.parseFloat(value ?? '')
   if (options.limitationEnabled) {
     return max(
index ca4a5c5b3a803efb9515ccbd2e3f8f79d0202fec..692e2f9a201e1bbb7cfbbfc1001dd7446236347d 100644 (file)
@@ -8,6 +8,9 @@ import { AuthorizationStatus } from '../types/AuthTypes.js'
 
 const moduleName = 'InMemoryAuthCache'
 
+// Rough heap-footprint estimate per cached entry — not measured; used only for stats display.
+const AVG_ENTRY_SIZE_BYTES = 500 as const
+
 /**
  * Cached authorization entry with expiration
  */
@@ -236,8 +239,7 @@ export class InMemoryAuthCache implements AuthCache {
     const hitRate = totalAccess > 0 ? (this.stats.hits / totalAccess) * 100 : 0
 
     // Calculate memory usage estimate
-    const avgEntrySize = 500 // Rough estimate: 500 bytes per entry
-    const memoryUsage = this.cache.size * avgEntrySize
+    const memoryUsage = this.cache.size * AVG_ENTRY_SIZE_BYTES
 
     // Clean expired rate limit entries
     this.cleanupExpiredRateLimits()
index 9c2739b7a3491393723e4807466cc824e770c8fd..a9b3d488433dc41a8604cd654b7ab9e49b2e2af3 100644 (file)
@@ -599,7 +599,7 @@ export class OCPPAuthServiceImpl implements OCPPAuthService {
       allowOfflineTxForUnknownId: false,
       authKeyManagementEnabled: false,
       authorizationCacheEnabled: true,
-      authorizationCacheLifetime: 3600,
+      authorizationCacheLifetime: Constants.DEFAULT_AUTH_CACHE_TTL_SECONDS,
       authorizationTimeout: 30,
       certificateAuthEnabled:
         this.chargingStation.stationInfo?.ocppVersion !== OCPPVersion.VERSION_16,
index 2ac68868637fb0bc89e2db87a8dbb523539ea0bf..3c3d667c70818b8ca630269972b97ce428d77824 100644 (file)
@@ -28,6 +28,7 @@ import {
   getErrorMessage,
   isEmpty,
   isNotEmptyArray,
+  JSONStringify,
   logger,
 } from '../../utils/index.js'
 import { AbstractUIServer } from './AbstractUIServer.js'
@@ -82,7 +83,7 @@ export class UIMCPServer extends AbstractUIServer {
   }
 
   private static createToolResponse (payload: unknown): CallToolResult {
-    return { content: [{ text: JSON.stringify(payload), type: 'text' as const }] }
+    return { content: [{ text: JSONStringify(payload), type: 'text' as const }] }
   }
 
   public override hasResponseHandler (uuid: UUIDv4): boolean {
index 9d4c403c01dfff99da62c6cd82fc1b8be5f393ec..d493c01454b481a89cf5c19cc974404affc1aa13 100644 (file)
@@ -152,6 +152,7 @@ const parseIPv4MappedAddress = (address: string): string | undefined => {
   if (hexMatch == null) {
     return undefined
   }
+  // radix-16 required for IPv4-mapped IPv6 hex octet parsing; convertToInt does not accept a radix
   const high = Number.parseInt(hexMatch[1], 16)
   const low = Number.parseInt(hexMatch[2], 16)
   return [high >> 8, high & 0xff, low >> 8, low & 0xff].join('.')
@@ -172,6 +173,7 @@ const expandIPv6 = (address: string): string[] | undefined => {
   if (groups.length !== 8) {
     return undefined
   }
+  // radix-16 required for IPv6 hex group normalization; convertToInt does not accept a radix
   return groups.every(isIPv6Group)
     ? groups.map(group => Number.parseInt(group, 16).toString(16))
     : undefined
index 1daa6ac7f192a69a4ad32e00240c02dc88be156c..c242c9ecacc1148d11f9ba238ec635bb3a2116bb 100644 (file)
@@ -90,9 +90,7 @@ const defaultWorkerConfiguration: WorkerConfiguration = {
   startDelay: DEFAULT_WORKER_START_DELAY_MS,
 }
 
-const defaultPersistState = true
-
-export const DEFAULT_PERSIST_STATE = defaultPersistState
+export const DEFAULT_PERSIST_STATE = true as const
 
 // eslint-disable-next-line @typescript-eslint/no-extraneous-class
 export class Configuration {
@@ -195,7 +193,7 @@ export class Configuration {
   }
 
   public static getPersistState (): boolean {
-    return Configuration.getConfigurationData()?.persistState ?? defaultPersistState
+    return Configuration.getConfigurationData()?.persistState ?? DEFAULT_PERSIST_STATE
   }
 
   public static getStationTemplateUrls (): StationTemplateUrl[] | undefined {
index a58ab40c44c189ced05bd64cb109411916d3b14f..9cd36e789a6c785b1a454d44bd3de875e01981cf 100644 (file)
@@ -14,7 +14,7 @@ import {
 } from './ConfigurationMigrations.js'
 import { ConfigurationSchema } from './ConfigurationSchema.js'
 import { configurationLogPrefix } from './ConfigurationUtils.js'
-import { assertIsJsonObject, clone, isEmpty } from './Utils.js'
+import { assertIsJsonObject, clone, isEmpty, isNotEmptyArray } from './Utils.js'
 
 const moduleName = 'ConfigurationValidation'
 
@@ -121,7 +121,7 @@ export const validateConfiguration = (parsed: unknown, filePath: string): Config
       )}`
     )
   }
-  if (remapErrors.length > 0) {
+  if (isNotEmptyArray(remapErrors)) {
     throw new ConfigurationValidationError(remapErrors, {
       filePath,
       migratedFrom,
index b6a622421aa16c03d2b1e9b191c6076e15f53f65..5cd0489935ee93d54de4bf06719fbafac67fdd15 100644 (file)
@@ -44,6 +44,8 @@ export class Constants {
 
   static readonly DEFAULT_EV_CONNECTION_TIMEOUT_SECONDS = 180
 
+  static readonly DEFAULT_EXPONENTIAL_BACKOFF_BASE_DELAY_MS = 100
+
   static readonly DEFAULT_FLUCTUATION_PERCENT = 5
 
   static readonly DEFAULT_HASH_ALGORITHM = 'sha384'
index 575d331ea12ac0d9facd1e5ccb51c24e3b75cd99..45dc3f59656cd087f309c52686009077198155ff 100644 (file)
@@ -2,7 +2,7 @@
  * @file Shared template fixtures for schema/migration/validation tests.
  */
 
-import { CURRENT_SCHEMA_VERSION } from '../../../src/charging-station/TemplateMigrations.js'
+import { CURRENT_SCHEMA_VERSION } from '../../../src/charging-station/index.js'
 import {
   TEST_CHARGE_POINT_MODEL,
   TEST_CHARGE_POINT_VENDOR,
index 9884d90a9cc1967c140f7096e7f65ce51567b71a..6861983b8feaeaf542b64a84bace56c227d9ef13 100644 (file)
@@ -8,7 +8,7 @@ import { afterEach, beforeEach, describe, it, mock } from 'node:test'
 
 import type { ChargingStation } from '../../../../src/charging-station/index.js'
 
-import { buildConfigKey } from '../../../../src/charging-station/ConfigurationKeyUtils.js'
+import { buildConfigKey } from '../../../../src/charging-station/index.js'
 import { OCPP20CertSigningRetryManager } from '../../../../src/charging-station/ocpp/2.0/OCPP20CertSigningRetryManager.js'
 import { OCPP20VariableManager } from '../../../../src/charging-station/ocpp/2.0/OCPP20VariableManager.js'
 import {
index f8f1054cb181e07abc61c9c95b80a9c7501cd7f5..d713c48a19bb4d0926db82bbbe72b5cc4b68b60f 100644 (file)
@@ -12,7 +12,7 @@ import {
   addConfigurationKey,
   buildConfigKey,
   setConfigurationKeyValue,
-} from '../../../../src/charging-station/ConfigurationKeyUtils.js'
+} from '../../../../src/charging-station/index.js'
 import { createTestableIncomingRequestService } from '../../../../src/charging-station/ocpp/2.0/__testable__/index.js'
 import { OCPP20IncomingRequestService } from '../../../../src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.js'
 import { OCPP20VariableManager } from '../../../../src/charging-station/ocpp/2.0/OCPP20VariableManager.js'
index 297a27fee8e96d73914eed9bd1e74ac99ae4e25e..9eed14c42eb4498b6c9a1b3d5892ccd4b67aad9a 100644 (file)
@@ -14,10 +14,9 @@ import assert from 'node:assert/strict'
 import { afterEach, beforeEach, describe, it, mock } from 'node:test'
 
 import type { ChargingStation } from '../../../../src/charging-station/index.js'
-import type { ConnectorStatus } from '../../../../src/types/ConnectorStatus.js'
-import type { EmptyObject } from '../../../../src/types/index.js'
+import type { ConnectorStatus, EmptyObject } from '../../../../src/types/index.js'
 
-import { addConfigurationKey } from '../../../../src/charging-station/ConfigurationKeyUtils.js'
+import { addConfigurationKey } from '../../../../src/charging-station/index.js'
 import {
   buildTransactionEvent,
   OCPP20ServiceUtils,
index 5878592150b0602eef0188d5b886ea5fb36cae8b..59ee7b52f1787b63ad009b793ed94d58f3de39ed 100644 (file)
@@ -13,7 +13,7 @@ import {
   buildConfigKey,
   deleteConfigurationKey,
   getConfigurationKey,
-} from '../../../../src/charging-station/ConfigurationKeyUtils.js'
+} from '../../../../src/charging-station/index.js'
 import { createTestableVariableManager } from '../../../../src/charging-station/ocpp/2.0/__testable__/index.js'
 import { OCPP20VariableManager } from '../../../../src/charging-station/ocpp/2.0/OCPP20VariableManager.js'
 import { VARIABLE_REGISTRY } from '../../../../src/charging-station/ocpp/2.0/OCPP20VariableRegistry.js'
index 881dec9c822d952b07602d36b4440c6880160194..7741526d6cbb41010e61ab87219de171ac664ebb 100644 (file)
@@ -15,7 +15,7 @@ import { afterEach, beforeEach, describe, it } from 'node:test'
 import { UIMCPServer } from '../../../src/charging-station/ui-server/UIMCPServer.js'
 import { HttpMethod } from '../../../src/charging-station/ui-server/UIServerUtils.js'
 import { ApplicationProtocol, ConfigurationSection } from '../../../src/types/index.js'
-import { Configuration } from '../../../src/utils/Configuration.js'
+import { Configuration } from '../../../src/utils/index.js'
 import { standardCleanup } from '../../helpers/TestLifecycleHelpers.js'
 import { createMockBootstrap, createMockUIServerConfiguration } from './UIServerTestUtils.js'
 
index bc3adb8cd148c3bdcb7e974eb489a87fab5d86cc..bc5493fa7a949d768724d86436835104e8bca54b 100644 (file)
@@ -38,7 +38,7 @@ import {
   ProcedureName,
   ResponseStatus,
 } from '../../../src/types/index.js'
-import { logger } from '../../../src/utils/Logger.js'
+import { logger } from '../../../src/utils/index.js'
 import {
   createLoggerMocks,
   standardCleanup,
index bdb27fca9138cfb5eeb3b961218bc04a55c77ba8..ae9b7c361ee6dfe3fd89cfeefdfa0f4725dac815 100644 (file)
@@ -25,7 +25,7 @@ import {
   ProcedureName,
   ResponseStatus,
 } from '../../../src/types/index.js'
-import { logger } from '../../../src/utils/Logger.js'
+import { logger } from '../../../src/utils/index.js'
 import { createLoggerMocks, standardCleanup } from '../../helpers/TestLifecycleHelpers.js'
 import { TEST_UUID } from './UIServerTestConstants.js'
 import {
index 0fd3a017dd9f81ab1cfe9b1a6c955164866493b3..a1f9966050e2ccf410ebc090fc59bccbf8b19c30 100644 (file)
@@ -16,7 +16,7 @@ import {
   ResponseStatus,
   UIRequestOrigin,
 } from '../../../../src/types/index.js'
-import { logger } from '../../../../src/utils/Logger.js'
+import { logger } from '../../../../src/utils/index.js'
 import { standardCleanup } from '../../../helpers/TestLifecycleHelpers.js'
 import { TEST_HASH_ID, TEST_UUID } from '../UIServerTestConstants.js'
 import {
index e9d9bc04101e9b97a5f8d65a58b4e8633bfbadbe..12c63bbf8d037fc7516cbe1f9835639049884d29 100644 (file)
@@ -7,6 +7,7 @@ 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 { DEFAULT_HOST, DEFAULT_PORT } from '../src/constants.js'
 import {
   AuthenticationType,
   ProcedureName,
@@ -21,8 +22,8 @@ await describe('WebSocketClient', async () => {
     const mockWs = createMockWebSocketLike()
     const factory: WebSocketFactory = () => mockWs
     const client = new WebSocketClient(factory, {
-      host: 'localhost',
-      port: 8080,
+      host: DEFAULT_HOST,
+      port: DEFAULT_PORT,
       protocol: Protocol.UI,
       version: ProtocolVersion['0.0.1'],
     })
@@ -45,8 +46,8 @@ await describe('WebSocketClient', async () => {
         type: AuthenticationType.PROTOCOL_BASIC_AUTH,
         username: 'admin',
       },
-      host: 'localhost',
-      port: 8080,
+      host: DEFAULT_HOST,
+      port: DEFAULT_PORT,
       protocol: Protocol.UI,
       version: ProtocolVersion['0.0.1'],
     })
@@ -62,8 +63,8 @@ await describe('WebSocketClient', async () => {
     const mockWs = createMockWebSocketLike()
     const factory: WebSocketFactory = () => mockWs
     const client = new WebSocketClient(factory, {
-      host: 'localhost',
-      port: 8080,
+      host: DEFAULT_HOST,
+      port: DEFAULT_PORT,
       protocol: Protocol.UI,
       version: ProtocolVersion['0.0.1'],
     })
@@ -90,8 +91,8 @@ await describe('WebSocketClient', async () => {
     const mockWs = createMockWebSocketLike()
     const factory: WebSocketFactory = () => mockWs
     const client = new WebSocketClient(factory, {
-      host: 'localhost',
-      port: 8080,
+      host: DEFAULT_HOST,
+      port: DEFAULT_PORT,
       protocol: Protocol.UI,
       version: ProtocolVersion['0.0.1'],
     })
@@ -120,8 +121,8 @@ await describe('WebSocketClient', async () => {
     const mockWs = createMockWebSocketLike()
     const factory: WebSocketFactory = () => mockWs
     const client = new WebSocketClient(factory, {
-      host: 'localhost',
-      port: 8080,
+      host: DEFAULT_HOST,
+      port: DEFAULT_PORT,
       protocol: Protocol.UI,
       version: ProtocolVersion['0.0.1'],
     })
@@ -164,8 +165,8 @@ await describe('WebSocketClient', async () => {
     const mockWs = createMockWebSocketLike()
     const factory: WebSocketFactory = () => mockWs
     const client = new WebSocketClient(factory, {
-      host: 'localhost',
-      port: 8080,
+      host: DEFAULT_HOST,
+      port: DEFAULT_PORT,
       protocol: Protocol.UI,
       version: ProtocolVersion['0.0.1'],
     })
@@ -183,8 +184,8 @@ await describe('WebSocketClient', async () => {
     const mockWs = createMockWebSocketLike()
     const factory: WebSocketFactory = () => mockWs
     const client = new WebSocketClient(factory, {
-      host: 'localhost',
-      port: 8080,
+      host: DEFAULT_HOST,
+      port: DEFAULT_PORT,
       protocol: Protocol.UI,
       version: ProtocolVersion['0.0.1'],
     })
@@ -203,8 +204,8 @@ await describe('WebSocketClient', async () => {
     const mockWs = createMockWebSocketLike()
     const factory: WebSocketFactory = () => mockWs
     const client = new WebSocketClient(factory, {
-      host: 'localhost',
-      port: 8080,
+      host: DEFAULT_HOST,
+      port: DEFAULT_PORT,
       protocol: Protocol.UI,
       version: ProtocolVersion['0.0.1'],
     })
@@ -245,8 +246,8 @@ await describe('WebSocketClient', async () => {
     const mockWs = createMockWebSocketLike()
     const factory: WebSocketFactory = () => mockWs
     const client = new WebSocketClient(factory, {
-      host: 'localhost',
-      port: 8080,
+      host: DEFAULT_HOST,
+      port: DEFAULT_PORT,
       protocol: Protocol.UI,
       version: ProtocolVersion['0.0.1'],
     })
@@ -264,8 +265,8 @@ await describe('WebSocketClient', async () => {
     const mockWs = createMockWebSocketLike()
     const factory: WebSocketFactory = () => mockWs
     const client = new WebSocketClient(factory, {
-      host: 'localhost',
-      port: 8080,
+      host: DEFAULT_HOST,
+      port: DEFAULT_PORT,
       protocol: Protocol.UI,
       version: ProtocolVersion['0.0.1'],
     })
@@ -286,8 +287,8 @@ await describe('WebSocketClient', async () => {
     const mockWs = createMockWebSocketLike()
     const factory: WebSocketFactory = () => mockWs
     const client = new WebSocketClient(factory, {
-      host: 'localhost',
-      port: 8080,
+      host: DEFAULT_HOST,
+      port: DEFAULT_PORT,
       protocol: Protocol.UI,
       version: ProtocolVersion['0.0.1'],
     })
@@ -306,8 +307,8 @@ await describe('WebSocketClient', async () => {
     const mockWs = createMockWebSocketLike()
     const factory: WebSocketFactory = () => mockWs
     const client = new WebSocketClient(factory, {
-      host: 'localhost',
-      port: 8080,
+      host: DEFAULT_HOST,
+      port: DEFAULT_PORT,
       protocol: Protocol.UI,
       version: ProtocolVersion['0.0.1'],
     })
@@ -340,8 +341,8 @@ await describe('WebSocketClient', async () => {
     const client = new WebSocketClient(
       () => mockWs,
       {
-        host: 'localhost',
-        port: 8080,
+        host: DEFAULT_HOST,
+        port: DEFAULT_PORT,
         protocol: Protocol.UI,
         version: ProtocolVersion['0.0.1'],
       },
@@ -369,8 +370,8 @@ await describe('WebSocketClient', async () => {
     const client = new WebSocketClient(
       () => mockWs,
       {
-        host: 'localhost',
-        port: 8080,
+        host: DEFAULT_HOST,
+        port: DEFAULT_PORT,
         protocol: Protocol.UI,
         version: ProtocolVersion['0.0.1'],
       },
@@ -397,8 +398,8 @@ await describe('WebSocketClient', async () => {
     const mockWs = createMockWebSocketLike()
     const factory: WebSocketFactory = () => mockWs
     const client = new WebSocketClient(factory, {
-      host: 'localhost',
-      port: 8080,
+      host: DEFAULT_HOST,
+      port: DEFAULT_PORT,
       protocol: Protocol.UI,
       version: ProtocolVersion['0.0.1'],
     })
@@ -421,8 +422,8 @@ await describe('WebSocketClient', async () => {
     const mockWs = createMockWebSocketLike()
     const factory: WebSocketFactory = () => mockWs
     const client = new WebSocketClient(factory, {
-      host: 'localhost',
-      port: 8080,
+      host: DEFAULT_HOST,
+      port: DEFAULT_PORT,
       protocol: Protocol.UI,
       version: ProtocolVersion['0.0.1'],
     })
@@ -445,8 +446,8 @@ await describe('WebSocketClient', async () => {
     const mockWs = createMockWebSocketLike()
     const factory: WebSocketFactory = () => mockWs
     const client = new WebSocketClient(factory, {
-      host: 'localhost',
-      port: 8080,
+      host: DEFAULT_HOST,
+      port: DEFAULT_PORT,
       protocol: Protocol.UI,
       version: ProtocolVersion['0.0.1'],
     })
@@ -469,7 +470,12 @@ await describe('WebSocketClient', async () => {
     const mockWs = createMockWebSocketLike()
     const client = new WebSocketClient(
       () => mockWs,
-      { host: 'localhost', port: 8080, protocol: Protocol.UI, version: ProtocolVersion['0.0.1'] },
+      {
+        host: DEFAULT_HOST,
+        port: DEFAULT_PORT,
+        protocol: Protocol.UI,
+        version: ProtocolVersion['0.0.1'],
+      },
       undefined,
       notification => {
         notifications.push(notification)
@@ -492,7 +498,12 @@ await describe('WebSocketClient', async () => {
     const factory: WebSocketFactory = () => mockWs
     const client = new WebSocketClient(
       factory,
-      { host: 'localhost', port: 8080, protocol: Protocol.UI, version: ProtocolVersion['0.0.1'] },
+      {
+        host: DEFAULT_HOST,
+        port: DEFAULT_PORT,
+        protocol: Protocol.UI,
+        version: ProtocolVersion['0.0.1'],
+      },
       undefined,
       notification => {
         notifications.push(notification)
@@ -514,8 +525,8 @@ await describe('WebSocketClient', async () => {
   await it('should NOT fire onNotification when callback is undefined', async () => {
     const mockWs = createMockWebSocketLike()
     const client = new WebSocketClient(() => mockWs, {
-      host: 'localhost',
-      port: 8080,
+      host: DEFAULT_HOST,
+      port: DEFAULT_PORT,
       protocol: Protocol.UI,
       version: ProtocolVersion['0.0.1'],
     })
index 32d0b681db0a348c83e5d2638b4b68350239302e..3cc5128700eb51f9ef6e15777150f9e2de072160 100644 (file)
@@ -2,12 +2,13 @@ import assert from 'node:assert'
 import { describe, it } from 'node:test'
 
 import { configurationSchema, uiServerConfigSchema } from '../src/config/schema.js'
+import { DEFAULT_HOST, DEFAULT_PORT } from '../src/constants.js'
 
 await describe('config schema validation', async () => {
   await it('should validate a minimal valid config', () => {
     const result = uiServerConfigSchema.safeParse({
-      host: 'localhost',
-      port: 8080,
+      host: DEFAULT_HOST,
+      port: DEFAULT_PORT,
       protocol: 'ui',
       version: '0.0.1',
     })
@@ -16,8 +17,8 @@ await describe('config schema validation', async () => {
 
   await it('should reject config with empty protocol', () => {
     const result = uiServerConfigSchema.safeParse({
-      host: 'localhost',
-      port: 8080,
+      host: DEFAULT_HOST,
+      port: DEFAULT_PORT,
       protocol: '',
       version: '0.0.1',
     })
@@ -26,8 +27,8 @@ await describe('config schema validation', async () => {
 
   await it('should reject config with protocol not in Protocol enum', () => {
     const result = uiServerConfigSchema.safeParse({
-      host: 'localhost',
-      port: 8080,
+      host: DEFAULT_HOST,
+      port: DEFAULT_PORT,
       protocol: 'ws',
       version: '0.0.1',
     })
@@ -36,8 +37,8 @@ await describe('config schema validation', async () => {
 
   await it('should reject config with version not in ProtocolVersion enum', () => {
     const result = uiServerConfigSchema.safeParse({
-      host: 'localhost',
-      port: 8080,
+      host: DEFAULT_HOST,
+      port: DEFAULT_PORT,
       protocol: 'ui',
       version: '2.0',
     })
@@ -46,7 +47,7 @@ await describe('config schema validation', async () => {
 
   await it('should reject missing required host field', () => {
     const result = uiServerConfigSchema.safeParse({
-      port: 8080,
+      port: DEFAULT_PORT,
       protocol: 'ui',
       version: '0.0.1',
     })
@@ -55,7 +56,7 @@ await describe('config schema validation', async () => {
 
   await it('should reject invalid port number', () => {
     const result = uiServerConfigSchema.safeParse({
-      host: 'localhost',
+      host: DEFAULT_HOST,
       port: 99999,
     })
     assert.strictEqual(result.success, false)
@@ -64,7 +65,7 @@ await describe('config schema validation', async () => {
   await it('should reject empty host string', () => {
     const result = uiServerConfigSchema.safeParse({
       host: '',
-      port: 8080,
+      port: DEFAULT_PORT,
       protocol: 'ui',
       version: '0.0.1',
     })
@@ -81,7 +82,7 @@ await describe('config schema validation', async () => {
       },
       host: 'simulator.example.com',
       name: 'My Simulator',
-      port: 8080,
+      port: DEFAULT_PORT,
       protocol: 'ui',
       secure: true,
       version: '0.0.1',
@@ -92,8 +93,8 @@ await describe('config schema validation', async () => {
   await it('should validate configuration with array of servers', () => {
     const result = configurationSchema.safeParse({
       uiServer: [
-        { host: 'server1.example.com', port: 8080, protocol: 'ui', version: '0.0.1' },
-        { host: 'server2.example.com', port: 8080, protocol: 'ui', version: '0.0.1' },
+        { host: 'server1.example.com', port: DEFAULT_PORT, protocol: 'ui', version: '0.0.1' },
+        { host: 'server2.example.com', port: DEFAULT_PORT, protocol: 'ui', version: '0.0.1' },
       ],
     })
     assert.strictEqual(result.success, true)
@@ -101,7 +102,7 @@ await describe('config schema validation', async () => {
 
   await it('should validate configuration with single server', () => {
     const result = configurationSchema.safeParse({
-      uiServer: { host: 'localhost', port: 8080, protocol: 'ui', version: '0.0.1' },
+      uiServer: { host: DEFAULT_HOST, port: DEFAULT_PORT, protocol: 'ui', version: '0.0.1' },
     })
     assert.strictEqual(result.success, true)
   })
@@ -112,8 +113,8 @@ await describe('config schema validation', async () => {
         enabled: true,
         type: 'protocol-basic-auth',
       },
-      host: 'localhost',
-      port: 8080,
+      host: DEFAULT_HOST,
+      port: DEFAULT_PORT,
       protocol: 'ui',
       version: '0.0.1',
     })
@@ -137,8 +138,8 @@ await describe('config schema validation', async () => {
         type: 'protocol-basic-auth',
         username: '',
       },
-      host: 'localhost',
-      port: 8080,
+      host: DEFAULT_HOST,
+      port: DEFAULT_PORT,
       protocol: 'ui',
       version: '0.0.1',
     })
@@ -158,8 +159,8 @@ await describe('config schema validation', async () => {
         type: 'protocol-basic-auth',
         username: 'admin',
       },
-      host: 'localhost',
-      port: 8080,
+      host: DEFAULT_HOST,
+      port: DEFAULT_PORT,
       protocol: 'ui',
       version: '0.0.1',
     })
@@ -179,8 +180,8 @@ await describe('config schema validation', async () => {
         type: 'protocol-basic-auth',
         username: 'a:b',
       },
-      host: 'localhost',
-      port: 8080,
+      host: DEFAULT_HOST,
+      port: DEFAULT_PORT,
       protocol: 'ui',
       version: '0.0.1',
     })
@@ -202,8 +203,8 @@ await describe('config schema validation', async () => {
         password: 'admin',
         type: 'protocol-basic-auth',
       },
-      host: 'localhost',
-      port: 8080,
+      host: DEFAULT_HOST,
+      port: DEFAULT_PORT,
       protocol: 'ui',
       version: '0.0.1',
     })
@@ -222,8 +223,8 @@ await describe('config schema validation', async () => {
         type: 'protocol-basic-auth',
         username: 'admin',
       },
-      host: 'localhost',
-      port: 8080,
+      host: DEFAULT_HOST,
+      port: DEFAULT_PORT,
       protocol: 'ui',
       version: '0.0.1',
     })
@@ -243,8 +244,8 @@ await describe('config schema validation', async () => {
         type: 'protocol-basic-auth',
         username: 'admin',
       },
-      host: 'localhost',
-      port: 8080,
+      host: DEFAULT_HOST,
+      port: DEFAULT_PORT,
       protocol: 'ui',
       version: '0.0.1',
     })
@@ -257,8 +258,8 @@ await describe('config schema validation', async () => {
         enabled: false,
         type: 'protocol-basic-auth',
       },
-      host: 'localhost',
-      port: 8080,
+      host: DEFAULT_HOST,
+      port: DEFAULT_PORT,
       protocol: 'ui',
       version: '0.0.1',
     })
index e4e241fe29f7c971a7ec7fb3fc46857013de31d0..f53905051ecb4ba18309d7b6c2b90166e5c85cad 100644 (file)
@@ -2,7 +2,7 @@ import { fileURLToPath } from 'node:url'
 import { mergeConfig } from 'vite'
 import { configDefaults, defineConfig } from 'vitest/config'
 
-import viteConfig from './vite.config'
+import viteConfig from './vite.config.js'
 
 const nodeMajor = Number.parseInt(process.versions.node.split('.')[0], 10)