]> Piment Noir Git Repositories - e-mobility-charging-stations-simulator.git/log
e-mobility-charging-stations-simulator.git
31 hours agodocs: update openspec config with current project structure and conventions
Jérôme Benoit [Tue, 17 Mar 2026 12:53:56 +0000 (13:53 +0100)] 
docs: update openspec config with current project structure and conventions

31 hours ago[autofix.ci] apply automated fixes
autofix-ci[bot] [Tue, 17 Mar 2026 12:36:26 +0000 (12:36 +0000)] 
[autofix.ci] apply automated fixes

31 hours agofix(ui): use runtime imports for enums and disable lint rule for .d.ts
Jérôme Benoit [Tue, 17 Mar 2026 12:34:02 +0000 (13:34 +0100)] 
fix(ui): use runtime imports for enums and disable lint rule for .d.ts

31 hours agofix(ocpp2): use convertToDate() for all incoming date fields in handlers
Jérôme Benoit [Tue, 17 Mar 2026 12:21:30 +0000 (13:21 +0100)] 
fix(ocpp2): use convertToDate() for all incoming date fields in handlers

Replace raw new Date(string) and direct string comparisons with
convertToDate() for date fields in incoming OCPP 2.0 payloads:
- validateChargingProfile: validFrom/validTo comparisons
- validateChargingSchedule: startSchedule comparisons
- simulateFirmwareUpdateLifecycle: retrieveDateTime/installDateTime
- handleResponseHeartbeat: currentTime display
- OCPP20CertificateManager: cert.validFrom/validTo validation

32 hours agofix(ocpp-server): update to websockets 16.x API (request.headers, request.path)
Jérôme Benoit [Tue, 17 Mar 2026 11:45:49 +0000 (12:45 +0100)] 
fix(ocpp-server): update to websockets 16.x API (request.headers, request.path)

32 hours agorefactor: replace const enum with enum for cross-module runtime compatibility
Jérôme Benoit [Tue, 17 Mar 2026 11:23:51 +0000 (12:23 +0100)] 
refactor: replace const enum with enum for cross-module runtime compatibility

44 hours agorefactor: re-export OCPPAuthServiceFactory in ocpp barrel and fix cross-sub-component...
Jérôme Benoit [Tue, 17 Mar 2026 00:05:02 +0000 (01:05 +0100)] 
refactor: re-export OCPPAuthServiceFactory in ocpp barrel and fix cross-sub-component imports

44 hours agorefactor: enforce consistent-type-imports eslint rule and fix violations
Jérôme Benoit [Mon, 16 Mar 2026 23:48:15 +0000 (00:48 +0100)] 
refactor: enforce consistent-type-imports eslint rule and fix violations

44 hours ago[autofix.ci] apply automated fixes
autofix-ci[bot] [Mon, 16 Mar 2026 23:15:22 +0000 (23:15 +0000)] 
[autofix.ci] apply automated fixes

