From: Jérôme Benoit Date: Sat, 21 Mar 2026 18:01:48 +0000 (+0100) Subject: refactor(auth): replace adapter Map with single adapter per station X-Git-Tag: v3.2~15 X-Git-Url: https://git.piment-noir.org/?a=commitdiff_plain;h=7021e25c37ec7e337bc4222d062124247d75d882;p=e-mobility-charging-stations-simulator.git refactor(auth): replace adapter Map with single adapter per station Each charging station has exactly one OCPP version, making the Map unnecessary indirection. Simplify to a single adapter field across the auth module. - AuthComponentFactory: createAdapter() returns single OCPPAuthAdapter - RemoteAuthStrategy: setAdapter()/clearAdapter() replace map ops - CertificateAuthStrategy: readonly adapter field, no map - OCPPAuthServiceImpl: single adapter, initializeAdapter() singular - Tests updated to match new single-adapter API --- diff --git a/src/charging-station/ocpp/auth/factories/AuthComponentFactory.ts b/src/charging-station/ocpp/auth/factories/AuthComponentFactory.ts index d70d3685..8bb4dceb 100644 --- a/src/charging-station/ocpp/auth/factories/AuthComponentFactory.ts +++ b/src/charging-station/ocpp/auth/factories/AuthComponentFactory.ts @@ -1,10 +1,9 @@ import type { ChargingStation } from '../../../ChargingStation.js' -import type { OCPP16AuthAdapter } from '../adapters/OCPP16AuthAdapter.js' -import type { OCPP20AuthAdapter } from '../adapters/OCPP20AuthAdapter.js' import type { AuthCache, AuthStrategy, LocalAuthListManager, + OCPPAuthAdapter, } from '../interfaces/OCPPAuthService.js' import type { AuthConfiguration } from '../types/AuthTypes.js' @@ -32,15 +31,12 @@ import { AuthConfigValidator } from '../utils/ConfigValidator.js' // eslint-disable-next-line @typescript-eslint/no-extraneous-class export class AuthComponentFactory { /** - * Create OCPP adapters based on charging station version + * Create OCPP adapter based on charging station version * @param chargingStation - Charging station instance used to determine OCPP version - * @returns Object containing version-specific adapter (OCPP 1.6 or 2.0.x) + * @returns Single version-specific adapter (OCPP 1.6 or 2.0.x) * @throws {Error} When OCPP version is not found or unsupported */ - static async createAdapters (chargingStation: ChargingStation): Promise<{ - ocpp16Adapter?: OCPP16AuthAdapter - ocpp20Adapter?: OCPP20AuthAdapter - }> { + static async createAdapter (chargingStation: ChargingStation): Promise { const ocppVersion = chargingStation.stationInfo?.ocppVersion if (!ocppVersion) { @@ -51,13 +47,13 @@ export class AuthComponentFactory { case OCPPVersion.VERSION_16: { // Use static import - circular dependency is acceptable here const { OCPP16AuthAdapter } = await import('../adapters/OCPP16AuthAdapter.js') - return { ocpp16Adapter: new OCPP16AuthAdapter(chargingStation) } + return new OCPP16AuthAdapter(chargingStation) } case OCPPVersion.VERSION_20: case OCPPVersion.VERSION_201: { // Use static import - circular dependency is acceptable here const { OCPP20AuthAdapter } = await import('../adapters/OCPP20AuthAdapter.js') - return { ocpp20Adapter: new OCPP20AuthAdapter(chargingStation) } + return new OCPP20AuthAdapter(chargingStation) } default: throw new OCPPError( @@ -82,28 +78,18 @@ export class AuthComponentFactory { /** * Create certificate authentication strategy * @param chargingStation - Charging station instance for certificate validation - * @param adapters - Container holding OCPP version-specific adapters - * @param adapters.ocpp16Adapter - Optional OCPP 1.6 protocol adapter - * @param adapters.ocpp20Adapter - Optional OCPP 2.0.x protocol adapter + * @param adapter - OCPP version-specific adapter * @param config - Authentication configuration with certificate settings * @returns Initialized certificate-based authentication strategy */ static async createCertificateStrategy ( chargingStation: ChargingStation, - adapters: { ocpp16Adapter?: OCPP16AuthAdapter; ocpp20Adapter?: OCPP20AuthAdapter }, + adapter: OCPPAuthAdapter, config: AuthConfiguration ): Promise { // Use static import - circular dependency is acceptable here const { CertificateAuthStrategy } = await import('../strategies/CertificateAuthStrategy.js') - const adapterMap = new Map() - if (adapters.ocpp16Adapter) { - adapterMap.set(OCPPVersion.VERSION_16, adapters.ocpp16Adapter) - } - if (adapters.ocpp20Adapter) { - adapterMap.set(OCPPVersion.VERSION_20, adapters.ocpp20Adapter) - adapterMap.set(OCPPVersion.VERSION_201, adapters.ocpp20Adapter) - } - const strategy = new CertificateAuthStrategy(chargingStation, adapterMap) + const strategy = new CertificateAuthStrategy(chargingStation, adapter) strategy.initialize(config) return strategy } @@ -157,16 +143,14 @@ export class AuthComponentFactory { /** * Create remote authentication strategy - * @param adapters - Container holding OCPP version-specific adapters - * @param adapters.ocpp16Adapter - Optional OCPP 1.6 protocol adapter - * @param adapters.ocpp20Adapter - Optional OCPP 2.0.x protocol adapter + * @param adapter - OCPP version-specific adapter * @param cache - Authorization cache for storing remote auth results * @param config - Authentication configuration controlling remote auth behavior * @param localAuthListManager - Optional local auth list manager for R17 cache exclusion * @returns Remote strategy instance or undefined if remote auth disabled */ static async createRemoteStrategy ( - adapters: { ocpp16Adapter?: OCPP16AuthAdapter; ocpp20Adapter?: OCPP20AuthAdapter }, + adapter: OCPPAuthAdapter, cache: AuthCache | undefined, config: AuthConfiguration, localAuthListManager?: LocalAuthListManager @@ -177,15 +161,7 @@ export class AuthComponentFactory { // Use static import - circular dependency is acceptable here const { RemoteAuthStrategy } = await import('../strategies/RemoteAuthStrategy.js') - const adapterMap = new Map() - if (adapters.ocpp16Adapter) { - adapterMap.set(OCPPVersion.VERSION_16, adapters.ocpp16Adapter) - } - if (adapters.ocpp20Adapter) { - adapterMap.set(OCPPVersion.VERSION_20, adapters.ocpp20Adapter) - adapterMap.set(OCPPVersion.VERSION_201, adapters.ocpp20Adapter) - } - const strategy = new RemoteAuthStrategy(adapterMap, cache, localAuthListManager) + const strategy = new RemoteAuthStrategy(adapter, cache, localAuthListManager) strategy.initialize(config) return strategy } @@ -193,9 +169,7 @@ export class AuthComponentFactory { /** * Create all authentication strategies based on configuration * @param chargingStation - Charging station instance for strategy initialization - * @param adapters - Container holding OCPP version-specific adapters - * @param adapters.ocpp16Adapter - Optional OCPP 1.6 protocol adapter - * @param adapters.ocpp20Adapter - Optional OCPP 2.0.x protocol adapter + * @param adapter - OCPP version-specific adapter * @param manager - Local auth list manager for local strategy * @param cache - Authorization cache shared across strategies * @param config - Authentication configuration controlling strategy creation @@ -203,7 +177,7 @@ export class AuthComponentFactory { */ static async createStrategies ( chargingStation: ChargingStation, - adapters: { ocpp16Adapter?: OCPP16AuthAdapter; ocpp20Adapter?: OCPP20AuthAdapter }, + adapter: OCPPAuthAdapter, manager: LocalAuthListManager | undefined, cache: AuthCache | undefined, config: AuthConfiguration @@ -217,13 +191,13 @@ export class AuthComponentFactory { } // Add remote strategy if enabled - const remoteStrategy = await this.createRemoteStrategy(adapters, cache, config, manager) + const remoteStrategy = await this.createRemoteStrategy(adapter, cache, config, manager) if (remoteStrategy) { strategies.push(remoteStrategy) } // Always add certificate strategy - const certStrategy = await this.createCertificateStrategy(chargingStation, adapters, config) + const certStrategy = await this.createCertificateStrategy(chargingStation, adapter, config) strategies.push(certStrategy) // Sort by priority diff --git a/src/charging-station/ocpp/auth/services/OCPPAuthServiceImpl.ts b/src/charging-station/ocpp/auth/services/OCPPAuthServiceImpl.ts index 7e717a7c..f15ed43e 100644 --- a/src/charging-station/ocpp/auth/services/OCPPAuthServiceImpl.ts +++ b/src/charging-station/ocpp/auth/services/OCPPAuthServiceImpl.ts @@ -1,6 +1,5 @@ import type { OCPP20IdTokenInfoType } from '../../../../types/index.js' -import type { OCPP16AuthAdapter } from '../adapters/OCPP16AuthAdapter.js' -import type { OCPP20AuthAdapter } from '../adapters/OCPP20AuthAdapter.js' +import type { OCPPAuthAdapter } from '../interfaces/OCPPAuthService.js' import type { LocalAuthStrategy } from '../strategies/LocalAuthStrategy.js' import { OCPPError } from '../../../../exception/index.js' @@ -35,7 +34,7 @@ import { AuthConfigValidator } from '../utils/ConfigValidator.js' const moduleName = 'OCPPAuthServiceImpl' export class OCPPAuthServiceImpl implements OCPPAuthService { - private readonly adapters: Map + private adapter?: OCPPAuthAdapter private readonly chargingStation: ChargingStation private config: AuthConfiguration private readonly metrics: { @@ -56,7 +55,6 @@ export class OCPPAuthServiceImpl implements OCPPAuthService { constructor (chargingStation: ChargingStation) { this.chargingStation = chargingStation this.strategies = new Map() - this.adapters = new Map() this.strategyPriority = ['local', 'remote', 'certificate'] // Initialize metrics tracking @@ -75,7 +73,7 @@ export class OCPPAuthServiceImpl implements OCPPAuthService { // Initialize default configuration this.config = this.createDefaultConfiguration() - // Note: Adapters and strategies will be initialized async via initialize() + // Note: Adapter and strategies will be initialized async via initialize() } /** @@ -420,7 +418,7 @@ export class OCPPAuthServiceImpl implements OCPPAuthService { * Must be called after construction */ public async initialize (): Promise { - await this.initializeAdapters() + await this.initializeAdapter() await this.initializeStrategies() } @@ -515,14 +513,14 @@ export class OCPPAuthServiceImpl implements OCPPAuthService { return false } - // Check if any adapter reports remote availability - for (const adapter of this.adapters.values()) { + // Check if adapter reports remote availability + if (this.adapter) { try { - if (adapter.isRemoteAvailable()) { + if (this.adapter.isRemoteAvailable()) { return true } } catch { - // Continue checking other adapters + // Adapter unavailable } } @@ -669,19 +667,10 @@ export class OCPPAuthServiceImpl implements OCPPAuthService { } /** - * Initialize OCPP adapters using AuthComponentFactory + * Initialize OCPP adapter using AuthComponentFactory */ - private async initializeAdapters (): Promise { - const adapters = await AuthComponentFactory.createAdapters(this.chargingStation) - - if (adapters.ocpp16Adapter) { - this.adapters.set(OCPPVersion.VERSION_16, adapters.ocpp16Adapter) - } - - if (adapters.ocpp20Adapter) { - this.adapters.set(OCPPVersion.VERSION_20, adapters.ocpp20Adapter) - this.adapters.set(OCPPVersion.VERSION_201, adapters.ocpp20Adapter) - } + private async initializeAdapter (): Promise { + this.adapter = await AuthComponentFactory.createAdapter(this.chargingStation) } /** @@ -690,9 +679,9 @@ export class OCPPAuthServiceImpl implements OCPPAuthService { private async initializeStrategies (): Promise { const ocppVersion = this.chargingStation.stationInfo?.ocppVersion - // Get adapters for strategy creation with proper typing - const ocpp16Adapter = this.adapters.get(OCPPVersion.VERSION_16) as OCPP16AuthAdapter | undefined - const ocpp20Adapter = this.adapters.get(OCPPVersion.VERSION_20) as OCPP20AuthAdapter | undefined + if (this.adapter == null) { + throw new OCPPError(ErrorType.INTERNAL_ERROR, 'Adapter must be initialized before strategies') + } // Create auth cache for strategy injection const authCache = AuthComponentFactory.createAuthCache(this.config) @@ -700,7 +689,7 @@ export class OCPPAuthServiceImpl implements OCPPAuthService { // Create strategies using factory const strategies = await AuthComponentFactory.createStrategies( this.chargingStation, - { ocpp16Adapter, ocpp20Adapter }, + this.adapter, undefined, // manager - delegated to OCPPAuthServiceImpl authCache, this.config diff --git a/src/charging-station/ocpp/auth/strategies/CertificateAuthStrategy.ts b/src/charging-station/ocpp/auth/strategies/CertificateAuthStrategy.ts index 118a977a..0059f9f4 100644 --- a/src/charging-station/ocpp/auth/strategies/CertificateAuthStrategy.ts +++ b/src/charging-station/ocpp/auth/strategies/CertificateAuthStrategy.ts @@ -28,7 +28,7 @@ export class CertificateAuthStrategy implements AuthStrategy { public readonly name = 'CertificateAuthStrategy' public readonly priority = 3 - private readonly adapters: Map + private readonly adapter: OCPPAuthAdapter private readonly chargingStation: ChargingStation private isInitialized = false private stats = { @@ -39,9 +39,9 @@ export class CertificateAuthStrategy implements AuthStrategy { totalRequests: 0, } - constructor (chargingStation: ChargingStation, adapters: Map) { + constructor (chargingStation: ChargingStation, adapter: OCPPAuthAdapter) { this.chargingStation = chargingStation - this.adapters = adapters + this.adapter = adapter } /** @@ -73,16 +73,7 @@ export class CertificateAuthStrategy implements AuthStrategy { ) } - // Get the appropriate adapter - const adapter = this.adapters.get(request.identifier.ocppVersion) - if (!adapter) { - return this.createFailureResult( - AuthorizationStatus.INVALID, - `No adapter available for OCPP ${request.identifier.ocppVersion}`, - request.identifier, - startTime - ) - } + const adapter = this.adapter // For OCPP 2.0, we can use certificate-based validation if (request.identifier.ocppVersion === OCPPVersion.VERSION_20) { @@ -126,16 +117,13 @@ export class CertificateAuthStrategy implements AuthStrategy { return false } - // Must have an adapter for this OCPP version - const hasAdapter = this.adapters.has(request.identifier.ocppVersion) - // Certificate authentication must be enabled const certAuthEnabled = config.certificateAuthEnabled // Must have certificate data in the identifier const hasCertificateData = this.hasCertificateData(request.identifier) - return hasAdapter && certAuthEnabled && hasCertificateData && this.isInitialized + return certAuthEnabled && hasCertificateData && this.isInitialized } cleanup (): void { diff --git a/src/charging-station/ocpp/auth/strategies/RemoteAuthStrategy.ts b/src/charging-station/ocpp/auth/strategies/RemoteAuthStrategy.ts index 82135f70..d7bcb7ca 100644 --- a/src/charging-station/ocpp/auth/strategies/RemoteAuthStrategy.ts +++ b/src/charging-station/ocpp/auth/strategies/RemoteAuthStrategy.ts @@ -1,4 +1,3 @@ -import type { OCPPVersion } from '../../../../types/index.js' import type { AuthCache, AuthStrategy, @@ -33,7 +32,7 @@ export class RemoteAuthStrategy implements AuthStrategy { public readonly name = 'RemoteAuthStrategy' public readonly priority = 2 // After local but before certificate - private adapters = new Map() + private adapter?: OCPPAuthAdapter private authCache?: AuthCache private isInitialized = false private localAuthListManager?: LocalAuthListManager @@ -49,27 +48,15 @@ export class RemoteAuthStrategy implements AuthStrategy { } constructor ( - adapters?: Map, + adapter?: OCPPAuthAdapter, authCache?: AuthCache, localAuthListManager?: LocalAuthListManager ) { - if (adapters) { - this.adapters = adapters - } + this.adapter = adapter this.authCache = authCache this.localAuthListManager = localAuthListManager } - /** - * Add an OCPP adapter for a specific version - * @param version - OCPP protocol version the adapter handles - * @param adapter - OCPP authentication adapter instance for remote operations - */ - public addAdapter (version: OCPPVersion, adapter: OCPPAuthAdapter): void { - this.adapters.set(version, adapter) - logger.debug(`${moduleName}: Added OCPP ${version} adapter`) - } - /** * Authenticate using remote CSMS authorization * @param request - Authorization request with identifier and context @@ -96,12 +83,10 @@ export class RemoteAuthStrategy implements AuthStrategy { `${moduleName}: Authenticating ${truncateId(request.identifier.value)} via CSMS for ${request.context}` ) - // Get appropriate adapter for OCPP version - const adapter = this.adapters.get(request.identifier.ocppVersion) + // Get adapter + const adapter = this.adapter if (!adapter) { - logger.warn( - `${moduleName}: No adapter available for OCPP version ${request.identifier.ocppVersion}` - ) + logger.warn(`${moduleName}: No adapter available`) return undefined } @@ -179,11 +164,11 @@ export class RemoteAuthStrategy implements AuthStrategy { * Check if this strategy can handle the authentication request * @param request - Authorization request to evaluate * @param config - Authentication configuration with remote authorization settings - * @returns True if an adapter exists for the OCPP version and remote auth is enabled + * @returns True if an adapter is available and remote auth is enabled */ public canHandle (request: AuthRequest, config: AuthConfiguration): boolean { - // Can handle if we have an adapter for the identifier's OCPP version - const hasAdapter = this.adapters.has(request.identifier.ocppVersion) + // Can handle if we have an adapter + const hasAdapter = this.adapter != null // Remote authorization must be enabled via configuration const remoteEnabled = config.remoteAuthorization !== false @@ -213,29 +198,35 @@ export class RemoteAuthStrategy implements AuthStrategy { logger.info(`${moduleName}: Cleanup completed`) } + /** + * Clear the OCPP adapter + */ + public clearAdapter (): void { + this.adapter = undefined + logger.debug(`${moduleName}: Cleared OCPP adapter`) + } + /** * Get strategy statistics * @returns Strategy statistics including success rates, response times, and error counts */ public async getStats (): Promise> { const cacheStats = this.authCache ? this.authCache.getStats() : null - const adapterStats = new Map() - // Collect adapter availability status - for (const [version, adapter] of this.adapters) { + let adapterAvailable = false + if (this.adapter) { try { - const isAvailable = await adapter.isRemoteAvailable() - adapterStats.set(`ocpp${version}Available`, isAvailable) - } catch (error) { - adapterStats.set(`ocpp${version}Available`, false) + adapterAvailable = await this.adapter.isRemoteAvailable() + } catch { + adapterAvailable = false } } return { ...this.stats, - adapterCount: this.adapters.size, - adapterStats: Object.fromEntries(adapterStats), + adapterAvailable, cacheStats, + hasAdapter: this.adapter != null, hasAuthCache: !!this.authCache, isInitialized: this.isInitialized, networkErrorRate: @@ -254,30 +245,24 @@ export class RemoteAuthStrategy implements AuthStrategy { } /** - * Initialize strategy with configuration and adapters + * Initialize strategy with configuration and adapter * @param config - Authentication configuration for adapter validation */ public initialize (config: AuthConfiguration): void { try { logger.info(`${moduleName}: Initializing...`) - // Validate that we have at least one adapter - if (this.adapters.size === 0) { - logger.warn(`${moduleName}: No OCPP adapters provided`) + // Validate that we have an adapter + if (this.adapter == null) { + logger.warn(`${moduleName}: No OCPP adapter provided`) } const stationVersion = config.ocppVersion ?? 'unknown' - // Validate adapter configurations (deduplicate by instance to avoid - // validating the same adapter twice for VERSION_20 and VERSION_201) - const validatedAdapters = new Set() - for (const [, adapter] of this.adapters) { - if (validatedAdapters.has(adapter)) { - continue - } - validatedAdapters.add(adapter) + // Validate adapter configuration + if (this.adapter) { try { - const isValid = adapter.validateConfiguration(config) + const isValid = this.adapter.validateConfiguration(config) if (!isValid) { logger.warn(`${moduleName}: Invalid configuration for OCPP ${stationVersion}`) } else { @@ -309,16 +294,12 @@ export class RemoteAuthStrategy implements AuthStrategy { } /** - * Remove an OCPP adapter - * @param version - OCPP protocol version of the adapter to remove - * @returns True if the adapter was found and removed + * Set the OCPP adapter + * @param adapter - OCPP authentication adapter instance for remote operations */ - public removeAdapter (version: OCPPVersion): boolean { - const removed = this.adapters.delete(version) - if (removed) { - logger.debug(`${moduleName}: Removed OCPP ${version} adapter`) - } - return removed + public setAdapter (adapter: OCPPAuthAdapter): void { + this.adapter = adapter + logger.debug(`${moduleName}: Set OCPP adapter`) } /** @@ -339,26 +320,18 @@ export class RemoteAuthStrategy implements AuthStrategy { /** * Test connectivity to remote authorization service - * @returns True if at least one OCPP adapter can reach its remote service + * @returns True if the OCPP adapter can reach its remote service */ public async testConnectivity (): Promise { - if (!this.isInitialized || this.adapters.size === 0) { + if (!this.isInitialized || this.adapter == null) { return false } - // Test connectivity for all adapters - const connectivityTests = Array.from(this.adapters.values()).map(async adapter => { - try { - return await adapter.isRemoteAvailable() - } catch (error) { - return false - } - }) - - const results = await Promise.allSettled(connectivityTests) - - // Return true if at least one adapter is available - return results.some(result => result.status === 'fulfilled' && result.value) + try { + return await this.adapter.isRemoteAvailable() + } catch { + return false + } } /** diff --git a/tests/charging-station/ocpp/auth/factories/AuthComponentFactory.test.ts b/tests/charging-station/ocpp/auth/factories/AuthComponentFactory.test.ts index 88f71e7d..0fc91b86 100644 --- a/tests/charging-station/ocpp/auth/factories/AuthComponentFactory.test.ts +++ b/tests/charging-station/ocpp/auth/factories/AuthComponentFactory.test.ts @@ -19,35 +19,35 @@ await describe('AuthComponentFactory', async () => { standardCleanup() }) - await describe('createAdapters', async () => { + await describe('createAdapter', async () => { await it('should create OCPP 1.6 adapter', async () => { const { station: chargingStation } = createMockChargingStation({ stationInfo: { ocppVersion: OCPPVersion.VERSION_16 }, }) - const result = await AuthComponentFactory.createAdapters(chargingStation) + const adapter = await AuthComponentFactory.createAdapter(chargingStation) - assert.notStrictEqual(result.ocpp16Adapter, undefined) - assert.strictEqual(result.ocpp20Adapter, undefined) + assert.notStrictEqual(adapter, undefined) + assert.strictEqual(adapter.ocppVersion, OCPPVersion.VERSION_16) }) await it('should create OCPP 2.0 adapter', async () => { const { station: chargingStation } = createMockChargingStation({ stationInfo: { ocppVersion: OCPPVersion.VERSION_20 }, }) - const result = await AuthComponentFactory.createAdapters(chargingStation) + const adapter = await AuthComponentFactory.createAdapter(chargingStation) - assert.strictEqual(result.ocpp16Adapter, undefined) - assert.notStrictEqual(result.ocpp20Adapter, undefined) + assert.notStrictEqual(adapter, undefined) + assert.strictEqual(adapter.ocppVersion, OCPPVersion.VERSION_20) }) await it('should create OCPP 2.0.1 adapter', async () => { const { station: chargingStation } = createMockChargingStation({ stationInfo: { ocppVersion: OCPPVersion.VERSION_201 }, }) - const result = await AuthComponentFactory.createAdapters(chargingStation) + const adapter = await AuthComponentFactory.createAdapter(chargingStation) - assert.strictEqual(result.ocpp16Adapter, undefined) - assert.notStrictEqual(result.ocpp20Adapter, undefined) + assert.notStrictEqual(adapter, undefined) + assert.strictEqual(adapter.ocppVersion, OCPPVersion.VERSION_20) }) await it('should throw error for unsupported version', async () => { @@ -55,7 +55,7 @@ await describe('AuthComponentFactory', async () => { stationInfo: { ocppVersion: 'VERSION_15' as OCPPVersion }, }) - await assert.rejects(AuthComponentFactory.createAdapters(chargingStation), { + await assert.rejects(AuthComponentFactory.createAdapter(chargingStation), { message: /Unsupported OCPP version/, }) }) @@ -64,7 +64,7 @@ await describe('AuthComponentFactory', async () => { const { station: chargingStation } = createMockChargingStation() chargingStation.stationInfo = undefined - await assert.rejects(AuthComponentFactory.createAdapters(chargingStation), { + await assert.rejects(AuthComponentFactory.createAdapter(chargingStation), { message: /OCPP version not found/, }) }) @@ -153,7 +153,7 @@ await describe('AuthComponentFactory', async () => { const { station: chargingStation } = createMockChargingStation({ stationInfo: { ocppVersion: OCPPVersion.VERSION_16 }, }) - const adapters = await AuthComponentFactory.createAdapters(chargingStation) + const adapter = await AuthComponentFactory.createAdapter(chargingStation) const config: AuthConfiguration = { allowOfflineTxForUnknownId: false, authorizationCacheEnabled: false, @@ -165,7 +165,7 @@ await describe('AuthComponentFactory', async () => { remoteAuthorization: false, } - const result = await AuthComponentFactory.createRemoteStrategy(adapters, undefined, config) + const result = await AuthComponentFactory.createRemoteStrategy(adapter, undefined, config) assert.strictEqual(result, undefined) }) @@ -174,7 +174,7 @@ await describe('AuthComponentFactory', async () => { const { station: chargingStation } = createMockChargingStation({ stationInfo: { ocppVersion: OCPPVersion.VERSION_16 }, }) - const adapters = await AuthComponentFactory.createAdapters(chargingStation) + const adapter = await AuthComponentFactory.createAdapter(chargingStation) const config: AuthConfiguration = { allowOfflineTxForUnknownId: false, authorizationCacheEnabled: false, @@ -186,7 +186,7 @@ await describe('AuthComponentFactory', async () => { remoteAuthorization: true, } - const result = await AuthComponentFactory.createRemoteStrategy(adapters, undefined, config) + const result = await AuthComponentFactory.createRemoteStrategy(adapter, undefined, config) assert.notStrictEqual(result, undefined) if (result) { @@ -200,7 +200,7 @@ await describe('AuthComponentFactory', async () => { const { station: chargingStation } = createMockChargingStation({ stationInfo: { ocppVersion: OCPPVersion.VERSION_16 }, }) - const adapters = await AuthComponentFactory.createAdapters(chargingStation) + const adapter = await AuthComponentFactory.createAdapter(chargingStation) const config: AuthConfiguration = { allowOfflineTxForUnknownId: false, authorizationCacheEnabled: false, @@ -213,7 +213,7 @@ await describe('AuthComponentFactory', async () => { const result = await AuthComponentFactory.createCertificateStrategy( chargingStation, - adapters, + adapter, config ) @@ -227,7 +227,7 @@ await describe('AuthComponentFactory', async () => { const { station: chargingStation } = createMockChargingStation({ stationInfo: { ocppVersion: OCPPVersion.VERSION_16 }, }) - const adapters = await AuthComponentFactory.createAdapters(chargingStation) + const adapter = await AuthComponentFactory.createAdapter(chargingStation) const config: AuthConfiguration = { allowOfflineTxForUnknownId: false, authorizationCacheEnabled: false, @@ -240,7 +240,7 @@ await describe('AuthComponentFactory', async () => { const result = await AuthComponentFactory.createStrategies( chargingStation, - adapters, + adapter, undefined, undefined, config @@ -254,7 +254,7 @@ await describe('AuthComponentFactory', async () => { const { station: chargingStation } = createMockChargingStation({ stationInfo: { ocppVersion: OCPPVersion.VERSION_16 }, }) - const adapters = await AuthComponentFactory.createAdapters(chargingStation) + const adapter = await AuthComponentFactory.createAdapter(chargingStation) const config: AuthConfiguration = { allowOfflineTxForUnknownId: false, authorizationCacheEnabled: false, @@ -268,7 +268,7 @@ await describe('AuthComponentFactory', async () => { const result = await AuthComponentFactory.createStrategies( chargingStation, - adapters, + adapter, undefined, undefined, config diff --git a/tests/charging-station/ocpp/auth/strategies/CertificateAuthStrategy.test.ts b/tests/charging-station/ocpp/auth/strategies/CertificateAuthStrategy.test.ts index b45da9a2..3a3f1ffb 100644 --- a/tests/charging-station/ocpp/auth/strategies/CertificateAuthStrategy.test.ts +++ b/tests/charging-station/ocpp/auth/strategies/CertificateAuthStrategy.test.ts @@ -54,10 +54,7 @@ await describe('CertificateAuthStrategy', async () => { }), }) - const adapters = new Map() - adapters.set(OCPPVersion.VERSION_20, mockOCPP20Adapter) - - strategy = new CertificateAuthStrategy(mockStation, adapters) + strategy = new CertificateAuthStrategy(mockStation, mockOCPP20Adapter) }) afterEach(() => { diff --git a/tests/charging-station/ocpp/auth/strategies/RemoteAuthStrategy.test.ts b/tests/charging-station/ocpp/auth/strategies/RemoteAuthStrategy.test.ts index 58fa2cef..2faa90ae 100644 --- a/tests/charging-station/ocpp/auth/strategies/RemoteAuthStrategy.test.ts +++ b/tests/charging-station/ocpp/auth/strategies/RemoteAuthStrategy.test.ts @@ -36,19 +36,13 @@ await describe('RemoteAuthStrategy', async () => { let mockAuthCache: AuthCache let mockLocalAuthListManager: LocalAuthListManager let mockOCPP16Adapter: OCPPAuthAdapter - let mockOCPP20Adapter: OCPPAuthAdapter beforeEach(() => { mockAuthCache = createMockAuthCache() mockLocalAuthListManager = createMockLocalAuthListManager() mockOCPP16Adapter = createMockOCPPAdapter(OCPPVersion.VERSION_16) - mockOCPP20Adapter = createMockOCPPAdapter(OCPPVersion.VERSION_20) - const adapters = new Map() - adapters.set(OCPPVersion.VERSION_16, mockOCPP16Adapter) - adapters.set(OCPPVersion.VERSION_20, mockOCPP20Adapter) - - strategy = new RemoteAuthStrategy(adapters, mockAuthCache, mockLocalAuthListManager) + strategy = new RemoteAuthStrategy(mockOCPP16Adapter, mockAuthCache, mockLocalAuthListManager) }) afterEach(() => { @@ -69,16 +63,15 @@ await describe('RemoteAuthStrategy', async () => { }) await describe('initialize', async () => { - await it('should initialize successfully with adapters', () => { + await it('should initialize successfully with adapter', () => { const config = createTestAuthConfig({ authorizationCacheEnabled: true }) assert.doesNotThrow(() => { strategy.initialize(config) }) }) - await it('should validate adapter configurations', () => { + await it('should validate adapter configuration', () => { mockOCPP16Adapter.validateConfiguration = () => true - mockOCPP20Adapter.validateConfiguration = () => true const config = createTestAuthConfig() assert.doesNotThrow(() => { strategy.initialize(config) @@ -133,7 +126,7 @@ await describe('RemoteAuthStrategy', async () => { strategy.initialize(config) }) - await it('should authenticate using OCPP 1.6 adapter', async () => { + await it('should authenticate using adapter', async () => { const config = createTestAuthConfig({ authorizationCacheEnabled: true }) const request = createMockAuthRequest({ identifier: createMockIdentifier( @@ -150,23 +143,6 @@ await describe('RemoteAuthStrategy', async () => { assert.strictEqual(result.method, AuthenticationMethod.REMOTE_AUTHORIZATION) }) - await it('should authenticate using OCPP 2.0 adapter', async () => { - const config = createTestAuthConfig({ authorizationCacheEnabled: true }) - const request = createMockAuthRequest({ - identifier: { - ocppVersion: OCPPVersion.VERSION_20, - type: IdentifierType.ID_TAG, - value: 'REMOTE_TAG_20', - }, - }) - - const result = await strategy.authenticate(request, config) - - assert.notStrictEqual(result, undefined) - assert.strictEqual(result?.status, AuthorizationStatus.ACCEPTED) - assert.strictEqual(result.method, AuthenticationMethod.REMOTE_AUTHORIZATION) - }) - await it('should cache successful authorization results', async () => { let cachedKey: string | undefined mockAuthCache.set = (key: string) => { @@ -321,6 +297,9 @@ await describe('RemoteAuthStrategy', async () => { }) await it('should return undefined when no adapter available', async () => { + const strategyNoAdapter = new RemoteAuthStrategy(undefined, mockAuthCache) + strategyNoAdapter.initialize(createTestAuthConfig()) + const config = createTestAuthConfig() const request = createMockAuthRequest({ identifier: { @@ -330,7 +309,7 @@ await describe('RemoteAuthStrategy', async () => { }, }) - const result = await strategy.authenticate(request, config) + const result = await strategyNoAdapter.authenticate(request, config) assert.strictEqual(result, undefined) }) @@ -417,9 +396,9 @@ await describe('RemoteAuthStrategy', async () => { }) await describe('adapter management', async () => { - await it('should add adapter dynamically', () => { + await it('should set adapter dynamically', () => { const newStrategy = new RemoteAuthStrategy() - newStrategy.addAdapter(OCPPVersion.VERSION_16, mockOCPP16Adapter) + newStrategy.setAdapter(mockOCPP16Adapter) const config = createTestAuthConfig() const request = createMockAuthRequest({ @@ -429,8 +408,8 @@ await describe('RemoteAuthStrategy', async () => { assert.strictEqual(newStrategy.canHandle(request, config), true) }) - await it('should remove adapter', () => { - void strategy.removeAdapter(OCPPVersion.VERSION_16) + await it('should clear adapter', () => { + strategy.clearAdapter() const config = createTestAuthConfig() const request = createMockAuthRequest({ @@ -454,9 +433,8 @@ await describe('RemoteAuthStrategy', async () => { assert.strictEqual(result, false) }) - await it('should return false when all adapters unavailable', async () => { + await it('should return false when adapter unavailable', async () => { mockOCPP16Adapter.isRemoteAvailable = () => false - mockOCPP20Adapter.isRemoteAvailable = () => false strategy.initialize(createTestAuthConfig()) const result = await strategy.testConnectivity() @@ -467,7 +445,7 @@ await describe('RemoteAuthStrategy', async () => { await describe('getStats', async () => { await it('should return strategy statistics', async () => { const stats = await strategy.getStats() - assert.strictEqual(stats.adapterCount, 2) + assert.strictEqual(stats.hasAdapter, true) assert.strictEqual(stats.failedRemoteAuth, 0) assert.strictEqual(stats.hasAuthCache, true) assert.strictEqual(stats.isInitialized, false) @@ -478,7 +456,7 @@ await describe('RemoteAuthStrategy', async () => { await it('should include adapter statistics', async () => { strategy.initialize(createTestAuthConfig()) const stats = await strategy.getStats() - assert.notStrictEqual(stats.adapterStats, undefined) + assert.strictEqual(typeof stats.adapterAvailable, 'boolean') }) })