reservationId: 1,
}) as Reservation
- await it('should verify getChargingStationId()', () => {
+ await it('should return formatted charging station ID with index', () => {
expect(getChargingStationId(1, chargingStationTemplate)).toBe(`${baseName}-00001`)
})
- await it('should verify getHashId()', () => {
+ await it('should return consistent hash ID for same template and index', () => {
expect(getHashId(1, chargingStationTemplate)).toBe(
'b4b1e8ec4fca79091d99ea9a7ea5901548010e6c0e98be9296f604b9d68734444dfdae73d7d406b6124b42815214d088'
)
})
- await it('should verify validateStationInfo() - Missing stationInfo', () => {
+ await it('should throw when stationInfo is missing', () => {
// For validation edge cases, we need to manually create invalid states
// since the factory is designed to create valid configurations
const stationNoInfo = createChargingStation({ baseName })
}).toThrow(new BaseError('Missing charging station information'))
})
- await it('should verify validateStationInfo() - Empty stationInfo', () => {
+ await it('should throw when stationInfo is empty object', () => {
// For validation edge cases, manually create empty stationInfo
const stationEmptyInfo = createChargingStation({ baseName })
stationEmptyInfo.stationInfo = {} as ChargingStationInfo
}).toThrow(new BaseError('Missing charging station information'))
})
- await it('should verify validateStationInfo() - Missing chargingStationId', () => {
+ await it('should throw when chargingStationId is undefined', () => {
const stationMissingId = createChargingStation({
baseName,
stationInfo: { baseName, chargingStationId: undefined },
}).toThrow(new BaseError('Missing chargingStationId in stationInfo properties'))
})
- await it('should verify validateStationInfo() - Empty chargingStationId', () => {
+ await it('should throw when chargingStationId is empty string', () => {
const stationEmptyId = createChargingStation({
baseName,
stationInfo: { baseName, chargingStationId: '' },
}).toThrow(new BaseError('Missing chargingStationId in stationInfo properties'))
})
- await it('should verify validateStationInfo() - Missing hashId', () => {
+ await it('should throw when hashId is undefined', () => {
const stationMissingHash = createChargingStation({
baseName,
stationInfo: {
}).toThrow(new BaseError(`${baseName}-00001: Missing hashId in stationInfo properties`))
})
- await it('should verify validateStationInfo() - Empty hashId', () => {
+ await it('should throw when hashId is empty string', () => {
const stationEmptyHash = createChargingStation({
baseName,
stationInfo: {
}).toThrow(new BaseError(`${baseName}-00001: Missing hashId in stationInfo properties`))
})
- await it('should verify validateStationInfo() - Missing templateIndex', () => {
+ await it('should throw when templateIndex is undefined', () => {
const stationMissingTemplate = createChargingStation({
baseName,
stationInfo: {
}).toThrow(new BaseError(`${baseName}-00001: Missing templateIndex in stationInfo properties`))
})
- await it('should verify validateStationInfo() - Invalid templateIndex (zero)', () => {
+ await it('should throw when templateIndex is zero', () => {
const stationInvalidTemplate = createChargingStation({
baseName,
stationInfo: {
)
})
- await it('should verify validateStationInfo() - Missing templateName', () => {
+ await it('should throw when templateName is undefined', () => {
const stationMissingName = createChargingStation({
baseName,
stationInfo: {
}).toThrow(new BaseError(`${baseName}-00001: Missing templateName in stationInfo properties`))
})
- await it('should verify validateStationInfo() - Empty templateName', () => {
+ await it('should throw when templateName is empty string', () => {
const stationEmptyName = createChargingStation({
baseName,
stationInfo: {
}).toThrow(new BaseError(`${baseName}-00001: Missing templateName in stationInfo properties`))
})
- await it('should verify validateStationInfo() - Missing maximumPower', () => {
+ await it('should throw when maximumPower is undefined', () => {
const stationMissingPower = createChargingStation({
baseName,
stationInfo: {
}).toThrow(new BaseError(`${baseName}-00001: Missing maximumPower in stationInfo properties`))
})
- await it('should verify validateStationInfo() - Invalid maximumPower (zero)', () => {
+ await it('should throw when maximumPower is zero', () => {
const stationInvalidPower = createChargingStation({
baseName,
stationInfo: {
)
})
- await it('should verify validateStationInfo() - Missing maximumAmperage', () => {
+ await it('should throw when maximumAmperage is undefined', () => {
const stationMissingAmperage = createChargingStation({
baseName,
stationInfo: {
)
})
- await it('should verify validateStationInfo() - Invalid maximumAmperage (zero)', () => {
+ await it('should throw when maximumAmperage is zero', () => {
const stationInvalidAmperage = createChargingStation({
baseName,
stationInfo: {
)
})
- await it('should verify validateStationInfo() - Valid configuration passes', () => {
+ await it('should pass validation with complete valid configuration', () => {
const validStation = createChargingStation({
baseName,
stationInfo: {
}).not.toThrow()
})
- await it('should verify validateStationInfo() - OCPP 2.0 requires EVSE', () => {
+ await it('should throw for OCPP 2.0 without EVSE configuration', () => {
const stationOcpp20 = createChargingStation({
baseName,
connectorsCount: 0, // Ensure no EVSEs are created
)
})
- await it('should verify validateStationInfo() - OCPP 2.0.1 requires EVSE', () => {
+ await it('should throw for OCPP 2.0.1 without EVSE configuration', () => {
const stationOcpp201 = createChargingStation({
baseName,
connectorsCount: 0, // Ensure no EVSEs are created
)
})
- await it('should verify checkChargingStationState() - Not started or starting', t => {
+ await it('should return false and warn when station is not started or starting', t => {
const warnMock = t.mock.method(logger, 'warn')
const stationNotStarted = createChargingStation({ baseName, started: false, starting: false })
expect(checkChargingStationState(stationNotStarted, 'log prefix |')).toBe(false)
expect(warnMock.mock.calls.length).toBe(1)
})
- await it('should verify checkChargingStationState() - Starting returns true', t => {
+ await it('should return true when station is starting', t => {
const warnMock = t.mock.method(logger, 'warn')
const stationStarting = createChargingStation({ baseName, started: false, starting: true })
expect(checkChargingStationState(stationStarting, 'log prefix |')).toBe(true)
expect(warnMock.mock.calls.length).toBe(0)
})
- await it('should verify checkChargingStationState() - Started returns true', t => {
+ await it('should return true when station is started', t => {
const warnMock = t.mock.method(logger, 'warn')
const stationStarted = createChargingStation({ baseName, started: true, starting: false })
expect(checkChargingStationState(stationStarted, 'log prefix |')).toBe(true)
expect(warnMock.mock.calls.length).toBe(0)
})
- await it('should verify getPhaseRotationValue()', () => {
+ await it('should return correct phase rotation value for connector and phase count', () => {
expect(getPhaseRotationValue(0, 0)).toBe('0.RST')
expect(getPhaseRotationValue(1, 0)).toBe('1.NotApplicable')
expect(getPhaseRotationValue(2, 0)).toBe('2.NotApplicable')
expect(getPhaseRotationValue(2, 3)).toBe('2.RST')
})
- await it('should verify getMaxNumberOfEvses()', () => {
+ await it('should return -1 for undefined EVSEs and 0 for empty object', () => {
expect(getMaxNumberOfEvses(undefined)).toBe(-1)
expect(getMaxNumberOfEvses({})).toBe(0)
})
- await it('should verify checkTemplate()', t => {
+ await it('should throw for undefined or empty template', t => {
const warnMock = t.mock.method(logger, 'warn')
const errorMock = t.mock.method(logger, 'error')
expect(() => {
expect(warnMock.mock.calls.length).toBe(1)
})
- await it('should verify checkConfiguration()', t => {
+ await it('should throw for undefined or empty configuration', t => {
const errorMock = t.mock.method(logger, 'error')
expect(() => {
checkConfiguration(undefined, 'log prefix |', 'configuration.json')
expect(errorMock.mock.calls.length).toBe(2)
})
- await it('should verify checkStationInfoConnectorStatus()', t => {
+ await it('should warn and clear status when connector has predefined status', t => {
const warnMock = t.mock.method(logger, 'warn')
checkStationInfoConnectorStatus(1, {} as ConnectorStatus, 'log prefix |', 'test-template.json')
expect(warnMock.mock.calls.length).toBe(0)
expect(connectorStatus.status).toBeUndefined()
})
- await it('should verify getBootConnectorStatus() - default to Available when no bootStatus', () => {
+ await it('should return Available when no bootStatus is defined', () => {
const chargingStation = createChargingStation({ baseName, connectorsCount: 2 })
const connectorStatus = {} as ConnectorStatus
expect(getBootConnectorStatus(chargingStation, 1, connectorStatus)).toBe(
)
})
- await it('should verify getBootConnectorStatus() - use bootStatus from template', () => {
+ await it('should return bootStatus from template when defined', () => {
const chargingStation = createChargingStation({ baseName, connectorsCount: 2 })
const connectorStatus = {
bootStatus: ConnectorStatusEnum.Unavailable,
)
})
- await it('should verify getBootConnectorStatus() - charging station unavailable overrides bootStatus', () => {
+ await it('should return Unavailable when charging station is inoperative', () => {
const chargingStation = createChargingStation({
baseName,
connectorDefaults: { availability: AvailabilityType.Inoperative },
)
})
- await it('should verify getBootConnectorStatus() - connector unavailable overrides bootStatus', () => {
+ await it('should return Unavailable when connector is inoperative', () => {
const chargingStation = createChargingStation({
baseName,
connectorDefaults: { availability: AvailabilityType.Inoperative },
)
})
- await it('should verify getBootConnectorStatus() - transaction in progress restores previous status', () => {
+ await it('should restore previous status when transaction is in progress', () => {
const chargingStation = createChargingStation({ baseName, connectorsCount: 2 })
const connectorStatus = {
bootStatus: ConnectorStatusEnum.Available,
)
})
- await it('should verify getBootConnectorStatus() - no transaction uses bootStatus over previous status', () => {
+ await it('should use bootStatus over previous status when no transaction', () => {
const chargingStation = createChargingStation({ baseName, connectorsCount: 2 })
const connectorStatus = {
bootStatus: ConnectorStatusEnum.Available,
})
// Tests for reservation helper functions
- await it('should verify hasReservationExpired() - expired reservation', () => {
+ await it('should return true when reservation has expired', () => {
expect(hasReservationExpired(createTestReservation(true))).toBe(true)
})
- await it('should verify hasReservationExpired() - valid reservation', () => {
+ await it('should return false when reservation is still valid', () => {
expect(hasReservationExpired(createTestReservation(false))).toBe(false)
})
- await it('should verify hasPendingReservation() - no reservation', () => {
+ await it('should return false when connector has no reservation', () => {
const connectorStatus = {} as ConnectorStatus
expect(hasPendingReservation(connectorStatus)).toBe(false)
})
- await it('should verify hasPendingReservation() - with valid reservation', () => {
+ await it('should return true when connector has valid pending reservation', () => {
const connectorStatus = { reservation: createTestReservation(false) } as ConnectorStatus
expect(hasPendingReservation(connectorStatus)).toBe(true)
})
- await it('should verify hasPendingReservation() - with expired reservation', () => {
+ await it('should return false when connector reservation has expired', () => {
const connectorStatus = { reservation: createTestReservation(true) } as ConnectorStatus
expect(hasPendingReservation(connectorStatus)).toBe(false)
})
- await it('should verify hasPendingReservations() - no reservations (without EVSEs)', () => {
+ await it('should return false when no reservations exist (connector mode)', () => {
const chargingStation = createChargingStation({ baseName, connectorsCount: 2 })
expect(hasPendingReservations(chargingStation)).toBe(false)
})
- await it('should verify hasPendingReservations() - with pending reservation (without EVSEs)', () => {
+ await it('should return true when pending reservation exists (connector mode)', () => {
const chargingStation = createChargingStation({ baseName, connectorsCount: 2 })
const connectorStatus = chargingStation.connectors.get(1)
if (connectorStatus != null) {
expect(hasPendingReservations(chargingStation)).toBe(true)
})
- await it('should verify hasPendingReservations() - no reservations (with EVSEs)', () => {
+ await it('should return false when no reservations exist (EVSE mode)', () => {
const chargingStation = createChargingStation({
baseName,
connectorsCount: 2,
expect(hasPendingReservations(chargingStation)).toBe(false)
})
- await it('should verify hasPendingReservations() - with pending reservation (with EVSEs)', () => {
+ await it('should return true when pending reservation exists (EVSE mode)', () => {
const chargingStation = createChargingStation({
baseName,
connectorsCount: 2,
expect(hasPendingReservations(chargingStation)).toBe(true)
})
- await it('should verify hasPendingReservations() - with expired reservation only (with EVSEs)', () => {
+ await it('should return false when only expired reservations exist (EVSE mode)', () => {
const chargingStation = createChargingStation({
baseName,
connectorsCount: 2,
})
// FR: G02.FR.06
- await it('should verify HeartBeat request conforms to OCPP 2.0 specification', () => {
+ await it('should build empty HeartBeat request conforming to OCPP 2.0 specification', () => {
const requestParams: OCPP20HeartbeatRequest = {}
const payload = (requestService as any).buildRequestPayload(
OCPP20VariableManager.getInstance().resetRuntimeOverrides()
})
- await it('should verify that OCPP20VariableManager can be instantiated as singleton', () => {
+ await it('should return same instance when getInstance() called multiple times', () => {
const manager1 = OCPP20VariableManager.getInstance()
const manager2 = OCPP20VariableManager.getInstance()
})
await describe('UIHttpServer test suite', async () => {
- await it('should verify sendResponse() deletes handler after sending', () => {
+ await it('should delete response handler after successful send', () => {
const server = new TestableUIHttpServer(createHttpServerConfig())
const res = new MockServerResponse()
expect(res.statusCode).toBe(200)
})
- await it('should verify sendResponse() logs error when handler not found', () => {
+ await it('should log error when response handler not found', () => {
const server = new TestableUIHttpServer(createHttpServerConfig())
server.sendResponse([TEST_UUID, { status: ResponseStatus.SUCCESS }])
expect(server.hasResponseHandler(TEST_UUID)).toBe(false)
})
- await it('should verify sendResponse() sets correct status code for failure', () => {
+ await it('should set status code 400 for failure responses', () => {
const server = new TestableUIHttpServer(createHttpServerConfig())
const res = new MockServerResponse()
expect(res.statusCode).toBe(400)
})
- await it('should verify sendResponse() handles send errors gracefully', () => {
+ await it('should handle send errors gracefully without throwing', () => {
const server = new TestableUIHttpServer(createHttpServerConfig())
const res = new MockServerResponse()
res.end = (): never => {
expect(server.hasResponseHandler(TEST_UUID)).toBe(false)
})
- await it('should verify sendResponse() sets correct Content-Type header', () => {
+ await it('should set application/json Content-Type header', () => {
const server = new TestableUIHttpServer(createHttpServerConfig())
const res = new MockServerResponse()
expect(res.headers['Content-Type']).toBe('application/json')
})
- await it('should verify response handlers cleanup', () => {
+ await it('should clean up response handlers after each response', () => {
const server = new TestableUIHttpServer(createHttpServerConfig())
const res1 = new MockServerResponse()
const res2 = new MockServerResponse()
expect(server.getResponseHandlersSize()).toBe(0)
})
- await it('should verify handlers cleared on server stop', () => {
+ await it('should clear all handlers on server stop', () => {
const server = new TestableUIHttpServer(createHttpServerConfig())
const res = new MockServerResponse()
expect(server.getResponseHandlersSize()).toBe(0)
})
- await it('should verify response payload serialization', () => {
+ await it('should serialize response payload to JSON correctly', () => {
const server = new TestableUIHttpServer(createHttpServerConfig())
const res = new MockServerResponse()
const payload = {
expect(parsedBody.hashIdsSucceeded).toEqual(['station-1', 'station-2'])
})
- await it('should verify response with error details', () => {
+ await it('should include error details in failure response', () => {
const server = new TestableUIHttpServer(createHttpServerConfig())
const res = new MockServerResponse()
const payload = {
expect(parsedBody.hashIdsFailed).toEqual(['station-1'])
})
- await it('should verify valid HTTP configuration', () => {
+ await it('should create server with valid HTTP configuration', () => {
const server = new UIHttpServer(createHttpServerConfig())
expect(server).toBeDefined()
})
- await it('should verify HTTP server with custom config', () => {
+ await it('should create server with custom host and port', () => {
const config = createMockUIServerConfiguration({
options: {
host: 'localhost',
})
await describe('Gzip compression', async () => {
- await it('should verify no compression when acceptsGzip is false', () => {
+ await it('should skip compression when acceptsGzip is false', () => {
const server = new TestableUIHttpServer(createHttpServerConfig())
const res = new MockServerResponse()
expect(res.headers['Content-Type']).toBe('application/json')
})
- await it('should verify no compression for small responses', () => {
+ await it('should skip compression for small response payloads', () => {
const server = new TestableUIHttpServer(createHttpServerConfig())
const res = new MockServerResponse()
expect(res.headers['Content-Type']).toBe('application/json')
})
- await it('should verify no compression below threshold', () => {
+ await it('should skip compression when payload below threshold', () => {
const server = new TestableUIHttpServer(createHttpServerConfig())
const res = new MockServerResponse()
const smallPayload = {
expect(res.headers['Content-Encoding']).toBeUndefined()
})
- await it('should verify compression headers for large responses', async () => {
+ await it('should set gzip Content-Encoding header for large responses', async () => {
const server = new TestableUIHttpServer(createHttpServerConfig())
const res = new MockServerResponse()
expect(res.headers.Vary).toBe('Accept-Encoding')
})
- await it('should verify compressed response decompresses to original payload', async () => {
+ await it('should decompress gzip response to original payload', async () => {
const server = new TestableUIHttpServer(createHttpServerConfig())
const res = new MockServerResponse()
const payload = createLargePayload()
expect(parsedBody.data).toBe(payload.data)
})
- await it('should verify no compression when acceptsGzip context is missing', () => {
+ await it('should skip compression when acceptsGzip context missing', () => {
const server = new TestableUIHttpServer(createHttpServerConfig())
const res = new MockServerResponse()
expect(res.headers['Content-Type']).toBe('application/json')
})
- await it('should verify acceptsGzip context cleanup after response', async () => {
+ await it('should cleanup acceptsGzip context after response sent', async () => {
const server = new TestableUIHttpServer(createHttpServerConfig())
const res = new MockServerResponse()
await describe('UIServerSecurity test suite', async () => {
await describe('isValidCredential()', async () => {
- await it('should verify matching credentials return true', () => {
+ await it('should return true for matching credentials', () => {
expect(isValidCredential('myPassword123', 'myPassword123')).toBe(true)
})
- await it('should verify non-matching credentials return false', () => {
+ await it('should return false for non-matching credentials', () => {
expect(isValidCredential('password1', 'password2')).toBe(false)
})
- await it('should verify empty string credentials match', () => {
+ await it('should return true for empty string credentials', () => {
expect(isValidCredential('', '')).toBe(true)
})
- await it('should verify different length credentials return false', () => {
+ await it('should return false for different length credentials', () => {
// cspell:disable-next-line
expect(isValidCredential('short', 'verylongpassword')).toBe(false)
})
})
await describe('createBodySizeLimiter()', async () => {
- await it('should verify bytes under limit return true', () => {
+ await it('should return true when bytes under limit', () => {
const limiter = createBodySizeLimiter(1000)
expect(limiter(500)).toBe(true)
})
- await it('should verify accumulated bytes exceeding limit return false', () => {
+ await it('should return false when accumulated bytes exceed limit', () => {
const limiter = createBodySizeLimiter(1000)
limiter(600)
expect(limiter(500)).toBe(false)
})
- await it('should verify exact limit boundary returns true', () => {
+ await it('should return true at exact limit boundary', () => {
const limiter = createBodySizeLimiter(1000)
expect(limiter(1000)).toBe(true)
})
await describe('createRateLimiter()', async () => {
- await it('should verify requests under limit are allowed', () => {
+ await it('should allow requests under rate limit', () => {
const limiter = createRateLimiter(5, 1000)
for (let i = 0; i < 5; i++) {
}
})
- await it('should verify requests exceeding limit are blocked', () => {
+ await it('should block requests exceeding rate limit', () => {
const limiter = createRateLimiter(3, 1000)
limiter('192.168.1.1')
limiter('192.168.1.1')
expect(limiter('192.168.1.1')).toBe(false)
})
- await it('should verify window resets after time expires', async () => {
+ await it('should reset window after time expires', async () => {
const limiter = createRateLimiter(2, 100)
limiter('10.0.0.1')
limiter('10.0.0.1')
expect(limiter('10.0.0.1')).toBe(true)
})
- await it('should verify new IPs rejected when at max tracked capacity', () => {
+ await it('should reject new IPs when at max tracked capacity', () => {
const limiter = createRateLimiter(10, 60000, 3)
expect(limiter('192.168.1.1')).toBe(true)
expect(limiter('192.168.1.4')).toBe(false)
})
- await it('should verify existing IPs still allowed when at capacity', () => {
+ await it('should allow existing IPs when at max capacity', () => {
const limiter = createRateLimiter(10, 60000, 2)
expect(limiter('192.168.1.1')).toBe(true)
expect(limiter('192.168.1.2')).toBe(true)
})
- await it('should verify expired entries cleanup when at capacity', async () => {
+ await it('should cleanup expired entries when at capacity', async () => {
const limiter = createRateLimiter(10, 50, 2)
expect(limiter('192.168.1.1')).toBe(true)
expect(limiter('192.168.1.2')).toBe(true)
})
await describe('isValidNumberOfStations()', async () => {
- await it('should verify valid number of stations returns true', () => {
+ await it('should return true for valid number within limit', () => {
expect(isValidNumberOfStations(50, DEFAULT_MAX_STATIONS)).toBe(true)
})
- await it('should verify exceeding max stations returns false', () => {
+ await it('should return false when exceeding max stations', () => {
expect(isValidNumberOfStations(150, DEFAULT_MAX_STATIONS)).toBe(false)
})
- await it('should verify zero stations returns false', () => {
+ await it('should return false for zero stations', () => {
expect(isValidNumberOfStations(0, DEFAULT_MAX_STATIONS)).toBe(false)
})
- await it('should verify negative stations returns false', () => {
+ await it('should return false for negative stations', () => {
expect(isValidNumberOfStations(-5, DEFAULT_MAX_STATIONS)).toBe(false)
})
- await it('should verify exact max stations boundary returns true', () => {
+ await it('should return true at exact max stations boundary', () => {
expect(isValidNumberOfStations(DEFAULT_MAX_STATIONS, DEFAULT_MAX_STATIONS)).toBe(true)
})
})
}
await describe('UIWebSocketServer test suite', async () => {
- await it('should verify sendResponse() deletes handler after sending', () => {
+ await it('should delete response handler after successful send', () => {
const config = createMockUIServerConfiguration()
const server = new TestableUIWebSocketServer(config)
const ws = new MockWebSocket()
expect(ws.sentMessages.length).toBe(1)
})
- await it('should verify sendResponse() logs error when handler not found', () => {
+ await it('should log error when response handler not found', () => {
const config = createMockUIServerConfiguration()
const server = new TestableUIWebSocketServer(config)
expect(server.hasResponseHandler(TEST_UUID)).toBe(false)
})
- await it('should verify sendResponse() deletes handler when WebSocket not open', () => {
+ await it('should delete handler when WebSocket not in OPEN state', () => {
const config = createMockUIServerConfiguration()
const server = new TestableUIWebSocketServer(config)
const ws = new MockWebSocket()
expect(ws.sentMessages.length).toBe(0)
})
- await it('should verify sendResponse() handles send errors gracefully', () => {
+ await it('should handle send errors gracefully without throwing', () => {
const config = createMockUIServerConfiguration()
const server = new TestableUIWebSocketServer(config)
const ws = new MockWebSocket()
expect(server.hasResponseHandler(TEST_UUID)).toBe(false)
})
- await it('should verify broadcast handler persistence (issue #1642)', async () => {
+ await it('should preserve broadcast handler until explicit deletion (issue #1642)', async () => {
const config = createMockUIServerConfiguration()
const server = new TestableUIWebSocketServer(config)
const mockService = new MockUIServiceBroadcast()
expect(server.hasResponseHandler(TEST_UUID)).toBe(false)
})
- await it('should verify non-broadcast handler immediate deletion', async () => {
+ await it('should delete non-broadcast handler immediately after response', async () => {
const config = createMockUIServerConfiguration()
const server = new TestableUIWebSocketServer(config)
const mockService = new MockUIServiceNonBroadcast()
expect(server.hasResponseHandler(TEST_UUID)).toBe(false)
})
- await it('should verify error handler cleanup', async () => {
+ await it('should preserve handler when service throws error', async () => {
const config = createMockUIServerConfiguration()
const server = new TestableUIWebSocketServer(config)
const mockService = new MockUIServiceError()
expect(server.getResponseHandlersSize()).toBe(1)
})
- await it('should verify response handlers cleanup', () => {
+ await it('should clean up response handlers after each response', () => {
const config = createMockUIServerConfiguration()
const server = new TestableUIWebSocketServer(config)
const ws1 = new MockWebSocket()
expect(server.getResponseHandlersSize()).toBe(0)
})
- await it('should verify handlers cleared on server stop', () => {
+ await it('should clear all handlers on server stop', () => {
const config = createMockUIServerConfiguration()
const server = new TestableUIWebSocketServer(config)
const ws = new MockWebSocket()
expect(server.getResponseHandlersSize()).toBe(0)
})
- await it('should verify valid WebSocket configuration', () => {
+ await it('should create server with valid WebSocket configuration', () => {
const config = createMockUIServerConfiguration()
const server = new UIWebSocketServer(config)
expect(server).toBeDefined()
})
- await it('should verify WebSocket server with custom config', () => {
+ await it('should create server with custom host and port', () => {
const config = createMockUIServerConfiguration({
options: {
host: 'localhost',
}
await describe('AbstractUIService test suite', async () => {
- await it('should verify sendResponse checks for response handler existence', () => {
+ await it('should check response handler existence before sending', () => {
const config = createMockUIServerConfiguration()
const server = new TestableUIWebSocketServer(config)
expect(server.hasResponseHandler(TEST_UUID)).toBe(false)
})
- await it('should verify requestHandler returns response for LIST_CHARGING_STATIONS', async () => {
+ await it('should return charging stations list for LIST_CHARGING_STATIONS', async () => {
const config = createMockUIServerConfiguration()
const server = new TestableUIWebSocketServer(config)
}
})
- await it('should verify requestHandler returns response for LIST_TEMPLATES', async () => {
+ await it('should return templates list for LIST_TEMPLATES', async () => {
const config = createMockUIServerConfiguration()
const server = new TestableUIWebSocketServer(config)
}
})
- await it('should verify requestHandler returns error response for unknown procedure', async () => {
+ await it('should return failure response for unknown procedure', async () => {
const config = createMockUIServerConfiguration()
const server = new TestableUIWebSocketServer(config)
}
})
- await it('should verify broadcast channel request tracking initialization', () => {
+ await it('should initialize broadcast channel expected responses to 0', () => {
const config = createMockUIServerConfiguration()
const server = new TestableUIWebSocketServer(config)
}
})
- await it('should verify broadcast channel cleanup on stop', () => {
+ await it('should cleanup broadcast channel on service stop', () => {
const config = createMockUIServerConfiguration()
const server = new TestableUIWebSocketServer(config)
}
})
- await it('should verify requestHandler handles errors gracefully', async () => {
+ await it('should return failure response when request handler throws', async () => {
const config = createMockUIServerConfiguration()
const server = new TestableUIWebSocketServer(config)
}
})
- await it('should verify UI service initialization', () => {
+ await it('should initialize UI service successfully', () => {
const config = createMockUIServerConfiguration()
const server = new TestableUIWebSocketServer(config)
}
})
- await it('should verify multiple service registrations', () => {
+ await it('should prevent duplicate service registrations', () => {
const config = createMockUIServerConfiguration()
const server = new TestableUIWebSocketServer(config)
import { BaseError } from '../../src/exception/BaseError.js'
await describe('BaseError test suite', async () => {
- await it('should verify that BaseError can be instantiated', () => {
+ await it('should create instance with default values', () => {
const baseError = new BaseError()
expect(baseError).toBeInstanceOf(BaseError)
expect(baseError.name).toBe('BaseError')
expect(baseError.date).toBeInstanceOf(Date)
})
- await it('should verify that BaseError can be instantiated with a message', () => {
+ await it('should create instance with custom message', () => {
const baseError = new BaseError('Test message')
expect(baseError).toBeInstanceOf(BaseError)
expect(baseError.message).toBe('Test message')
import { Constants } from '../../src/utils/Constants.js'
await describe('OCPPError test suite', async () => {
- await it('should verify that OCPPError can be instantiated', () => {
+ await it('should create instance with error code and default values', () => {
const ocppError = new OCPPError(ErrorType.GENERIC_ERROR, '')
expect(ocppError).toBeInstanceOf(OCPPError)
expect(ocppError.name).toBe('OCPPError')
} from '../charging-station/helpers/StationHelpers.js'
import { MockIdTagsCache, MockSharedLRUCache } from '../charging-station/mocks/MockCaches.js'
+/**
+ * Result type for console mocks
+ */
+export interface ConsoleMockResult {
+ errorMock: { mock: { calls: unknown[][] } }
+ infoMock: { mock: { calls: unknown[][] } }
+ warnMock: { mock: { calls: unknown[][] } }
+}
+
+/**
+ * Result type for logger mocks
+ */
+export interface LoggerMockResult {
+ errorMock: { mock: { calls: unknown[][] } }
+ warnMock: { mock: { calls: unknown[][] } }
+}
+
/**
* Timer APIs that can be mocked in tests
*/
apis?: MockableTimerAPI[]
}
+/**
+ * Mock context type for Node.js test module
+ */
+interface MockContext {
+ mock: {
+ method: (
+ object: object,
+ methodName: string
+ ) => { mock: { calls: unknown[][] } }
+ }
+}
+
/**
* Helper class for managing mock charging stations in tests
*
}
}
+/**
+ * Factory for creating centralized console mocks
+ *
+ * Reduces boilerplate in tests that need to mock console methods.
+ * @param t - Test context from node:test
+ * @param options - Which console methods to mock
+ * @param options.error - Whether to mock console.error
+ * @param options.info - Whether to mock console.info
+ * @param options.warn - Whether to mock console.warn
+ * @returns Object with console mock references
+ * @example
+ * ```typescript
+ * import { createConsoleMocks } from '../helpers/TestLifecycleHelpers.js'
+ *
+ * await it('should log to console', t => {
+ * const { errorMock, warnMock } = createConsoleMocks(t, { error: true, warn: true })
+ *
+ * // ... test code ...
+ *
+ * expect(warnMock.mock.calls.length).toBe(1)
+ * })
+ * ```
+ */
+export function createConsoleMocks (
+ t: MockContext,
+ options: { error?: boolean; info?: boolean; warn?: boolean } = {}
+): Partial<ConsoleMockResult> {
+ const result: Partial<ConsoleMockResult> = {}
+
+ if (options.error === true) {
+ result.errorMock = t.mock.method(console, 'error')
+ }
+ if (options.warn === true) {
+ result.warnMock = t.mock.method(console, 'warn')
+ }
+ if (options.info === true) {
+ result.infoMock = t.mock.method(console, 'info')
+ }
+
+ return result
+}
+
+/**
+ * Factory for creating centralized logger mocks
+ *
+ * Reduces boilerplate in tests that need to mock logger methods.
+ * @param t - Test context from node:test
+ * @param logger - Logger instance to mock (must have error and warn methods)
+ * @param logger.error - Logger error method
+ * @param logger.warn - Logger warn method
+ * @returns Object with warn and error mock references
+ * @example
+ * ```typescript
+ * import { createLoggerMocks } from '../helpers/TestLifecycleHelpers.js'
+ * import { logger } from '../../src/utils/Logger.js'
+ *
+ * await it('should handle errors', t => {
+ * const { warnMock, errorMock } = createLoggerMocks(t, logger)
+ *
+ * // ... test code ...
+ *
+ * expect(errorMock.mock.calls.length).toBe(1)
+ * })
+ * ```
+ */
+export function createLoggerMocks (
+ t: MockContext,
+ logger: { error: unknown; warn: unknown }
+): LoggerMockResult {
+ return {
+ errorMock: t.mock.method(logger, 'error'),
+ warnMock: t.mock.method(logger, 'warn'),
+ }
+}
+
/**
* Setup a connector with an active transaction
*
} from '../../src/types/ConfigurationData.js'
await describe('ConfigurationData test suite', async () => {
- await it('should verify ConfigurationSection enumeration', () => {
+ await it('should define ConfigurationSection enumeration values', () => {
expect(ConfigurationSection.log).toBe('log')
expect(ConfigurationSection.performanceStorage).toBe('performanceStorage')
expect(ConfigurationSection.uiServer).toBe('uiServer')
expect(ConfigurationSection.worker).toBe('worker')
})
- await it('should verify SupervisionUrlDistribution enumeration', () => {
+ await it('should define SupervisionUrlDistribution enumeration values', () => {
expect(SupervisionUrlDistribution.CHARGING_STATION_AFFINITY).toBe('charging-station-affinity')
expect(SupervisionUrlDistribution.RANDOM).toBe('random')
expect(SupervisionUrlDistribution.ROUND_ROBIN).toBe('round-robin')
})
- await it('should verify ApplicationProtocolVersion enumeration', () => {
+ await it('should define ApplicationProtocolVersion enumeration values', () => {
expect(ApplicationProtocolVersion.VERSION_11).toBe('1.1')
expect(ApplicationProtocolVersion.VERSION_20).toBe('2.0')
})
import { AsyncLock, AsyncLockType } from '../../src/utils/AsyncLock.js'
await describe('AsyncLock test suite', async () => {
- await it('should verify runExclusive() on sync fn', () => {
+ await it('should run synchronous functions exclusively in sequence', () => {
const runs = 10
const executed: number[] = []
let count = 0
}
})
- await it('should verify runExclusive() on async fn', () => {
+ await it('should run asynchronous functions exclusively in sequence', () => {
const runs = 10
const executed: number[] = []
let count = 0
* @description Unit tests for configuration utility functions
*/
import { expect } from '@std/expect'
-import { describe, it } from 'node:test'
+import { afterEach, describe, it } from 'node:test'
import { FileType, StorageType } from '../../src/types/index.js'
import {
handleFileException,
logPrefix,
} from '../../src/utils/ConfigurationUtils.js'
+import { standardCleanup } from '../helpers/TestLifecycleHelpers.js'
await describe('ConfigurationUtils test suite', async () => {
- await it('should verify logPrefix()', () => {
+ afterEach(() => {
+ standardCleanup()
+ })
+
+ await it('should return log prefix with simulator configuration', () => {
expect(logPrefix()).toContain(' Simulator configuration |')
})
- await it('should verify buildPerformanceUriFilePath()', () => {
+ await it('should build file URI path for performance storage', () => {
const result = buildPerformanceUriFilePath('test.json')
expect(result).toContain('test.json')
expect(result).toMatch(/^file:\/\/.*test\.json$/)
})
- await it('should verify getDefaultPerformanceStorageUri()', () => {
+ await it('should return appropriate URI for storage types', () => {
// Test JSON_FILE storage type
const jsonUri = getDefaultPerformanceStorageUri(StorageType.JSON_FILE)
expect(jsonUri).toMatch(/^file:\/\/.*\.json$/)
}).toThrow(Error)
})
- await it('should verify handleFileException()', t => {
+ await it('should throw and log error for file exceptions', t => {
const mockConsoleError = t.mock.method(console, 'error')
const error = new Error() as NodeJS.ErrnoException
error.code = 'ENOENT'
expect(mockConsoleError.mock.calls.length).toBe(1)
})
- await it('should verify checkWorkerElementsPerWorker()', () => {
+ await it('should validate worker elements per worker configuration', () => {
// These calls should not throw exceptions
expect(() => {
checkWorkerElementsPerWorker(undefined)
import { ACElectricUtils, DCElectricUtils } from '../../src/utils/ElectricUtils.js'
await describe('ElectricUtils test suite', async () => {
- await it('should verify DCElectricUtils.power()', () => {
+ await it('should calculate DC power from voltage and current', () => {
expect(DCElectricUtils.power(230, 1)).toBe(230)
})
- await it('should verify DCElectricUtils.amperage()', () => {
+ await it('should calculate DC amperage from power and voltage', () => {
expect(DCElectricUtils.amperage(1, 230)).toBe(0)
})
- await it('should verify ACElectricUtils.powerTotal()', () => {
+ await it('should calculate total AC power for all phases', () => {
expect(ACElectricUtils.powerTotal(3, 230, 1)).toBe(690)
})
- await it('should verify ACElectricUtils.powerPerPhase()', () => {
+ await it('should calculate AC power per phase', () => {
expect(ACElectricUtils.powerPerPhase(230, 1)).toBe(230)
})
- await it('should verify ACElectricUtils.amperageTotal()', () => {
+ await it('should calculate total AC amperage for all phases', () => {
expect(ACElectricUtils.amperageTotal(3, 1)).toBe(3)
})
- await it('should verify ACElectricUtils.amperageTotalFromPower()', () => {
+ await it('should calculate total AC amperage from power and voltage', () => {
expect(ACElectricUtils.amperageTotalFromPower(690, 230)).toBe(3)
})
- await it('should verify ACElectricUtils.amperagePerPhaseFromPower()', () => {
+ await it('should calculate AC amperage per phase from power', () => {
expect(ACElectricUtils.amperagePerPhaseFromPower(3, 690, 230)).toBe(1)
})
})
* @description Unit tests for error handling utilities
*/
import { expect } from '@std/expect'
-import { describe, it } from 'node:test'
+import { afterEach, describe, it } from 'node:test'
import {
FileType,
} from '../../src/utils/ErrorUtils.js'
import { logger } from '../../src/utils/Logger.js'
import { createChargingStation } from '../ChargingStationFactory.js'
+import { standardCleanup } from '../helpers/TestLifecycleHelpers.js'
await describe('ErrorUtils test suite', async () => {
const chargingStation = createChargingStation({ baseName: 'CS-TEST' })
- await it('should verify handleFileException()', t => {
+ afterEach(() => {
+ standardCleanup()
+ })
+
+ await it('should throw or warn based on error code and options', t => {
const consoleWarnMock = t.mock.method(console, 'warn')
const consoleErrorMock = t.mock.method(console, 'error')
const warnMock = t.mock.method(logger, 'warn')
expect(consoleErrorMock.mock.calls.length).toBe(1)
})
- await it('should verify handleSendMessageError()', t => {
+ await it('should log error and optionally throw for send message errors', t => {
const errorMock = t.mock.method(logger, 'error')
const logPrefixMock = t.mock.method(chargingStation, 'logPrefix')
const error = new Error()
expect(errorMock.mock.calls.length).toBe(2)
})
- await it('should verify handleIncomingRequestError()', t => {
+ await it('should log error and return error response for incoming requests', t => {
const errorMock = t.mock.method(logger, 'error')
const logPrefixMock = t.mock.method(chargingStation, 'logPrefix')
const error = new Error()
import { average, max, median, min, percentile, std } from '../../src/utils/StatisticUtils.js'
await describe('StatisticUtils test suite', async () => {
- await it('should verify average()', () => {
+ await it('should calculate arithmetic mean of array values', () => {
expect(average([])).toBe(0)
expect(average([0.08])).toBe(0.08)
expect(average([0.25, 4.75, 3.05, 6.04, 1.01, 2.02, 5.03])).toBe(3.1642857142857146)
expect(average([0.25, 4.75, 3.05, 6.04, 1.01, 2.02])).toBe(2.8533333333333335)
})
- await it('should verify median()', () => {
+ await it('should calculate median value of array', () => {
expect(median([])).toBe(0)
expect(median([0.08])).toBe(0.08)
expect(median([0.25, 4.75, 3.05, 6.04, 1.01, 2.02, 5.03])).toBe(3.05)
expect(median([0.25, 4.75, 3.05, 6.04, 1.01, 2.02])).toBe(2.535)
})
- await it('should verify min()', () => {
+ await it('should return minimum value from arguments', () => {
expect(min()).toBe(Number.POSITIVE_INFINITY)
expect(min(0, 1)).toBe(0)
expect(min(1, 0)).toBe(0)
expect(min(-1, 0)).toBe(-1)
})
- await it('should verify max()', () => {
+ await it('should return maximum value from arguments', () => {
expect(max()).toBe(Number.NEGATIVE_INFINITY)
expect(max(0, 1)).toBe(1)
expect(max(1, 0)).toBe(1)
expect(max(-1, 0)).toBe(0)
})
- await it('should verify percentile()', () => {
+ await it('should calculate nth percentile of array', () => {
expect(percentile([], 25)).toBe(0)
expect(percentile([0.08], 50)).toBe(0.08)
const array0 = [0.25, 4.75, 3.05, 6.04, 1.01, 2.02, 5.03]
expect(percentile(array0, 100)).toBe(6.04)
})
- await it('should verify std()', () => {
+ await it('should calculate standard deviation of array', () => {
expect(std([0.25, 4.75, 3.05, 6.04, 1.01, 2.02, 5.03])).toBe(2.1879050645374383)
})
})
afterEach(() => {
standardCleanup()
})
- await it('should verify generateUUID()/validateUUID()', () => {
+ await it('should generate valid UUIDs and validate them correctly', () => {
const uuid = generateUUID()
expect(uuid).toBeDefined()
expect(uuid.length).toEqual(36)
expect(validateUUID(true)).toBe(false)
})
- await it('should verify validateIdentifierString()', () => {
+ await it('should validate identifier strings within length constraints', () => {
expect(validateIdentifierString('550e8400-e29b-41d4-a716-446655440000', 36)).toBe(true)
expect(validateIdentifierString('CSMS-TXN-12345', 36)).toBe(true)
expect(validateIdentifierString('a', 36)).toBe(true)
expect(validateIdentifierString('valid', 4)).toBe(false)
})
- await it('should verify sleep()', async t => {
+ await it('should sleep for specified milliseconds using timer mock', async t => {
/**
* Timer mock pattern for testing asynchronous timer-based operations.
* Uses Node.js test module's built-in timer mocking API.
}
})
- await it('should verify formatDurationMilliSeconds()', () => {
+ await it('should format milliseconds duration into human readable string', () => {
expect(formatDurationMilliSeconds(0)).toBe('0 seconds')
expect(formatDurationMilliSeconds(900)).toBe('0 seconds')
expect(formatDurationMilliSeconds(1000)).toBe('1 second')
expect(formatDurationMilliSeconds(hoursToMilliseconds(4380))).toBe('182 days 12 hours')
})
- await it('should verify formatDurationSeconds()', () => {
+ await it('should format seconds duration into human readable string', () => {
expect(formatDurationSeconds(0)).toBe('0 seconds')
expect(formatDurationSeconds(0.9)).toBe('0 seconds')
expect(formatDurationSeconds(1)).toBe('1 second')
expect(formatDurationSeconds(hoursToSeconds(4380))).toBe('182 days 12 hours')
})
- await it('should verify isValidDate()', () => {
+ await it('should validate date objects and timestamps correctly', () => {
expect(isValidDate(undefined)).toBe(false)
expect(isValidDate(-1)).toBe(true)
expect(isValidDate(0)).toBe(true)
expect(isValidDate(new Date())).toBe(true)
})
- await it('should verify convertToDate()', () => {
+ await it('should convert various input types to Date objects', () => {
expect(convertToDate(undefined)).toBe(undefined)
expect(convertToDate(null)).toBe(undefined)
expect(() => convertToDate('')).toThrow(new Error("Cannot convert to date: ''"))
expect(date).toStrictEqual(new Date(dateStr))
})
- await it('should verify convertToInt()', () => {
+ await it('should convert various input types to integers', () => {
expect(convertToInt(undefined)).toBe(0)
expect(convertToInt(null)).toBe(0)
expect(convertToInt(0)).toBe(0)
}).toThrow("Cannot convert to integer: 'NaN'")
})
- await it('should verify convertToFloat()', () => {
+ await it('should convert various input types to floats', () => {
expect(convertToFloat(undefined)).toBe(0)
expect(convertToFloat(null)).toBe(0)
expect(convertToFloat(0)).toBe(0)
}).toThrow("Cannot convert to float: 'NaN'")
})
- await it('should verify convertToBoolean()', () => {
+ await it('should convert various input types to booleans', () => {
expect(convertToBoolean(undefined)).toBe(false)
expect(convertToBoolean(null)).toBe(false)
expect(convertToBoolean('true')).toBe(true)
expect(convertToBoolean('NoNBoolean')).toBe(false)
})
- await it('should verify secureRandom()', () => {
+ await it('should generate cryptographically secure random numbers between 0 and 1', () => {
const random = secureRandom()
expect(typeof random === 'number').toBe(true)
expect(random).toBeGreaterThanOrEqual(0)
expect(random).toBeLessThan(1)
})
- await it('should verify roundTo()', () => {
+ await it('should round numbers to specified decimal places correctly', () => {
expect(roundTo(0, 2)).toBe(0)
expect(roundTo(0.5, 0)).toBe(1)
expect(roundTo(0.5, 2)).toBe(0.5)
expect(roundTo(-5.015, 2)).toBe(-5.02)
})
- await it('should verify getRandomFloat()', () => {
+ await it('should generate random floats within specified range', () => {
let randomFloat = getRandomFloat()
expect(typeof randomFloat === 'number').toBe(true)
expect(randomFloat).toBeGreaterThanOrEqual(0)
expect(randomFloat).toBeLessThanOrEqual(0)
})
- await it('should verify extractTimeSeriesValues()', () => {
+ await it('should extract numeric values from timestamped circular buffer', () => {
expect(
extractTimeSeriesValues(
new CircularBuffer<TimestampedData>(Array, Constants.DEFAULT_CIRCULAR_BUFFER_CAPACITY)
expect(extractTimeSeriesValues(circularBuffer)).toEqual([1.1, 2.2, 3.3])
})
- await it('should verify isAsyncFunction()', () => {
+ await it('should correctly identify async functions from other types', () => {
expect(isAsyncFunction(null)).toBe(false)
expect(isAsyncFunction(undefined)).toBe(false)
expect(isAsyncFunction(true)).toBe(false)
expect(isAsyncFunction(TestClass.testStaticAsync)).toBe(true)
})
- await it('should verify clone()', () => {
+ await it('should deep clone objects, arrays, dates, maps and sets', () => {
const obj = { 1: 1 }
expect(clone(obj)).toStrictEqual(obj)
expect(clone(obj) === obj).toBe(false)
expect(() => clone(weakSet)).toThrow(new Error('#<WeakSet> could not be cloned.'))
})
- await it('should verify once()', () => {
+ await it('should execute function only once regardless of call count', () => {
let called = 0
const fn = (): number => ++called
const onceFn = once(fn)
expect(result3).toBe(1)
})
- await it('should verify has()', () => {
+ await it('should check if property exists in object using has()', () => {
expect(has('', 'test')).toBe(false)
expect(has('test', '')).toBe(false)
expect(has('test', 'test')).toBe(false)
expect(has(2, { 1: '1' })).toBe(false)
})
- await it('should verify isEmpty()', () => {
+ await it('should detect empty strings, objects, arrays, maps and sets', () => {
expect(isEmpty('')).toBe(true)
expect(isEmpty(' ')).toBe(true)
expect(isEmpty(' ')).toBe(true)
expect(isEmpty(new WeakSet())).toBe(false)
})
- await it('should verify isNotEmptyString()', () => {
+ await it('should detect non-empty strings correctly', () => {
expect(isNotEmptyString('')).toBe(false)
expect(isNotEmptyString(' ')).toBe(false)
expect(isNotEmptyString(' ')).toBe(false)
expect(isNotEmptyString(new WeakSet())).toBe(false)
})
- await it('should verify isNotEmptyArray()', () => {
+ await it('should detect non-empty arrays correctly', () => {
expect(isNotEmptyArray([])).toBe(false)
expect(isNotEmptyArray([1, 2])).toBe(true)
expect(isNotEmptyArray(['1', '2'])).toBe(true)
expect(isNotEmptyArray(new WeakSet())).toBe(false)
})
- await it('should verify insertAt()', () => {
+ await it('should insert substring at specified index position', () => {
expect(insertAt('test', 'ing', 'test'.length)).toBe('testing')
// eslint-disable-next-line @cspell/spellchecker
expect(insertAt('test', 'ing', 2)).toBe('teingst')
})
- await it('should verify convertToIntOrNaN()', () => {
+ await it('should convert to integer or return NaN for invalid input', () => {
expect(convertToIntOrNaN(undefined)).toBe(0)
expect(convertToIntOrNaN(null)).toBe(0)
expect(convertToIntOrNaN('0')).toBe(0)
expect(Number.isNaN(convertToIntOrNaN('abc'))).toBe(true)
})
- await it('should verify isArraySorted()', () => {
+ await it('should check if array is sorted according to comparator', () => {
expect(isArraySorted<number>([], (a, b) => a - b)).toBe(true)
expect(isArraySorted<number>([1], (a, b) => a - b)).toBe(true)
expect(isArraySorted<number>([1, 2, 3, 4, 5], (a, b) => a - b)).toBe(true)
expect(isArraySorted<number>([2, 1, 3, 4, 5], (a, b) => a - b)).toBe(false)
})
- await it('should verify clampToSafeTimerValue()', () => {
+ await it('should clamp values to safe timer range (0 to MAX_SETINTERVAL_DELAY)', () => {
expect(clampToSafeTimerValue(0)).toBe(0)
expect(clampToSafeTimerValue(1000)).toBe(1000)
expect(clampToSafeTimerValue(Constants.MAX_SETINTERVAL_DELAY)).toBe(
// Exponential Backoff Algorithm Tests (WebSocket Reconnection)
// -------------------------------------------------------------------------
- await it('should verify exponentialDelay() with default parameters', () => {
+ await it('should calculate exponential delay with default parameters', () => {
// Formula: delay = 2^retryNumber * delayFactor + (0-20% random jitter)
// With default delayFactor = 100ms
expect(delay3).toBeLessThanOrEqual(960) // 800 + 20% max jitter
})
- await it('should verify exponentialDelay() with custom delayFactor', () => {
+ await it('should calculate exponential delay with custom delay factor', () => {
// Custom delayFactor = 50ms
const delay0 = exponentialDelay(0, 50)
expect(delay0).toBeGreaterThanOrEqual(50)
expect(delay2).toBeLessThanOrEqual(960)
})
- await it('should verify exponentialDelay() exponential growth pattern', () => {
+ await it('should follow 2^n exponential growth pattern', () => {
// Verify that delays follow 2^n exponential growth pattern
const delayFactor = 100
}
})
- await it('should verify exponentialDelay() includes random jitter', () => {
+ await it('should include random jitter in exponential delay', () => {
// Run multiple times to verify jitter produces different values
const delays = new Set<number>()
const retryNumber = 3
expect(delays.size).toBeGreaterThan(1)
})
- await it('should verify exponentialDelay() jitter is within 0-20% range', () => {
+ await it('should keep jitter within 0-20% range of base delay', () => {
// For a given retry, jitter should add 0-20% of base delay
const retryNumber = 4
const delayFactor = 100
}
})
- await it('should verify exponentialDelay() handles edge cases', () => {
+ await it('should handle edge cases (default retry, large retry, small factor)', () => {
// Default retryNumber (0)
const defaultRetry = exponentialDelay()
expect(defaultRetry).toBeGreaterThanOrEqual(100) // 2^0 * 100
expect(smallFactor).toBeLessThan(5) // 4 + 20%
})
- await it('should verify exponentialDelay() for WebSocket reconnection scenarios', () => {
+ await it('should calculate appropriate delays for WebSocket reconnection scenarios', () => {
// Simulate typical WebSocket reconnection delay sequence
const delayFactor = 100 // Default used in ChargingStation.reconnect()
* @description Unit tests for worker process utility functions
*/
import { expect } from '@std/expect'
-import { describe, it } from 'node:test'
+import { afterEach, describe, it } from 'node:test'
import { WorkerProcessType } from '../../src/worker/WorkerTypes.js'
import {
randomizeDelay,
sleep,
} from '../../src/worker/WorkerUtils.js'
+import { standardCleanup } from '../helpers/TestLifecycleHelpers.js'
await describe('WorkerUtils test suite', async () => {
- await it('should verify checkWorkerProcessType()', () => {
+ afterEach(() => {
+ standardCleanup()
+ })
+
+ await it('should validate worker process types correctly', () => {
// Valid worker process types should not throw
expect(() => {
checkWorkerProcessType(WorkerProcessType.dynamicPool)
}).toThrow(SyntaxError)
})
- await it('should verify sleep()', async t => {
+ await it('should return timeout object after specified delay', async t => {
t.mock.timers.enable({ apis: ['setTimeout'] })
try {
const delay = 10 // 10ms for fast test execution
}
})
- await it('should verify defaultExitHandler()', t => {
+ await it('should log info for success/termination codes, error for other codes', t => {
const mockConsoleInfo = t.mock.method(console, 'info')
const mockConsoleError = t.mock.method(console, 'error')
expect(mockConsoleError.mock.calls.length).toBe(1)
})
- await it('should verify defaultErrorHandler()', t => {
+ await it('should log error with error details', t => {
const mockConsoleError = t.mock.method(console, 'error')
const testError = new Error('Test error message')
expect(mockConsoleError.mock.calls.length).toBe(2)
})
- await it('should verify randomizeDelay()', () => {
+ await it('should randomize delay within ±20% tolerance', () => {
const baseDelay = 1000
const tolerance = baseDelay * 0.2 // 20% tolerance as per implementation