44 hours agochore: release main (#1727) ocpp-server@v3.1.1 simulator@v3.1.1 webui@v3.1.1
Jérôme Benoit [Mon, 16 Mar 2026 23:12:46 +0000 (00:12 +0100)] 
chore: release main (#1727)

44 hours agofix(tests): skip flaky RequestStopTransaction test on macOS + Node 22
Jérôme Benoit [Mon, 16 Mar 2026 23:11:26 +0000 (00:11 +0100)] 
fix(tests): skip flaky RequestStopTransaction test on macOS + Node 22

45 hours agoci: move coverage/lint/typecheck/sonar pipeline from Node 22.x to 24.x
Jérôme Benoit [Mon, 16 Mar 2026 23:02:44 +0000 (00:02 +0100)] 
ci: move coverage/lint/typecheck/sonar pipeline from Node 22.x to 24.x

Node 22 --experimental-test-coverage has known bugs (nodejs/node#55510)
that cause false CI failures. Move all gated steps (coverage, lint,
typecheck, SonarCloud, dependency review) to Node 24.x. Node 22 remains
in the test matrix for regular pnpm test runs.

45 hours agofix(tests): use conditional callCount expectation instead of skip for Node 22
Jérôme Benoit [Mon, 16 Mar 2026 22:52:59 +0000 (23:52 +0100)] 
fix(tests): use conditional callCount expectation instead of skip for Node 22

45 hours agofix(tests): remove unnecessary double flushMicrotasks in skipped test
Jérôme Benoit [Mon, 16 Mar 2026 22:24:19 +0000 (23:24 +0100)] 
fix(tests): remove unnecessary double flushMicrotasks in skipped test

45 hours agofix(tests): add second flushMicrotasks for RequestStopTransaction listener
Jérôme Benoit [Mon, 16 Mar 2026 21:56:52 +0000 (22:56 +0100)] 
fix(tests): add second flushMicrotasks for RequestStopTransaction listener

The async chain in requestStopTransaction traverses a dynamic import()
in checkConnectorStatusTransition (OCPPServiceUtils), which may resolve
after the first setImmediate on macOS + Node 22. A second flush ensures
the StatusNotification call completes before the assertion.

46 hours agofix(tests): add second flushMicrotasks for RequestStopTransaction listener
Jérôme Benoit [Mon, 16 Mar 2026 21:56:52 +0000 (22:56 +0100)] 
fix(tests): add second flushMicrotasks for RequestStopTransaction listener

The async chain in requestStopTransaction traverses a dynamic import()
in checkConnectorStatusTransition (OCPPServiceUtils), which may resolve
after the first setImmediate on macOS + Node 22. A second flush ensures
the StatusNotification call completes before the assertion.

46 hours agotest: harmonize event listener test pattern across OCPP command test files (#1730)
Jérôme Benoit [Mon, 16 Mar 2026 21:36:24 +0000 (22:36 +0100)] 
test: harmonize event listener test pattern across OCPP command test files (#1730)

* test: harmonize event listener test pattern across OCPP command test files

Add event listener test sections to 7 OCPP incoming request command test
files (4 OCPP 1.6, 3 OCPP 2.0) following the reference pattern from
RequestStopTransaction, TriggerMessage, UpdateFirmware, and GetLog tests.

Each listener section contains: registration test, accepted-fires test,
rejected-not-fires test, and error-graceful test.

Also restructures CustomerInformation to wrap existing listener tests in a
properly named describe block, and adds createOCPP16ListenerStation helper
to OCPP16TestUtils.ts.

Files modified:
- tests/charging-station/ocpp/1.6/OCPP16TestUtils.ts
- tests/charging-station/ocpp/1.6/OCPP16IncomingRequestService-RemoteStartTransaction.test.ts
- tests/charging-station/ocpp/1.6/OCPP16IncomingRequestService-RemoteStopUnlock.test.ts
- tests/charging-station/ocpp/1.6/OCPP16IncomingRequestService-TriggerMessage.test.ts
- tests/charging-station/ocpp/1.6/OCPP16IncomingRequestService-Firmware.test.ts
- tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-RequestStartTransaction.test.ts
- tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-GetBaseReport.test.ts
- tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-CustomerInformation.test.ts
- tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-RequestStopTransaction.test.ts

* fix(tests): address PR review — extract shared helper, fix mock cleanup, use flushMicrotasks

- Extract createOCPP20ListenerStation to OCPP20TestUtils.ts, removing
  duplication between RequestStart and RequestStop test files
- Replace inline import('node:test').mock.fn with top-level mock import
  in RemoteStartTransaction tests
- Remove redundant mock.reset() from 3 listener afterEach blocks —
  standardCleanup() already calls mock.restoreAll()
- Replace all await Promise.resolve() with flushMicrotasks() across 5
  OCPP 2.0 test files for more robust async side-effect flushing

* fix(tests): replace remaining await Promise.resolve() with flushMicrotasks()

8 occurrences in 5 files (3 OCPP16, 2 OCPP20) missed in the initial
review fix. Now all listener tests use flushMicrotasks() consistently.

* fix(tests): fix 3 audit findings — JSDoc headers and missing afterEach

- GetBaseReport: move @file JSDoc above first import (style guide §2)
- OCPP20TestUtils: add missing @file/@description header (style guide §2)
- RequestStopTransaction: add afterEach with standardCleanup to listener
  describe block (style guide §3)

* fix(tests): address 5 minor audit findings

- Fix import paths in 7 OCPP 2.0 test files: ../../../../tests/helpers/
  → ../../../helpers/ (correct relative path, consistent with 35 sibling
  files in the same directory)
- Add eventType assertion in RequestStartTransaction listener test to
  verify TransactionEvent(Started) per E02.FR.01
- Add flushMicrotasks() to RequestStopTransaction listener test for
  consistent emit→flush→assert pattern

* refactor(tests): move mock.method to beforeEach and parameterize trigger tests

- Move duplicated mock.method calls into listener beforeEach blocks
  in 5 files (UpdateFirmware, GetLog, GetBaseReport, CustomerInfo,
  Firmware). Rejection tests override inline. Net -147 lines.
- Parameterize OCPP16 + OCPP20 TriggerMessage trigger-fires tests
  using data-driven triggerCases arrays (already done in prior commit,
  this commit includes the Firmware mock cleanup).

* style(tests): remove inconsistent separator comment in RemoteStopUnlock

The listener section had a '// ───' separator not used in any of
the other 10 test files. The await describe block is sufficient.

* docs(tests): add summary line to startTransaction JSDoc

* docs(tests): add event listener testing section to TEST_STYLE_GUIDE

Add §11 documenting the established listener test pattern: emit()
direct, flushMicrotasks(), listenerCount first, accepted/rejected/error
triad, mock.method in beforeEach. Add listener station factories to
§9 mock factories table and flushMicrotasks to §10 utility table.

* docs(tests): fix incorrect mock API in §11 code example

Replace Jest-style mockImplementation() with Node.js test runner
mock.method() override pattern matching actual test code.

* fix(tests): align all 112 test files with TEST_STYLE_GUIDE

- Move @file JSDoc headers above first import in 3 files (GetVariables,
  MessageChannelUtils, Utils)
- Replace await Promise.resolve() with flushMicrotasks() in
  AutomaticTransactionGenerator
- Replace 6 setTimeout(resolve, 50) hacks with flushMicrotasks() in
  ChargingStationWorkerBroadcastChannel
- Document spec traceability prefix exception (G03.FR.xx, G04.INT.xx)
  in TEST_STYLE_GUIDE §1 naming conventions

* docs(tests): align TEST_STYLE_GUIDE with actual test infrastructure

- Fix createMockChargingStation location (ChargingStationTestUtils →
  helpers/StationHelpers)
- Add 7 widely-used factories to §9 table (10+ usages each)
- Remove unused expectAcceptedAuthorization from §9 auth table
- All locations verified against actual exports

* docs(tests): fix guide inconsistencies — deduplicate entries, fix assertion example

- Remove createMockCertificateManager from §10 OCPP 2.0 sub-table
  (already in §9 factory table — no duplication)
- Fix §9 usage example: assert.ok → assert.strictEqual (§7 compliance)
- Remove setupConnectorWithTransaction/clearConnectorTransaction from
  §10 lifecycle table (test setup helpers, not lifecycle utilities)
- Reorder §10: group cleanup/async utilities before spy factories

* docs(tests): fix guide precision — assert.ok scope, test script quotes, restore helpers

- Core Principles: clarify assert.ok is for boolean/existence only
- §5: add missing quotes around glob in test script (matches package.json)
- §10: restore setupConnectorWithTransaction/clearConnectorTransaction
  (27 usages across test suite — should not have been removed)

2 days agorefactor(tests): separate handler/listener tests and remove setTimeout hacks
Jérôme Benoit [Mon, 16 Mar 2026 17:58:26 +0000 (18:58 +0100)] 
refactor(tests): separate handler/listener tests and remove setTimeout hacks

Align TriggerMessage, UpdateFirmware, and GetLog test files with the
RequestStopTransaction reference pattern:
- Split into 'Handler validation' + 'event listener' describe groups
- Replace raw setTimeout delays with withMockTimers where needed
- Use emit() directly for listener tests (no wrapper helpers)
- Follows TEST_STYLE_GUIDE.md conventions

2 days agochore(deps): update all non-major dependencies to ^20.5.0 (#1728)
renovate[bot] [Mon, 16 Mar 2026 17:47:17 +0000 (18:47 +0100)] 
chore(deps): update all non-major dependencies to ^20.5.0 (#1728)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2 days agorefactor(tests): split RequestStopTransaction tests into handler and listener groups
Jérôme Benoit [Mon, 16 Mar 2026 17:39:51 +0000 (18:39 +0100)] 
refactor(tests): split RequestStopTransaction tests into handler and listener groups

Remove emitStopEvent helper with fragile setTimeout(50) hack.
Handler tests call handleRequestStopTransaction directly.
Listener tests use emit() directly, matching the TriggerMessage
test pattern. 1856 tests (+3 new listener-specific tests).

2 days agotest(ocpp20): update RequestStopTransaction tests for event listener pattern
Jérôme Benoit [Mon, 16 Mar 2026 17:07:29 +0000 (18:07 +0100)] 
test(ocpp20): update RequestStopTransaction tests for event listener pattern

Adapt test expectations after moving requestStopTransaction from
inline handler to post-response event listener.

2 days agorefactor(ocpp): harmonize post-response event listener pattern across stacks
Jérôme Benoit [Mon, 16 Mar 2026 16:54:02 +0000 (17:54 +0100)] 
refactor(ocpp): harmonize post-response event listener pattern across stacks

Move post-response logic from inline handler to event listeners:
- OCPP16 UpdateFirmware: fire-and-forget in handler → event listener
  matching OCPP20 UpdateFirmware pattern
- OCPP20 RequestStopTransaction: await in handler → event listener
  matching OCPP16 RemoteStopTransaction pattern

All commands with post-response behavior now use the same pattern:
handler validates and returns response, event listener performs
the async action after the response is sent to the CSMS.

2 days agorefactor(ocpp20): move TransactionEvent(Started) to event listener pattern
Jérôme Benoit [Mon, 16 Mar 2026 16:28:14 +0000 (17:28 +0100)] 
refactor(ocpp20): move TransactionEvent(Started) to event listener pattern

Move sendTransactionEvent(Started) and startTxUpdatedInterval from
inside handleRequestStartTransaction to a post-response event
listener in the constructor, matching the OCPP 1.6
RemoteStartTransaction pattern where the handler validates and
returns Accepted, then an event triggers the actual StartTransaction
message send.

2 days agofix(ocpp): graceful OCPPError fallback instead of throw in buildMessageToSend
Jérôme Benoit [Mon, 16 Mar 2026 15:54:52 +0000 (16:54 +0100)] 
fix(ocpp): graceful OCPPError fallback instead of throw in buildMessageToSend

Replace defensive throw with OCPPError construction when messagePayload
is not an OCPPError instance. Uses InternalError code per OCPP-J §4.2.3.
Harmonizes with the ternary instanceof pattern used at L452/503 in the
same file for errorDetails extraction.

2 days agochore: remove outdated OCPPSpecRequirements.md
Jérôme Benoit [Mon, 16 Mar 2026 15:35:01 +0000 (16:35 +0100)] 
chore: remove outdated OCPPSpecRequirements.md

2 days agorefactor: extract ensureError and getErrorMessage helpers
Jérôme Benoit [Mon, 16 Mar 2026 15:33:41 +0000 (16:33 +0100)] 
refactor: extract ensureError and getErrorMessage helpers

Replace 61 duplicated error coercion patterns across 18 files:
- 34× 'error instanceof Error ? error : new Error(String(error))'
  → ensureError(error)
- 27× 'error instanceof Error ? error.message : String(error)'
  → getErrorMessage(error)

New helpers in src/utils/ErrorUtils.ts, exported via barrel.

2 days agofix(ocpp20): send actual MeterValues on trigger and block sessions during firmware...
Jérôme Benoit [Mon, 16 Mar 2026 15:20:23 +0000 (16:20 +0100)] 
fix(ocpp20): send actual MeterValues on trigger and block sessions during firmware update

- F06.FR.06: TriggerMessage for MeterValues now sends TransactionEvent(Updated)
  with actual meter readings for each EVSE with active transactions instead
  of hardcoded value: 0
- L01.FR.07: UpdateFirmware sets idle EVSE connectors to Unavailable when
  AllowNewSessionsPendingFirmwareUpdate is absent or false, preventing new
  sessions while waiting for active transactions to end before installing

2 days agofix(ocpp20): send TransactionEvent(Started) on RequestStartTransaction (E02.FR.01)
Jérôme Benoit [Mon, 16 Mar 2026 14:58:06 +0000 (15:58 +0100)] 
fix(ocpp20): send TransactionEvent(Started) on RequestStartTransaction (E02.FR.01)

The handler accepted remote start transactions without notifying the
CSMS via TransactionEvent. Per E02.FR.01, the CS SHALL send
TransactionEvent with eventType=Started when a transaction begins.

Also starts meter values interval for the new transaction.

2 days agofix(ocpp): replace unsafe messagePayload as OCPPError casts with instanceof
Jérôme Benoit [Mon, 16 Mar 2026 14:57:48 +0000 (15:57 +0100)] 
fix(ocpp): replace unsafe messagePayload as OCPPError casts with instanceof

6 casts in sendMessage error handling replaced with proper
instanceof OCPPError guard per existing codebase pattern.

2 days agofix(charging-station): add try/finally guards to Bootstrap start/stop
Jérôme Benoit [Mon, 16 Mar 2026 14:57:29 +0000 (15:57 +0100)] 
fix(charging-station): add try/finally guards to Bootstrap start/stop

Prevent zombie starting/stopping flags when exceptions occur,
matching the pattern already applied in ChargingStation lifecycle.

2 days agofix(ocpp16): align BootNotification runtime schema maxLength with spec §6.3
Jérôme Benoit [Mon, 16 Mar 2026 14:57:13 +0000 (15:57 +0100)] 
fix(ocpp16): align BootNotification runtime schema maxLength with spec §6.3

chargePointSerialNumber, chargeBoxSerialNumber, meterSerialNumber:
50 → 25 (CiString25Type per spec §6.3 + OCA JSON schema)

2 days agorefactor(ocpp20): harmonize per-station state naming
Jérôme Benoit [Mon, 16 Mar 2026 13:21:23 +0000 (14:21 +0100)] 
refactor(ocpp20): harmonize per-station state naming

- OCPP20PerStationState → OCPP20StationState (drop redundant 'Per')
- stationStates → stationsState (plural: collection of all stations)
- ss → stationState (descriptive local variable name)

2 days agorefactor(ocpp20): remove redundant lastFirmwareStatus from per-station state
Jérôme Benoit [Mon, 16 Mar 2026 13:09:55 +0000 (14:09 +0100)] 
refactor(ocpp20): remove redundant lastFirmwareStatus from per-station state

lastFirmwareStatus in the WeakMap was a duplicate of
stationInfo.firmwareStatus (now written by sendFirmwareStatusNotification).

TriggerMessage now uses hasFirmwareUpdateInProgress() to determine
whether to send Idle or the current intermediate state, which also
fixes a bug where failure end states (DownloadFailed, InstallationFailed,
etc.) were returned instead of Idle.

2 days agorefactor: use union types consistently in common code and tests
Jérôme Benoit [Mon, 16 Mar 2026 12:36:04 +0000 (13:36 +0100)] 
refactor: use union types consistently in common code and tests

- OCPPServiceUtils: use SampledValue union in generics, use
  StatusNotificationRequest union in satisfies, remove unused
  OCPP20StatusNotificationRequest import
- BroadcastChannel: use MeterValuesRequest/Response unions
- OCPPServiceUtils-validation.test: OCPP16MessageTrigger → MessageTrigger,
  remove redundant casts

2 days agorefactor(ocpp): use union types in common code where possible
Jérôme Benoit [Mon, 16 Mar 2026 12:27:33 +0000 (13:27 +0100)] 
refactor(ocpp): use union types in common code where possible

- OCPPServiceUtils: use SampledValue union in generic constraints
  instead of inline OCPP16SampledValue | OCPP20SampledValue union;
  use StatusNotificationRequest union in satisfies check
- BroadcastChannel: use MeterValuesRequest/Response unions instead
  of OCPP20-specific types in version-dispatched handleMeterValues

Stack-specific types remain in version-switch branches where
TypeScript requires narrowed types for array push operations.

2 days agorefactor(tests): use union types instead of stack-specific types in common tests
Jérôme Benoit [Mon, 16 Mar 2026 12:20:30 +0000 (13:20 +0100)] 
refactor(tests): use union types instead of stack-specific types in common tests

Replace OCPP16RequestCommand → RequestCommand,
OCPP16IncomingRequestCommand → IncomingRequestCommand, and
OCPP20RequestCommand → RequestCommand in test files outside
stack-specific directories. Remove redundant 'as Type' casts
that were bridging the gap.

2 days agorefactor(ocpp20): prefix OperationalStatusEnumType with OCPP20
Jérôme Benoit [Mon, 16 Mar 2026 12:11:41 +0000 (13:11 +0100)] 
refactor(ocpp20): prefix OperationalStatusEnumType with OCPP20

Last unprefixed OCPP 2.0 type participating in a common union.
OperationalStatusEnumType → OCPP20OperationalStatusEnumType
matching the OCPP16AvailabilityType counterpart in the
AvailabilityType union.

2 days agorefactor(ocpp20): prefix FirmwareStatusEnumType, extend FirmwareStatus union
Jérôme Benoit [Mon, 16 Mar 2026 12:00:33 +0000 (13:00 +0100)] 
refactor(ocpp20): prefix FirmwareStatusEnumType, extend FirmwareStatus union

- Rename FirmwareStatusEnumType → OCPP20FirmwareStatusEnumType per
  existing OCPP16FirmwareStatus naming convention
- Extend FirmwareStatus union to include all OCPP 2.0.1 firmware states
- Extend FirmwareStatusNotificationRequest to union both versions
- Write firmwareStatus to stationInfo in sendFirmwareStatusNotification
  fixing hasFirmwareUpdateInProgress which was always returning false
- Stack-specific code uses OCPP20FirmwareStatusEnumType, common code
  uses generic FirmwareStatus union

2 days agofix(ocpp16): reject float values for integer configuration keys per spec §5.3
Jérôme Benoit [Mon, 16 Mar 2026 00:20:36 +0000 (01:20 +0100)] 
fix(ocpp16): reject float values for integer configuration keys per spec §5.3

Replace convertToInt (parseInt — silently truncates 3.7 to 3) with
Number.isInteger check. The spec §9.1 defines these keys as Type:
integer, and §5.3 requires Rejected for values that do not conform
to the expected format.

2 days agochore: add .mypy_cache and .pytest_cache to root .gitignore
Jérôme Benoit [Mon, 16 Mar 2026 00:08:28 +0000 (01:08 +0100)] 
chore: add .mypy_cache and .pytest_cache to root .gitignore

2 days agofix(ocpp16): return Idle in TriggerMessage when not actively uploading/updating
Jérôme Benoit [Sun, 15 Mar 2026 23:59:05 +0000 (00:59 +0100)] 
fix(ocpp16): return Idle in TriggerMessage when not actively uploading/updating

Per spec §4.4/§7.24 and §4.5/§7.25, Idle SHALL only be sent via
TriggerMessage when not busy. The code was returning stale terminal
statuses (Uploaded, UploadFailed, Installed, etc.) instead of Idle.

- DiagnosticsStatusNotification: return Uploading only when actively
  uploading, Idle otherwise (fixes stale Uploaded/UploadFailed)
- FirmwareStatusNotification: return Downloading/Downloaded/Installing
  only when in progress, Idle otherwise (fixes stale Installed/Failed)
- UpdateFirmware guard: allow retry after DownloadFailed or
  InstallationFailed instead of blocking all non-Installed statuses

2 days agochore: release main (#1708) ocpp-server@v3.1.0 simulator@v3.1.0 v3 v3.1 webui@v3.1.0
Jérôme Benoit [Sun, 15 Mar 2026 23:35:18 +0000 (00:35 +0100)] 
chore: release main (#1708)

* chore: release main

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2 days agochore: exclude .mypy_cache from prettier scanning
Jérôme Benoit [Sun, 15 Mar 2026 23:31:37 +0000 (00:31 +0100)] 
chore: exclude .mypy_cache from prettier scanning

2 days agorefactor: replace 54 unsafe error casts with instanceof guards
Jérôme Benoit [Sun, 15 Mar 2026 23:13:20 +0000 (00:13 +0100)] 
refactor: replace 54 unsafe error casts with instanceof guards

All 'error as Error', 'error as OCPPError', and 'error as
NodeJS.ErrnoException' casts replaced with proper instanceof
type narrowing across 16 files. Uses the pattern already
established in the auth strategies module:
  error instanceof Error ? error.message : String(error)

For call sites that pass error objects to functions, wraps
non-Error values: error instanceof Error ? error : new Error(String(error))

2 days agorefactor(ocpp): extract responseHandler and incomingRequestHandler into base classes
Jérôme Benoit [Sun, 15 Mar 2026 22:57:20 +0000 (23:57 +0100)] 
refactor(ocpp): extract responseHandler and incomingRequestHandler into base classes

Both methods were 95% identical across OCPP 1.6 and 2.0 subclasses
(~160 lines of pure duplication). Move the shared logic into the base
classes, parameterized by 3 abstract properties each subclass provides:
- bootNotificationRequestCommand / pendingStateBlockedCommands
- csmsName ('central system' vs 'CSMS')
- isRequestCommandSupported / isIncomingRequestCommandSupported

Zero behavior change — pure refactoring validated by 1853 passing tests.

2 days agofix(ocpp): use per-subclass singleton map to prevent version collision
Jérôme Benoit [Sun, 15 Mar 2026 22:37:22 +0000 (23:37 +0100)] 
fix(ocpp): use per-subclass singleton map to prevent version collision

The singleton instance was stored as a single static field on the base
class. In mixed OCPP 1.6/2.0 workers, the first getInstance() call
wins — subsequent calls for a different version silently return the
wrong instance cast via 'as T'.

Replace with Map<Constructor, Instance> keyed by the concrete subclass
constructor. Each version now gets its own singleton entry. Zero
changes needed in subclasses or calling code.

2 days agorefactor(ocpp20): extract named constants for hardcoded sleep/timeout values
Jérôme Benoit [Sun, 15 Mar 2026 22:24:50 +0000 (23:24 +0100)] 
refactor(ocpp20): extract named constants for hardcoded sleep/timeout values

Replace 8 magic numbers with semantic constants:
- FIRMWARE_STATUS_DELAY_MS, FIRMWARE_INSTALL_DELAY_MS,
  FIRMWARE_VERIFY_DELAY_MS for firmware lifecycle simulation
- LOG_UPLOAD_STEP_DELAY_MS for log upload simulation
- RESET_DELAY_MS, RESET_IDLE_MONITOR_INTERVAL_MS for reset scheduling
- CERTIFICATE_VERIFY_DELAY_MS for auth strategy retry delay

2 days agofix(charging-station): add try/finally guards to lifecycle methods
Jérôme Benoit [Sun, 15 Mar 2026 22:24:20 +0000 (23:24 +0100)] 
fix(charging-station): add try/finally guards to lifecycle methods

Prevent zombie state flags when exceptions occur:
- start(): wrap in try/finally to always reset starting=false
- stop(): wrap in try/finally to always reset stopping=false
- reset(): catch stop() failure and abort instead of proceeding
  with sleep+start on a half-stopped station
- delete(): catch stop() failure so cleanup always proceeds

2 days agorefactor(ocpp-server): parametrize failure-path tests to eliminate duplication
Jérôme Benoit [Sun, 15 Mar 2026 21:52:18 +0000 (22:52 +0100)] 
refactor(ocpp-server): parametrize failure-path tests to eliminate duplication

2 days agorefactor(ocpp-server): deduplicate outgoing commands, harden timer, expand test coverage
Jérôme Benoit [Sun, 15 Mar 2026 21:43:56 +0000 (22:43 +0100)] 
refactor(ocpp-server): deduplicate outgoing commands, harden timer, expand test coverage

2 days agochore(ocpp-server): migrate pyproject.toml to PEP 621 project metadata
Jérôme Benoit [Sun, 15 Mar 2026 20:43:50 +0000 (21:43 +0100)] 
chore(ocpp-server): migrate pyproject.toml to PEP 621 project metadata

Move name, version, description, authors, readme from deprecated
[tool.poetry.*] to [project.*] per PEP 621. Move dependencies from
[tool.poetry.dependencies] to [project.dependencies] with PEP 508
specifiers. Set requires-python to >=3.12,<4.0 to match ocpp package
constraint. Regenerate poetry.lock.

2 days agofix(tests): remove stale optional chaining on now-required variableAttribute
Jérôme Benoit [Sun, 15 Mar 2026 20:29:21 +0000 (21:29 +0100)] 
fix(tests): remove stale optional chaining on now-required variableAttribute

ReportDataType.variableAttribute was made required in 429cdbe3 but
test files still used ?. and ?? [] on the field. The stale eslint
cache masked these errors locally; CI (no cache) correctly rejected.

2 days agostyle(ocpp20): condense B09.FR.31 comment to single line per codebase convention
Jérôme Benoit [Sun, 15 Mar 2026 20:24:39 +0000 (21:24 +0100)] 
style(ocpp20): condense B09.FR.31 comment to single line per codebase convention

2 days agofix(ocpp20): implement proper M04.FR.06 guard via isChargingStationCertificateHash
Jérôme Benoit [Sun, 15 Mar 2026 20:16:10 +0000 (21:16 +0100)] 
fix(ocpp20): implement proper M04.FR.06 guard via isChargingStationCertificateHash

The previous M04.FR.06 guard was ineffective: it called
getInstalledCertificates() which scans root cert directories and
maps types via mapInstallTypeToGetType(), which has no case for
ChargingStationCertificate (stored via CertificateSigned, not
InstallCertificate). The V2GCertificateChain filter never matched.

Add isChargingStationCertificateHash() to OCPP20CertificateManager
that directly scans the ChargingStationCertificate directory and
compares certificate hashes. Use it in handleRequestDeleteCertificate
for a reliable M04.FR.06 guard.

3 days agofix(ocpp20): remediate 4 conformance findings from cross-audit
Jérôme Benoit [Sun, 15 Mar 2026 20:04:35 +0000 (21:04 +0100)] 
fix(ocpp20): remediate 4 conformance findings from cross-audit

- M04.FR.06: Narrow DeleteCertificate guard to V2GCertificateChain type
  only, allowing legitimate deletion of root certificates (CSMSRoot,
  ManufacturerRoot, MORootCert, V2GRoot)
- B09.FR.05: Use InvalidConfSlot reasonCode per errata 2025-09 when
  configurationSlot is not in NetworkConfigurationPriority list
- B09.FR.04/FR.31: Check AllowSecurityProfileDowngrade variable before
  rejecting downgrades — allow 3→2 when true, never allow to profile 1
  (errata 2025-09 §2.12)
- L01.FR.06: Wait for active transactions to end before commencing
  firmware installation via polling loop in lifecycle simulation

3 days agorefactor(ocpp16): track diagnosticsStatus and extract composite schedule helper
Jérôme Benoit [Sun, 15 Mar 2026 19:22:37 +0000 (20:22 +0100)] 
refactor(ocpp16): track diagnosticsStatus and extract composite schedule helper

- Add diagnosticsStatus to ChargingStationInfo, set at each
  DiagnosticsStatusNotification send point (Uploading, Uploaded,
  UploadFailed). TriggerMessage now reports actual diagnostics status
  instead of hardcoded Idle, mirroring the firmwareStatus pattern.

- Extract composeCompositeSchedule() private method, eliminating
  ~120 lines of duplicated profile preparation logic between the
  connectorId=0 and connectorId>0 branches of GetCompositeSchedule.

3 days agofix(ocpp16): ocpp 1.6 conformance audit — 8 findings remediated
Jérôme Benoit [Sun, 15 Mar 2026 18:54:33 +0000 (19:54 +0100)] 
fix(ocpp16): ocpp 1.6 conformance audit — 8 findings remediated

Fixes validated against OCPP 1.6 Edition 2 spec and OCA JSON schemas:

- DT-01: DataTransfer returns UnknownMessageId when vendorId matches
  but messageId is provided (spec §4.3)
- CCP-01: ClearChargingProfile applies id/stackLevel/purpose filters
  when connectorId is specified instead of clearing all profiles (spec §5.5)
- TM-01/02/03: TriggerMessage handles DiagnosticsStatusNotification,
  FirmwareStatusNotification and MeterValues triggers (spec §5.17)
- GAP-03: ChangeConfiguration rejects non-integer and negative values
  for known integer config keys (spec §5.3)
- GAP-11: Reset distinguishes Hard/Soft — hard reset skips graceful
  transaction stopping (spec §5.14)
- GCS-02: GetCompositeSchedule implements connectorId=0 to report
  total grid consumption across all connectors (spec §5.7)

3 days agofix(ocpp16): fix StopTransaction unit schema and ChargingSchedule field name
Jérôme Benoit [Sun, 15 Mar 2026 18:53:54 +0000 (19:53 +0100)] 
fix(ocpp16): fix StopTransaction unit schema and ChargingSchedule field name

- Add missing 'Celsius' to StopTransaction.json unit enum (M-2/SP-1)
  The runtime schema had only 'Celcius' (legacy typo) while the OCA
  official schema has both. TS enum produces 'Celsius' which was
  rejected by AJV when ocppStrictCompliance was enabled.

- Rename minChargeRate to minChargingRate in OCPP16ChargingSchedule (RST-01)
  Field name typo caused JSON serialization mismatch with spec §7.13
  and schema 'minChargingRate', breaking SetChargingProfile and
  GetCompositeSchedule payloads.

3 days agofix: clear OCPPAuthServiceFactory cached instance on station delete
Jérôme Benoit [Sun, 15 Mar 2026 18:36:48 +0000 (19:36 +0100)] 
fix: clear OCPPAuthServiceFactory cached instance on station delete

clearInstance was never called, leaking one OCPPAuthService per station
in the factory's static instances Map after station stop/delete.

3 days agofix(ocpp20): clean up VariableManager mappings cache on station stop
Jérôme Benoit [Sun, 15 Mar 2026 18:23:00 +0000 (19:23 +0100)] 
fix(ocpp20): clean up VariableManager mappings cache on station stop

invalidateMappingsCache was never called on station stop, leaking
invalidVariables and validatedStations entries for each stopped station.
Call it alongside resetRuntimeOverrides in stop() for complete cleanup.

3 days agofix(ocpp20): isolate VariableManager overrides per station
Jérôme Benoit [Sun, 15 Mar 2026 18:19:07 +0000 (19:19 +0100)] 
fix(ocpp20): isolate VariableManager overrides per station

runtimeOverrides, minSetOverrides, and maxSetOverrides were stored as flat
Maps on the singleton OCPP20VariableManager, causing SetVariables on one
station to affect all others sharing the same service instance.

Apply the same Map<stationId, Map<key, value>> pattern already used by
invalidVariables in the same class. resetRuntimeOverrides now accepts
optional stationId, matching the invalidateMappingsCache API.

3 days agorefactor(ocpp20): isolate per-station state with WeakMap instead of singleton properties
Jérôme Benoit [Sun, 15 Mar 2026 18:04:39 +0000 (19:04 +0100)] 
refactor(ocpp20): isolate per-station state with WeakMap instead of singleton properties

Per-station state (firmware update tracking, report cache, connector status
backup) was incorrectly stored as flat properties on the singleton
OCPP20IncomingRequestService, causing cross-station pollution when multiple
stations share the same service instance.

Introduce OCPP20PerStationState interface and a WeakMap<ChargingStation, ...>
to properly isolate state per station, matching the stateless service pattern
used by OCPP16IncomingRequestService. State is lazily initialized and
automatically garbage collected when the station is released.

3 days agodocs: fix README OCPP 2.0.x notes with spec-verified FR references
Jérôme Benoit [Sun, 15 Mar 2026 17:39:23 +0000 (18:39 +0100)] 
docs: fix README OCPP 2.0.x notes with spec-verified FR references

3 days agofix(ocpp20): ocpp 2.0.1 conformance audit — 17 findings remediated
Jérôme Benoit [Sun, 15 Mar 2026 17:30:57 +0000 (18:30 +0100)] 
fix(ocpp20): ocpp 2.0.1 conformance audit — 17 findings remediated

- fix(types): widen DataTransfer data to JsonType for unconstrained payloads
- fix(types): narrow VariableAttributeType.type to AttributeEnumType
- fix(types): make ReportDataType.variableAttribute required per JSON schema
- fix(types): allow custom string values for UnitOfMeasure.unit
- fix(firmware): remove invalid AcceptedCanceled from FirmwareStatusEnumType
- fix(firmware): return AcceptedCanceled on cancel, trigger lifecycle for both
- fix(firmware): track lastFirmwareStatus for TriggerMessage (L01.FR.25/26)
- fix(firmware): accept UpdateFirmware with active transactions (L01.FR.06)
- fix(transaction): make evseId optional in RequestStartTransaction (F01.FR.10)
- fix(transaction): consult AuthorizeRemoteStart variable (F01.FR.02)
- fix(network): implement security profile downgrade detection (B09.FR.04)
- fix(network): implement slot-in-priority validation (B09.FR.05)
- fix(trigger): contextual EVSE validation for MeterValues/StatusNotification only
- fix(availability): save/restore pre-Inoperative connector statuses (G03.FR.07)
- fix(availability): add connector-level ChangeAvailability targeting

3 days agofix(ocpp16): harmonize error logging — log silent catches, demote business violations...
Jérôme Benoit [Sun, 15 Mar 2026 15:28:51 +0000 (16:28 +0100)] 
fix(ocpp16): harmonize error logging — log silent catches, demote business violations to warn

3 days agofix(ocpp20): add missing JSDoc @param description for hashAlgorithm in getInstalledCe...
Jérôme Benoit [Sun, 15 Mar 2026 15:19:58 +0000 (16:19 +0100)] 
fix(ocpp20): add missing JSDoc @param description for hashAlgorithm in getInstalledCertificates

3 days agodocs: update README OCPP 2.0.x section with post-audit implementation details
Jérôme Benoit [Sun, 15 Mar 2026 15:17:15 +0000 (16:17 +0100)] 
docs: update README OCPP 2.0.x section with post-audit implementation details

3 days agofix(ocpp20): remediate all OCPP 2.0.1 audit findings (#1726)
Jérôme Benoit [Sun, 15 Mar 2026 15:10:29 +0000 (16:10 +0100)] 
fix(ocpp20): remediate all OCPP 2.0.1 audit findings (#1726)

* fix(ocpp20): add consistent statusInfo to all rejection responses

* fix(ocpp20): cache validatePersistentMappings and fix MinSet/MaxSet atomicity

* feat(ocpp20): implement SetNetworkProfile Accepted path per B09.FR.01

* fix(ocpp20): prevent deletion of ChargingStationCertificate per M04.FR.06

* feat(ocpp20): implement GetLog lifecycle with LogStatusNotification per N01

* fix(ocpp20): implement NotifyCustomerInformation pagination and N09.FR.09 validation

* feat(ocpp20): add X.509 certificate validation per A02.FR.06 and M05

* feat(ocpp20): implement UpdateFirmware lifecycle with status notifications per L01/L02

* feat(ocpp20): generate real PKCS#10 CSR with node:crypto per A02.FR.02

* feat(ocpp20): add firmware signature check, transaction blocking, cancellation

* docs(ocpp20): document OCSP limitation + enforce MaxCertificateChainSize

* style(ocpp20): fix all lint errors and warnings

* [autofix.ci] apply automated fixes

* fix(ocpp20): address PR review findings — enum fix, per-station cache, cancellation status

* [autofix.ci] apply automated fixes

* docs(ocpp20): document simulator limitations for SetNetworkProfile and X.509 validation

* refactor(ocpp20): extract ASN.1 DER utilities to dedicated module

* fix(ocpp20): scope invalidVariables per-station in VariableManager singleton

* [autofix.ci] apply automated fixes

* test(ocpp20): improve test hygiene — extract flushMicrotasks, remove dead code, strengthen spy coverage

* [autofix.ci] apply automated fixes

* fix(ocpp20): replace silent hashId fallback with fail-fast guard in VariableManager

* fix(ocpp20): align B09.FR.02 reasonCode to errata 2025-09 InvalidNetworkConf

* style(ocpp20): normalize spec ref format in additionalInfo to parentheses style

* fix(ocpp20): use request hashAlgorithm in M04.FR.06 guard for algorithm-independent certificate matching

* [autofix.ci] apply automated fixes

* style(ocpp20): harmonize message content — sentence case, consistent punctuation, no em-dash

* fix(ocpp20): unify .catch() log level to logger.error for all fire-and-forget notifications

* fix(ocpp20): remove noisy per-call OCSP stub warning — limitation already documented in JSDoc

* refactor(ocpp20): rename invalidVariablesPerStation to invalidVariables for naming consistency

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
3 days agochore(deps): lock file maintenance (#1725)
renovate[bot] [Sun, 15 Mar 2026 11:42:53 +0000 (12:42 +0100)] 
chore(deps): lock file maintenance (#1725)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
3 days agochore(deps): update dependency jsdom to v29 (#1723)
renovate[bot] [Sun, 15 Mar 2026 10:15:19 +0000 (11:15 +0100)] 
chore(deps): update dependency jsdom to v29 (#1723)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
3 days agochore(deps): update dependency lint-staged to ^16.4.0 (#1721)
renovate[bot] [Sun, 15 Mar 2026 10:13:51 +0000 (11:13 +0100)] 
chore(deps): update dependency lint-staged to ^16.4.0 (#1721)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
3 days agochore(deps): update dependency pytest-cov to v7 (#1724)
renovate[bot] [Sun, 15 Mar 2026 10:11:52 +0000 (11:11 +0100)] 
chore(deps): update dependency pytest-cov to v7 (#1724)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
3 days agochore(deps): update dependency pytest to v9 (#1722)
renovate[bot] [Sun, 15 Mar 2026 09:59:28 +0000 (10:59 +0100)] 
chore(deps): update dependency pytest to v9 (#1722)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
3 days agoci(ocpp-server): add type checking, test coverage and unit tests to CI pipeline ...
Jérôme Benoit [Sat, 14 Mar 2026 23:39:20 +0000 (00:39 +0100)] 
ci(ocpp-server): add type checking, test coverage and unit tests to CI pipeline (#1720)

3 days agofix(renovate): enable Poetry dependency detection for OCPP2 mock server
Jérôme Benoit [Sat, 14 Mar 2026 22:46:56 +0000 (23:46 +0100)] 
fix(renovate): enable Poetry dependency detection for OCPP2 mock server

Override :ignoreModulesAndTests preset to remove **/tests/** from
ignorePaths, allowing Renovate to discover tests/ocpp-server/pyproject.toml
and manage its Python/Poetry dependencies.

4 days agodocs: fix root README coherence with codebase
Jérôme Benoit [Sat, 14 Mar 2026 00:15:41 +0000 (01:15 +0100)] 
docs: fix root README coherence with codebase

- Add missing NotifyCustomerInformation to OCPP 2.0.x section
- Add undocumented station template fields (iccid, imsi,
  meterSerialNumberPrefix, meterType)
- List all available OCPP command procedure names in UI Protocol section
  with separate OCPP 1.6 and 2.0.x lists
- Update copyright year to 2026 in README and LICENSE

4 days agodocs(ui-web): rewrite README with coherent structure and code-verified content
Jérôme Benoit [Sat, 14 Mar 2026 00:01:04 +0000 (01:01 +0100)] 
docs(ui-web): rewrite README with coherent structure and code-verified content

4 days agofix(ocpp-server): share charge_points set across connections and harden test quality
Jérôme Benoit [Fri, 13 Mar 2026 21:08:30 +0000 (22:08 +0100)] 
fix(ocpp-server): share charge_points set across connections and harden test quality

- Fix charge_points set created per-connection instead of shared across
  all connections via ServerConfig
- Remove AuthConfig.__getitem__ dict-compat hack and isinstance(dict)
  coercion branch from ChargePoint.__init__
- Migrate all test fixtures from raw dicts to AuthConfig instances
- Replace 11 weak 'assert X is not None' with typed assertions
  (isinstance, enum equality)
- Hoist Action import to module level in test_server.py

4 days agofix(ocpp-server): use MagicMock for sync method in test to fix RuntimeWarning
Jérôme Benoit [Fri, 13 Mar 2026 20:42:50 +0000 (21:42 +0100)] 
fix(ocpp-server): use MagicMock for sync method in test to fix RuntimeWarning

4 days agotest(ocpp-server): add connection lifecycle and command scheduling tests
Jérôme Benoit [Fri, 13 Mar 2026 20:39:29 +0000 (21:39 +0100)] 
test(ocpp-server): add connection lifecycle and command scheduling tests

4 days agotest(ocpp-server): add behavioral tests for all outgoing commands
Jérôme Benoit [Fri, 13 Mar 2026 20:30:47 +0000 (21:30 +0100)] 
test(ocpp-server): add behavioral tests for all outgoing commands

4 days agotest(ocpp-server): add Timer class test coverage
Jérôme Benoit [Fri, 13 Mar 2026 20:21:08 +0000 (21:21 +0100)] 
test(ocpp-server): add Timer class test coverage

4 days agotest(ocpp-server): centralize constants, strengthen assertions, add auth modes, trim...
Jérôme Benoit [Fri, 13 Mar 2026 20:15:04 +0000 (21:15 +0100)] 
test(ocpp-server): centralize constants, strengthen assertions, add auth modes, trim meta-tests

5 days agofix(ocpp-server): extract CP ID from last URL segment and scope ChargePoints to instance
Jérôme Benoit [Fri, 13 Mar 2026 20:06:33 +0000 (21:06 +0100)] 
fix(ocpp-server): extract CP ID from last URL segment and scope ChargePoints to instance

5 days agorefactor(ocpp-server): introduce AuthMode, AuthConfig and ServerConfig typed dataclasses
Jérôme Benoit [Fri, 13 Mar 2026 20:01:00 +0000 (21:01 +0100)] 
refactor(ocpp-server): introduce AuthMode, AuthConfig and ServerConfig typed dataclasses

5 days agofix(ocpp-server): add suppress=False to all call() invocations and widen randint...
Jérôme Benoit [Fri, 13 Mar 2026 19:53:21 +0000 (20:53 +0100)] 
fix(ocpp-server): add suppress=False to all call() invocations and widen randint range

5 days agodocs(ocpp-server): restructure README with logical section hierarchy
Jérôme Benoit [Fri, 13 Mar 2026 18:58:05 +0000 (19:58 +0100)] 
docs(ocpp-server): restructure README with logical section hierarchy

5 days agofeat(ocpp-server): add error handling, configurable params, async tests, and Python...
Jérôme Benoit [Fri, 13 Mar 2026 18:53:46 +0000 (19:53 +0100)] 
feat(ocpp-server): add error handling, configurable params, async tests, and Python 3.14+ compat

- Add centralized error handling in _send_command dispatch for
  TimeoutError, OCPPError, and ConnectionClosed
- Make host/port configurable via --host and --port CLI args
- Make boot notification status configurable via --boot-status
- Make TransactionEvent total cost configurable via --total-cost
- Extract server defaults as module-level constants
- Fix asyncio.iscoroutine() deprecation in timer.py: use
  inspect.iscoroutinefunction() + inspect.isawaitable() for Python 3.14+
- Add proper type annotations and docstrings to timer.py
- Add pytest-asyncio for async handler testing (82 tests total)
- Add async tests for all 15 incoming OCPP handlers
- Add error handling tests for command dispatch layer
- Add tests for configurable boot_status and total_cost
- Update README with new server and charging behavior CLI options

5 days agofeat(ocpp-server): overhaul OCPP 2.0.1 mock server with full command coverage
Jérôme Benoit [Fri, 13 Mar 2026 18:39:58 +0000 (19:39 +0100)] 
feat(ocpp-server): overhaul OCPP 2.0.1 mock server with full command coverage

- Add 4 new @on handlers: Get15118EVCertificate, GetCertificateStatus,
  SignCertificate, NotifyCustomerInformation
- Add 9 new outgoing commands: CertificateSigned, CustomerInformation,
  DeleteCertificate, GetInstalledCertificateIds, GetLog,
  GetTransactionStatus, InstallCertificate, SetNetworkProfile,
  UpdateFirmware (20 total)
- Fix camelCase key access bug in authorize/transaction handlers
  (ocpp library converts to snake_case)
- Extract _resolve_auth_status() to eliminate duplicated auth logic
- Replace ConnectionError with InternalError for offline simulation
- Use set.discard() instead of set.remove() for safe cleanup
- Add match default case for unknown TransactionEvent types
- Modernize type hints: Optional[X] -> X | None
- Rewrite test suite with pytest (51 tests, 4 test classes)
- Update ruff to ^0.15, add pytest ^8 dev dependency
- Update README with all 20 outgoing commands and 15 incoming handlers

5 days agotest: remove 24 tautological enum value assertion tests
Jérôme Benoit [Fri, 13 Mar 2026 16:29:36 +0000 (17:29 +0100)] 
test: remove 24 tautological enum value assertion tests

Remove tests that assert compile-time constant enum values (e.g.
EnumName.MEMBER === 'hardcodedString'), which always pass by definition
and exercise no production code.

- Delete ConfigurationData.test.ts (3 tautological tests)
- Remove ProcedureName/BroadcastChannelProcedureName enum groups (16 tests)
- Remove AuthTypes Enums block (4 tests)
- Remove OutputFormat enum test (1 test)
- Fix assert.strictEqual(true, false) to assert.fail() in OCPPAuthServiceFactory

All quality gates pass. Coverage unchanged (1800 tests, 0 failures).

5 days agochore(deps): update all non-major dependencies (#1716)
renovate[bot] [Fri, 13 Mar 2026 15:54:30 +0000 (16:54 +0100)] 
chore(deps): update all non-major dependencies (#1716)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
5 days agofeat(ocpp2): integrate all 8 missing OCPP 2.0.1 outgoing commands into UIService...
Jérôme Benoit [Fri, 13 Mar 2026 15:54:08 +0000 (16:54 +0100)] 
feat(ocpp2): integrate all 8 missing OCPP 2.0.1 outgoing commands into UIService/BroadcastChannel pipeline (#1718)

* feat(types): add OCPP 2.0.1 UI procedure and broadcast channel enums

- Add 8 new ProcedureName entries (get15118EVCertificate, getCertificateStatus, logStatusNotification, notifyCustomerInformation, notifyReport, securityEventNotification, signCertificate, transactionEvent)
- Mirror same 8 entries in BroadcastChannelProcedureName
- Extend BroadcastChannelRequestPayload with optional OCPP 2.0.1 fields (eventType, evseId, idToken, transactionData)

* feat(ui-service): wire OCPP 2.0.1 commands through UI service pipeline

Add 8 new ProcedureNameToBroadCastChannelProcedureNameMapping entries for:
get15118EVCertificate, getCertificateStatus, logStatusNotification,
notifyCustomerInformation, notifyReport, securityEventNotification,
signCertificate, transactionEvent

* feat(broadcast-channel): add OCPP 2.0.1 command handlers and response mapping

- Add 8 new command handlers: get15118EVCertificate, getCertificateStatus,
  logStatusNotification, notifyCustomerInformation, notifyReport,
  securityEventNotification, signCertificate, transactionEvent
- Update CommandResponse type union with 4 OCPP 2.0.1 response types
- Add 4 commandResponseToResponseStatus cases for non-empty responses
- Fix METER_VALUES handler: add OCPP 2.0.1 passthrough branch (version-detected)
- Fire-and-forget commands handled automatically by isEmpty bailout

* fix(ocpp2): add missing certificate commands to buildRequestPayload

Add SIGN_CERTIFICATE, GET_15118_EV_CERTIFICATE, GET_CERTIFICATE_STATUS
to the passthrough case group — these previously fell to the default
branch which threw OCPPError(NOT_SUPPORTED)

* test(broadcast-channel): add OCPP 2.0.1 UIService pipeline integration tests

Add 45 tests across 6 groups covering:
- ProcedureName enum: 8 new OCPP 2.0.1 entries
- BroadcastChannelProcedureName enum: 8 new OCPP 2.0.1 entries
- ProcedureNameToBroadCastChannelProcedureNameMapping: 8 new entries
- BroadcastChannelRequestPayload: OCPP 2.0.1 optional fields
- commandResponseToResponseStatus: 4 new command response cases
- commandHandlers Map: 8 new entries registered

* style: fix perfectionist ordering in broadcast channel handler and tests

* [autofix.ci] apply automated fixes

* test(broadcast-channel): add buildRequestPayload and behavioral handler tests

Add Group 7 (buildRequestPayload certificate passthrough) and Group 8
(commandHandlers behavioral) tests addressing audit findings:

- 3 tests verify GET_15118_EV_CERTIFICATE, GET_CERTIFICATE_STATUS, and
  SIGN_CERTIFICATE payloads pass through unchanged
- 8 tests verify each OCPP 2.0.1 handler invokes requestHandler with the
  correct RequestCommand enum value

Total: 56 tests in file, 1816 across suite.

* [autofix.ci] apply automated fixes

* refactor(test): remove redundant test groups from broadcast channel tests

Remove Group 4 (BroadcastChannelRequestPayload tautological - type system
already enforces at compile time) and Group 6 (commandHandlers existence -
fully subsumed by Group 8 behavioral tests that verify both existence and
correct RequestCommand routing).

45 tests across 6 groups, 1805 total suite.

* fix: address PR review comments — tighten types, add fire-and-forget response cases, barrel export

- Add explicit commandResponseToResponseStatus cases for 4 fire-and-forget
  commands (LogStatusNotification, NotifyCustomerInformation, NotifyReport,
  SecurityEventNotification) using isEmpty pattern matching existing
  MeterValues/StatusNotification behavior
- Tighten BroadcastChannelRequestPayload types: idToken uses
  OCPP20IdTokenType, rename transactionData to transactionInfo with
  OCPP20TransactionType per OCPP 2.0.1 spec
- Add OCPP20AuthorizationStatusEnumType to barrel export in types/index.ts,
  switch deep imports to barrel imports in source and test files
- Add 8 tests for fire-and-forget commandResponseToResponseStatus coverage
- Fix cspell warnings in test mock data strings

* test: improve OCPP 2.0.1 broadcast channel coverage with dispatch and pipeline tests

Add commandHandler dispatch pipeline tests (Group 6) and requestHandler
full pipeline tests (Group 7) to exercise handler closure bodies through
the actual dispatch path. Convert Group 6 from direct Map.get() to
commandHandler() method dispatch for better V8 coverage tracking.

59 tests across 7 groups (was 53 tests across 6 groups).

* refactor(broadcast-channel): extract handler closures to named private methods

Extract 17 async handler closures from the Map constructor into named
private methods with .bind(this) references. This improves V8 coverage
tracking accuracy for arrow function closures inside Map constructor
arrays, where tsx/esbuild source map misalignment causes inconsistent
coverage attribution.

No behavioral changes — all handlers execute the same logic.

* [autofix.ci] apply automated fixes

* test(broadcast-channel): add mapping completeness validation for UI service command pipeline

* [autofix.ci] apply automated fixes

* refactor(types): remove unused OCPP 2.0 fields from BroadcastChannelRequestPayload

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
5 days agotest(ocpp): add schema registration coverage tests for OCPP 1.6 and 2.0
Jérôme Benoit [Thu, 12 Mar 2026 23:04:47 +0000 (00:04 +0100)] 
test(ocpp): add schema registration coverage tests for OCPP 1.6 and 2.0

Verify every command enum value has its request and response schemas
registered in ServiceUtils, and that all referenced schema files exist
on disk. Prevents regressions where a command is implemented but its
schema registration is forgotten.

5 days agofix(ocpp2): add missing outgoing schema entries for certificate commands
Jérôme Benoit [Thu, 12 Mar 2026 22:56:04 +0000 (23:56 +0100)] 
fix(ocpp2): add missing outgoing schema entries for certificate commands

Register Get15118EVCertificate, GetCertificateStatus, and SignCertificate
in OCPP20ServiceUtils.outgoingRequestSchemaNames to ensure JSON schema
validation for these certificate management commands.

5 days agorefactor(ocpp): remove dead parseJsonSchemaFile overrides and standardize validation
Jérôme Benoit [Thu, 12 Mar 2026 22:42:20 +0000 (23:42 +0100)] 
refactor(ocpp): remove dead parseJsonSchemaFile overrides and standardize validation

- Delete unused parseJsonSchemaFile overrides from OCPP16/20ServiceUtils
  (createPayloadValidatorMap already passes version via options)
- Remove orphaned JSONSchemaType and JsonType imports
- Standardize null-validator return to false in OCPPRequestService
  (consistent with OCPPIncomingRequestService and OCPPResponseService)
- Change validateRequestPayload and validateIncomingRequestResponsePayload
  visibility from private to protected (consistent with other base classes)

5 days agorefactor(ocpp): harmonize JSON schema payload validation across OCPP stacks
Jérôme Benoit [Thu, 12 Mar 2026 22:31:30 +0000 (23:31 +0100)] 
refactor(ocpp): harmonize JSON schema payload validation across OCPP stacks

- Absorb missing-validator null guard into base class methods, eliminating
  4 identical private validatePayload wrappers and their bind() calls
- Remove dead code (unreachable isValid branches in ResponseService wrappers)
- Make parseJsonSchemaFile throw on failure instead of silently returning {},
  preventing validators that accept everything as a security bypass
- Add dual-path schema resolution for production (esbuild) and test (tsx)
- Align OCPP 1.6 schema map construction with OCPP 2.0 registry pattern,
  replacing ~200 lines of verbose inline entries with declarative registries
- Collapse 4 identical payload options factory methods into 1 per version

5 days agorefactor(ocpp2): convert incoming request fire-and-forget patterns to event listeners...
Jérôme Benoit [Thu, 12 Mar 2026 21:43:33 +0000 (22:43 +0100)] 
refactor(ocpp2): convert incoming request fire-and-forget patterns to event listeners (#1715)

* refactor(ocpp2): convert UpdateFirmware fire-and-forget to event listener

* refactor(ocpp2): convert GetLog fire-and-forget to event listener

* refactor(ocpp2): convert CustomerInformation fire-and-forget to event listener

* refactor(ocpp2): convert TriggerMessage fire-and-forget to event listener

* [autofix.ci] apply automated fixes

* test(ocpp2): improve TRIGGER_MESSAGE listener test coverage for SonarCloud

* test(ocpp2): remove 10 redundant handler tests that add no branch coverage

* test(ocpp2): remove 5 project-wide redundant tests identified by cross-validated audit

* refactor(ocpp2): extract StatusNotification trigger methods to reduce cognitive complexity

* [autofix.ci] apply automated fixes

* test(ocpp2): strengthen StatusNotification trigger assertions with payload and options validation

* [autofix.ci] apply automated fixes

* fix(ocpp2): relocate B11.FR.01 spec reference to correct surviving test

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
6 days agofeat(ocpp2): OCPP 2.0.1 Core certification readiness (#1712)
Jérôme Benoit [Thu, 12 Mar 2026 18:57:32 +0000 (19:57 +0100)] 
feat(ocpp2): OCPP 2.0.1 Core certification readiness (#1712)

* feat(ocpp2): add type definitions and validator configs for OCPP 2.0.1 Core certification commands

* feat(ocpp2): implement DataTransfer reject handler

* feat(ocpp2): implement SetNetworkProfile reject handler

* feat(ocpp2): implement GetTransactionStatus handler

* feat(ocpp2): implement CustomerInformation clear handler

* feat(ocpp2): implement SecurityEventNotification outgoing command

* fix(ocpp2): handle TransactionEvent response idTokenInfo status

* feat(ocpp2): implement ChangeAvailability handler

* feat(ocpp2): implement standalone MeterValues outgoing command

* feat(ocpp2): implement UpdateFirmware and FirmwareStatusNotification

Add UpdateFirmware (CSMS→CS) incoming request handler with simulated
firmware update lifecycle state machine (Downloading → Downloaded →
SignatureVerified → Installing → Installed) and FirmwareStatusNotification
(CS→CSMS) outgoing command.

- Handle UPDATE_FIRMWARE incoming request, return Accepted status
- Simulate firmware update lifecycle via chained setTimeout calls
- Send FirmwareStatusNotification at each state transition
- Check firmware.signature presence for SignatureVerified state
- Add testable interfaces for both handler and request service
- Add 8 tests (5 for UpdateFirmware, 3 for FirmwareStatusNotification)

* feat(ocpp2): implement GetLog and LogStatusNotification

- Add GetLog incoming request handler with simulated upload lifecycle
  (Uploading → Uploaded via chained setTimeout)
- Add LogStatusNotification outgoing command in RequestService
- Register handleResponseLogStatusNotification in ResponseService
- Update testable interfaces with new handler and request method
- Add 4 GetLog tests (DiagnosticsLog, SecurityLog, requestId, retries)
- Add 3 LogStatusNotification tests (Uploading, requestId, empty response)
- All quality gates pass: lint, typecheck, build, 1737 tests

* feat(ocpp2): expand TriggerMessage handler with new trigger types

* docs: update README with OCPP 2.0.1 Core certification commands

* style(ocpp2): fix space-before-function-paren in TriggerMessage handler and test

* fix(ocpp2): add missing case branches in buildRequestPayload for new commands

buildRequestPayload throws NOT_SUPPORTED for FirmwareStatusNotification,
LogStatusNotification, MeterValues, NotifyCustomerInformation, and
SecurityEventNotification since they have no case branches. This causes
guaranteed runtime failures when TriggerMessage invokes requestHandler
for these commands.

Add pass-through case branches matching the existing pattern used by
other notification commands.

* fix(ocpp2): use zero-based seqNo in NotifyCustomerInformation per OCPP 2.0.1 spec

* fix(ocpp2): stop only specific transaction in handleResponseTransactionEvent

handleResponseTransactionEvent was stopping ALL active transactions when
any rejected idTokenInfo.status arrived. Per OCPP 2.0.1 spec (D01/D05),
only the specific transaction referenced by the TransactionEvent request
should be stopped.

Extract the transactionId from the request payload and use
getConnectorIdByTransactionId/getEvseIdByTransactionId to find and stop
only the affected transaction.

* fix(ocpp2): set idle EVSEs Inoperative immediately on CS-level ChangeAvailability per G03.FR.04

* fix(ocpp2): add firmware lifecycle delay, JSDoc, and consistent handler patterns

- Add delay between Downloaded and SignatureVerified in firmware update lifecycle per J01
- Add missing JSDoc for requestLogStatusNotification in testable interface
- Convert 4 arrow function handlers to regular methods for consistency with pre-existing handlers

* refactor(ocpp2): resolve SonarCloud quality gate findings

- Replace duplicated delay() functions with shared sleep() utility
- Extract handleEvseChangeAvailability and handleCsLevelInoperative to
  reduce cognitive complexity of handleRequestChangeAvailability
- Extract hasAnyActiveTransaction to eliminate nested loops
- Fix negated conditions and nested template literals

* refactor(ocpp2): deduplicate validator configs via shared schema name maps

- Extract incomingRequestSchemaNames and outgoingRequestSchemaNames as
  single source of truth for command-to-schema mappings
- Generate request/response configs from shared maps, eliminating 96
  lines of structural duplication flagged by SonarCloud
- Fix remaining negated conditions in ternary expressions

* fix(test): deduplicate MeterValues call, add multi-EVSE isolation test

* fix(ocpp2): eliminate double status notification, document messagesInQueue

* test(ocpp2): align test files with TEST_STYLE_GUIDE conventions

* fix: comply with E14.FR.06 and harmonize statusInfo

7 days agochore: update serena project configuration
Jérôme Benoit [Wed, 11 Mar 2026 12:33:00 +0000 (13:33 +0100)] 
chore: update serena project configuration

Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
7 days agochore(deps): lock file maintenance (#1707)
renovate[bot] [Wed, 11 Mar 2026 11:42:04 +0000 (12:42 +0100)] 
chore(deps): lock file maintenance (#1707)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>