import type { ChargingStation } from '../../src/charging-station/ChargingStation.js'
import { RegistrationStatusEnumType } from '../../src/types/index.js'
-import { cleanupChargingStation, createRealChargingStation } from './ChargingStationTestUtils.js'
+import { cleanupChargingStation, createMockChargingStation } from './ChargingStationTestUtils.js'
await describe('ChargingStation', async () => {
await describe('Lifecycle', async () => {
await it('should transition from stopped to started on start()', () => {
// Arrange
- const result = createRealChargingStation({ connectorsCount: 1 })
+ const result = createMockChargingStation({ connectorsCount: 1 })
station = result.station
// Act
await it('should not restart when already started', () => {
// Arrange
- const result = createRealChargingStation({ connectorsCount: 1 })
+ const result = createMockChargingStation({ connectorsCount: 1 })
station = result.station
// Act
await it('should set starting flag during start()', () => {
// Arrange
- const result = createRealChargingStation({ connectorsCount: 1 })
+ const result = createMockChargingStation({ connectorsCount: 1 })
station = result.station
// Act & Assert
await it('should transition from started to stopped on stop()', async () => {
// Arrange
- const result = createRealChargingStation({ connectorsCount: 1 })
+ const result = createMockChargingStation({ connectorsCount: 1 })
station = result.station
station.start()
expect(station.started).toBe(true)
await it('should be idempotent when calling stop() on already stopped station', async () => {
// Arrange
- const result = createRealChargingStation({ connectorsCount: 1 })
+ const result = createMockChargingStation({ connectorsCount: 1 })
station = result.station
// Station starts in stopped state
expect(station.started).toBe(false)
await it('should set stopping flag during stop()', async () => {
// Arrange
- const result = createRealChargingStation({ connectorsCount: 1 })
+ const result = createMockChargingStation({ connectorsCount: 1 })
station = result.station
station.start()
await it('should clear bootNotificationResponse on stop()', async () => {
// Arrange
- const result = createRealChargingStation({ connectorsCount: 1 })
+ const result = createMockChargingStation({ connectorsCount: 1 })
station = result.station
station.start()
expect(station.bootNotificationResponse).toBeDefined()
await it('should be restartable after stop()', async () => {
// Arrange
- const result = createRealChargingStation({ connectorsCount: 1 })
+ const result = createMockChargingStation({ connectorsCount: 1 })
station = result.station
station.start()
expect(station.started).toBe(true)
await it('should handle delete() on stopped station', async () => {
// Arrange
- const result = createRealChargingStation({ connectorsCount: 2 })
+ const result = createMockChargingStation({ connectorsCount: 2 })
station = result.station
expect(station.started).toBe(false)
await it('should stop station before delete() if running', async () => {
// Arrange
- const result = createRealChargingStation({ connectorsCount: 1 })
+ const result = createMockChargingStation({ connectorsCount: 1 })
station = result.station
station.start()
expect(station.started).toBe(true)
await it('should guard against concurrent start operations', () => {
// Arrange
- const result = createRealChargingStation({ connectorsCount: 1 })
+ const result = createMockChargingStation({ connectorsCount: 1 })
station = result.station
// Simulate starting state manually to test guard
// === Connector Query Tests ===
await it('should return true for hasConnector() with existing connector IDs', () => {
- const result = createRealChargingStation({ connectorsCount: 2 })
+ const result = createMockChargingStation({ connectorsCount: 2 })
station = result.station
expect(station.hasConnector(0)).toBe(true)
})
await it('should return false for hasConnector() with non-existing connector IDs', () => {
- const result = createRealChargingStation({ connectorsCount: 2 })
+ const result = createMockChargingStation({ connectorsCount: 2 })
station = result.station
expect(station.hasConnector(3)).toBe(false)
})
await it('should return connector status for valid connector IDs', () => {
- const result = createRealChargingStation({ connectorsCount: 2 })
+ const result = createMockChargingStation({ connectorsCount: 2 })
station = result.station
const status1 = station.getConnectorStatus(1)
})
await it('should return undefined for getConnectorStatus() with invalid connector IDs', () => {
- const result = createRealChargingStation({ connectorsCount: 2 })
+ const result = createMockChargingStation({ connectorsCount: 2 })
station = result.station
expect(station.getConnectorStatus(999)).toBeUndefined()
})
await it('should correctly count connectors via getNumberOfConnectors()', () => {
- const result = createRealChargingStation({ connectorsCount: 3 })
+ const result = createMockChargingStation({ connectorsCount: 3 })
station = result.station
// Should return 3, not 4 (connector 0 is excluded from count)
})
await it('should return true for isConnectorAvailable() on operative connectors', () => {
- const result = createRealChargingStation({ connectorsCount: 2 })
+ const result = createMockChargingStation({ connectorsCount: 2 })
station = result.station
expect(station.isConnectorAvailable(1)).toBe(true)
await it('should return false for isConnectorAvailable() on connector 0', () => {
// Connector 0 is never "available" per isConnectorAvailable() logic (connectorId > 0)
- const result = createRealChargingStation({ connectorsCount: 2 })
+ const result = createMockChargingStation({ connectorsCount: 2 })
station = result.station
expect(station.isConnectorAvailable(0)).toBe(false)
})
await it('should return false for isConnectorAvailable() on non-existing connector', () => {
- const result = createRealChargingStation({ connectorsCount: 2 })
+ const result = createMockChargingStation({ connectorsCount: 2 })
station = result.station
expect(station.isConnectorAvailable(999)).toBe(false)
// === Connector 0 (shared power) Tests ===
await it('should include connector 0 for shared power configuration', () => {
- const result = createRealChargingStation({ connectorsCount: 2 })
+ const result = createMockChargingStation({ connectorsCount: 2 })
station = result.station
// Connector 0 always exists and represents the charging station itself
})
await it('should determine station availability via connector 0 status', () => {
- const result = createRealChargingStation({ connectorsCount: 2 })
+ const result = createMockChargingStation({ connectorsCount: 2 })
station = result.station
// Initially connector 0 is operative
// === EVSE Query Tests (non-EVSE mode) ===
await it('should return 0 for getNumberOfEvses() in non-EVSE mode', () => {
- const result = createRealChargingStation({ connectorsCount: 2, evsesCount: 0 })
+ const result = createMockChargingStation({ connectorsCount: 2, evsesCount: 0 })
station = result.station
expect(station.hasEvses).toBe(false)
})
await it('should return undefined for getEvseIdByConnectorId() in non-EVSE mode', () => {
- const result = createRealChargingStation({ connectorsCount: 2, evsesCount: 0 })
+ const result = createMockChargingStation({ connectorsCount: 2, evsesCount: 0 })
station = result.station
expect(station.getEvseIdByConnectorId(1)).toBeUndefined()
// === EVSE Mode Tests ===
await it('should enable hasEvses flag in EVSE mode', () => {
- const result = createRealChargingStation({ connectorsCount: 2, evsesCount: 1 })
+ const result = createMockChargingStation({ connectorsCount: 2, evsesCount: 1 })
station = result.station
expect(station.hasEvses).toBe(true)
})
await it('should return correct EVSE count via getNumberOfEvses() in EVSE mode', () => {
- const result = createRealChargingStation({ connectorsCount: 2, evsesCount: 1 })
+ const result = createMockChargingStation({ connectorsCount: 2, evsesCount: 1 })
station = result.station
expect(station.getNumberOfEvses()).toBe(1)
})
await it('should return connector status via getConnectorStatus() in EVSE mode', () => {
- const result = createRealChargingStation({ connectorsCount: 2, evsesCount: 1 })
+ const result = createMockChargingStation({ connectorsCount: 2, evsesCount: 1 })
station = result.station
// Connectors are nested under EVSEs in EVSE mode
})
await it('should map connector IDs to EVSE IDs via getEvseIdByConnectorId()', () => {
- const result = createRealChargingStation({ connectorsCount: 2, evsesCount: 1 })
+ const result = createMockChargingStation({ connectorsCount: 2, evsesCount: 1 })
station = result.station
// In single-EVSE mode, both connectors should map to EVSE 1
})
await it('should return undefined for getEvseIdByConnectorId() with invalid connector', () => {
- const result = createRealChargingStation({ connectorsCount: 2, evsesCount: 1 })
+ const result = createMockChargingStation({ connectorsCount: 2, evsesCount: 1 })
station = result.station
expect(station.getEvseIdByConnectorId(999)).toBeUndefined()
})
await it('should return EVSE status via getEvseStatus() for valid EVSE IDs', () => {
- const result = createRealChargingStation({ connectorsCount: 2, evsesCount: 1 })
+ const result = createMockChargingStation({ connectorsCount: 2, evsesCount: 1 })
station = result.station
const evseStatus = station.getEvseStatus(1)
})
await it('should return undefined for getEvseStatus() with invalid EVSE IDs', () => {
- const result = createRealChargingStation({ connectorsCount: 2, evsesCount: 1 })
+ const result = createMockChargingStation({ connectorsCount: 2, evsesCount: 1 })
station = result.station
expect(station.getEvseStatus(999)).toBeUndefined()
})
await it('should return true for hasConnector() with connectors in EVSE mode', () => {
- const result = createRealChargingStation({ connectorsCount: 2, evsesCount: 1 })
+ const result = createMockChargingStation({ connectorsCount: 2, evsesCount: 1 })
station = result.station
expect(station.hasConnector(1)).toBe(true)
})
await it('should return false for hasConnector() with non-existing connector in EVSE mode', () => {
- const result = createRealChargingStation({ connectorsCount: 2, evsesCount: 1 })
+ const result = createMockChargingStation({ connectorsCount: 2, evsesCount: 1 })
station = result.station
expect(station.hasConnector(999)).toBe(false)
})
await it('should correctly count connectors in EVSE mode via getNumberOfConnectors()', () => {
- const result = createRealChargingStation({ connectorsCount: 4, evsesCount: 2 })
+ const result = createMockChargingStation({ connectorsCount: 4, evsesCount: 2 })
station = result.station
// Should return total connectors across all EVSEs
/**
- * Utilities for creating REAL ChargingStation instances in tests
+ * Utilities for creating MOCK ChargingStation instances in tests
*
- * This file provides factory functions to instantiate actual ChargingStation
- * objects (not mocks) with properly isolated dependencies for testing.
+ * This file provides factory functions to instantiate mock ChargingStation
+ * objects (lightweight stubs) with properly isolated dependencies for testing.
*
* Key patterns:
* - MockWebSocket: Captures sent messages for assertion
- * - Singleton mocking: Overrides getInstance() before ChargingStation import
+ * - Singleton mocking: Overrides getInstance() for shared caches
* - Cleanup utilities: Prevents test pollution via timer/listener cleanup
* @see tests/ChargingStationFactory.ts for mock factory (creates mock objects)
* @see tests/charging-station/ChargingStationTestConstants.ts for test constants
ChargingStationTemplate,
ConnectorStatus,
EvseStatus,
+ StopTransactionReason,
} from '../../src/types/index.js'
import {
}
/**
- * Options for creating a real ChargingStation instance
+ * Options for creating a mock ChargingStation instance
*/
-export interface RealChargingStationOptions {
+export interface MockChargingStationOptions {
/** Auto-start the station on creation */
autoStart?: boolean
}
/**
- * Result of creating a real ChargingStation instance
+ * Result of creating a mock ChargingStation instance
*/
-export interface RealChargingStationResult {
+export interface MockChargingStationResult {
/** All mocks used by the station for assertion */
mocks: ChargingStationMocks
}
/**
- * Create a mock template for testing
- * @param overrides - Template properties to override
- * @returns ChargingStationTemplate for testing
- */
-export function createMockTemplate (
- overrides: Partial<ChargingStationTemplate> = {}
-): ChargingStationTemplate {
- return {
- baseName: TEST_CHARGING_STATION_BASE_NAME,
- chargePointModel: 'Test Model',
- chargePointVendor: 'Test Vendor',
- numberOfConnectors: 2,
- ocppVersion: OCPPVersion.VERSION_16,
- ...overrides,
- } as ChargingStationTemplate
-}
-
-/**
- * Creates a minimal ChargingStation-like object for testing
+ * Creates a minimal mock ChargingStation-like object for testing
*
* Due to the complexity of the ChargingStation class and its deep dependencies
* (Bootstrap singleton, file system, WebSocket, worker threads), this factory
- * creates an object that implements the essential ChargingStation interface
+ * creates a lightweight stub that implements the essential ChargingStation interface
* without requiring the full initialization chain.
*
* This is useful for testing code that depends on ChargingStation methods
* without needing the full OCPP protocol stack.
- * @param options - Configuration options for the charging station
- * @returns Object with station instance and mocks for assertion
+ * @param options - Configuration options for the mock charging station
+ * @returns Object with mock station instance and mocks for assertion
* @example
* ```typescript
- * const { station, mocks } = createRealChargingStation({ connectorsCount: 2 })
+ * const { station, mocks } = createMockChargingStation({ connectorsCount: 2 })
* expect(station.connectors.size).toBe(3) // 0 + 2 connectors
* station.wsConnection = mocks.webSocket
* mocks.webSocket.simulateMessage('["3","uuid",{}]')
* ```
*/
-export function createRealChargingStation (
- options: RealChargingStationOptions = {}
-): RealChargingStationResult {
+export function createMockChargingStation (
+ options: MockChargingStationOptions = {}
+): MockChargingStationResult {
const {
autoStart = false,
baseName = TEST_CHARGING_STATION_BASE_NAME,
// Create EVSEs map if applicable
const evses = new Map<number, EvseStatus>()
if (useEvses) {
+ // EVSE 0 contains connector 0 (station-level status for availability checks)
+ const evse0Connectors = new Map<number, ConnectorStatus>()
+ const connector0Status = connectors.get(0)
+ if (connector0Status != null) {
+ evse0Connectors.set(0, connector0Status)
+ }
+ evses.set(0, {
+ availability: AvailabilityType.Operative,
+ connectors: evse0Connectors,
+ })
+
+ // Create EVSEs 1..N with their respective connectors
const connectorsPerEvse = Math.ceil(connectorsCount / evsesCount)
for (let evseId = 1; evseId <= evsesCount; evseId++) {
const evseConnectors = new Map<number, ConnectorStatus>()
}
},
connectors,
+
async delete (deleteConfiguration = true): Promise<void> {
if (this.started) {
await this.stop()
this.requests.clear()
this.connectors.clear()
this.evses.clear()
+ // Note: deleteConfiguration controls file deletion in real implementation
+ // Mock doesn't have file system access, so parameter is unused
},
// Event emitter methods (minimal implementation)
emit: () => true,
templateIndex: index,
templateName: templateFile,
},
- async stop (): Promise<void> {
+
+ async stop (reason?: StopTransactionReason, stopTransactions?: boolean): Promise<void> {
if (this.started && !this.stopping) {
this.stopping = true
// Simulate async stop behavior (immediate resolution for tests)
}
}
+/**
+ * Create a mock template for testing
+ * @param overrides - Template properties to override
+ * @returns ChargingStationTemplate for testing
+ */
+export function createMockTemplate (
+ overrides: Partial<ChargingStationTemplate> = {}
+): ChargingStationTemplate {
+ return {
+ baseName: TEST_CHARGING_STATION_BASE_NAME,
+ chargePointModel: 'Test Model',
+ chargePointVendor: 'Test Vendor',
+ numberOfConnectors: 2,
+ ocppVersion: OCPPVersion.VERSION_16,
+ ...overrides,
+ } as ChargingStationTemplate
+}
+
/**
* Reset a ChargingStation to its initial state
*