]> Piment Noir Git Repositories - e-mobility-charging-stations-simulator.git/commit
feat(simulator): add forceTransactionOnInvalidIdToken template flag (#1907)
authorJérôme Benoit <jerome.benoit@piment-noir.org>
Wed, 17 Jun 2026 16:49:22 +0000 (18:49 +0200)
committerGitHub <noreply@github.com>
Wed, 17 Jun 2026 16:49:22 +0000 (18:49 +0200)
commite12ddd9945822e2e028c13f7fb079634219c5fe5
treedde5956ad490f297d9d2730ada43c6a0ac117191
parent11aac65d02feddb039641053484bca92b0921146
feat(simulator): add forceTransactionOnInvalidIdToken template flag (#1907)

* feat(simulator): add forceTransactionOnInvalidIdToken template flag

Allow station-initiated transactions to continue even when CSMS replies
with a non-Accepted IdToken status (OCPP 1.6 idTagInfo.status, OCPP 2.0.1
idTokenInfo.status). Default false preserves spec-compliant behavior.

The flag is non-spec-compliant by design (violates OCPP 2.0.1 E05.FR.09,
E05.FR.10, E06.FR.04 when enabled) and is intended for testing edge-case
charging station implementations that ignore CSMS rejection. JSDoc on the
field warns explicitly; README entry flags it as a non-spec-compliant test
override.

Scope in OCPP 2.0.1 is limited to TransactionEvent eventType=Started;
mid-transaction revocation (Updated/Ended event types) still triggers
deauthorization regardless of the flag. The OCPP 2.0.1 \`StopTxOnInvalidId\`
device-model variable is left untouched — the template flag short-circuits
ahead of the variable's accounting branch.

Tests: 16 new test cases across both OCPP namespaces covering force-on/off,
mid-tx revocation preservation, override marker log presence, auth cache
non-relaxation, pre-Start guard preservation, and full status-enum parity
(8 OCPP 2.0.1 statuses).

Closes #1826

* fix(types): restore fixedName and pin endedConnector non-null

The forceTransactionOnInvalidIdToken JSDoc rewrite in 73e3358a accidentally
deleted the adjacent fixedName?: boolean field on ChargingStationTemplate,
breaking 30+ TS errors in CI (ChargingStationNameTemplate Pick keyed on
fixedName). Local pnpm test does not run tsc --noEmit, so the regression
was invisible until CI.

Also fix a CI-only error in OCPP20ResponseService-ForceTxOnInvalid.test.ts:
endedConnector is typed as ConnectorStatus | undefined; replace the
optional-chain assertions with an explicit if/fail guard so TS narrows
the type for the subsequent strict-equal assertions.

* test(2.0): document deferred MV-pump fence and ConcurrentTx scope

Phase-4 review-C MINOR-1: the 2.0-T2 test asserts that
\`startUpdatedMeterValues\` is called but stubs the helper, so a wire-level
regression where the interval binds to the wrong connector would not be
caught. Phase 6 golden-set with the live mock CSMS will close that gap;
add a TODO comment to make the deferral explicit.

Phase-4 review-C NIT-1: the T7 enum-parity loop omits ConcurrentTx; this
is correct (ConcurrentTx is not an IdToken rejection in OCPP 2.0.1) but
deserves an inline comment to prevent a future contributor from adding it
naively.

* test(ocpp): tighten forceTransactionOnInvalidIdToken coverage and clarify docs

Follow-up to #1907 applying post-review fixes from a 3-angle audit
(production / types-schema-readme / tests-adversarial).

Production (OCPP 2.0):
- Simplify defensive `else if (overrideRejection && status !== Accepted)`
  to `else if (overrideRejection)` in handleResponseTransactionEvent;
  the second predicate is entailed by the enclosing if/else.
- Trim duplicated 5-line comment block down to a 2-line pointer to the
  canonical JSDoc on ChargingStationTemplate.forceTransactionOnInvalidIdToken.

Documentation:
- ChargingStationTemplate JSDoc: disambiguate from the OCPP variables
  StopTransactionOnInvalidId (1.6) and StopTxOnInvalidId (2.0.1) which
  control mid-transaction stop on revocation and have inverse polarity;
  state independence from ocppStrictCompliance.
- README row: same disambiguation appended to the cell.

Tests (OCPP 1.6 ForceTxOnInvalid):
- T5 (pre-Start guard): change response status from Accepted to Invalid
  so the test exercises the flag-vs-guard interaction it claims to lock.
- T6 (new): status-enum parity loop via Object.values(OCPP16AuthorizationStatus)
  excluding Accepted and ConcurrentTx (3 instances: Blocked, Expired, Invalid).
- T2: replicate the Phase-6 fake-timer-fence TODO from the sibling 2.0 file
  (MV pump observability disclosure).
- Rename test titles to should [verb] per tests/TEST_STYLE_GUIDE.md \xa71.

Tests (OCPP 2.0 ForceTxOnInvalid):
- buildTransactionEventRequest: add optional idToken parameter to exercise
  the auth-cache update path at handleResponseTransactionEvent.
- T8 (new): C10.FR.01/04/05 auth-cache invariant test, parametric over
  [flag=true, flag=false] (2 instances) asserting updateAuthorizationCache
  is called with the supplied idToken and the CSMS-replied idTokenInfo.
- T4 (Ended): add deauth call-count assertion (=== 0); locks the
  cleanup-runs-before-deauth-gate ordering invariant. The deauth path is
  reached but no-ops because cleanupEndedTransaction clears transactionId
  first, so getConnectorIdByTransactionId returns null. A regression that
  reorders cleanup after the gate (or preserves transactionId on Ended)
  flips this to 1.
- T7 (parity): replace static 8-element array with
  Object.values(OCPP20AuthorizationStatusEnumType).filter(...). Same 8
  instances today; future enum additions are auto-covered.
- T6: split into 2 it() blocks (flag-on / flag-off) eliminating the
  mid-test standardCleanup+remock pattern. Each block additionally asserts
  that the override-marker warn-log is NOT emitted on null idTokenInfo,
  locking the invariant against an A6-style regression where the
  override-marker else-if is hoisted outside the outer null guard.
- Rename test titles to should [verb].

Verification:
- pnpm typecheck exit 0
- pnpm lint exit 0
- pnpm test: 0 fail across the full suite
- 23/23 GREEN on the two ForceTxOnInvalid files (was 17/17; +6 new)
- Three mutation experiments confirm regression-detection strength:
  * Drop `|| forceTransactionOnInvalidIdToken` from OCPP16 gate (line 427):
    T2 + 3 parity tests fail with `false !== true` on transactionStarted.
  * Replace `if (requestPayload.idToken != null)` with `if (false)` in
    OCPP20 cache update (line 519): T8 (both flag states) fails `0 !== 1`.
  * Bypass the OCPP16 unauthorized-remote-start guard (lines 315-329):
    T5 only fails (regression-localized to that scenario).

* docs(ocpp): note ocppStrictCompliance independence and clarify Started log

Follow-up to review v2 of #1907 closing the two actionable findings
(B4 Low, N1 nit). Two remaining v2 nits (B5 multi-line JSDoc, R3 test
cast) are intentionally deferred — rationale in
/tmp/pr-1907-review-v2/fixes-design.md.

B4 — ChargingStationTemplate.forceTransactionOnInvalidIdToken JSDoc and
the matching README row both gain a one-clause disambiguation: "Independent
of `ocppStrictCompliance` (operates on response handling, not schema
validation)". Mirrors the pattern used by the sibling `outOfOrderEndMeterValues`
row (which cites ITS `ocppStrictCompliance` coupling), preventing readers
from inferring a non-existent coupling for the new flag.

N1 — OCPP20ResponseService.handleResponseTransactionEvent override warn
log now reads "...on eventType=Started despite..." (was "...on Started
despite..."). The `eventType=` prefix removes ambiguity with OCPP
connector state names (Charging, Available) and aligns the log vocabulary
with the JSDoc, the inline reference comment, and the test fixture
(`requestPayload.eventType === OCPP20TransactionEventEnumType.Started`).
The override-marker substring `forceTransactionOnInvalidIdToken=true` is
preserved verbatim, so the four test assertions that grep for it remain
unaffected.

Verification:
- pnpm typecheck exit 0
- pnpm lint exit 0
- pnpm test: 0 fail across the full suite
- 23/23 GREEN on the two ForceTxOnInvalid files (count unchanged; doc-only changes)
- README table re-aligned by prettier on save (column padding shifted
  421 -> 460 chars to fit the longer description); diff is mechanical
  whitespace, content change is one clause.
README.md
src/charging-station/TemplateSchema.ts
src/charging-station/ocpp/1.6/OCPP16ResponseService.ts
src/charging-station/ocpp/2.0/OCPP20ResponseService.ts
src/types/ChargingStationTemplate.ts
src/utils/Constants.ts
tests/charging-station/ocpp/1.6/OCPP16ResponseService-ForceTxOnInvalid.test.ts [new file with mode: 0644]
tests/charging-station/ocpp/2.0/OCPP20ResponseService-ForceTxOnInvalid.test.ts [new file with mode: 0644]