fix(validation): accept 1-decimal charging limits under multipleOf:0.1 (Issue #18)#34
Merged
Merged
Conversation
…1 (Issue #18) jsonschema 0.17's MultipleOfFloatValidator checks `multipleOf` on raw f64 bit values, so a valid one-decimal limit like 21.4 (stored as 21.3999999...) is wrongly rejected against `multipleOf: 0.1` in SetChargingProfile, RemoteStartTransaction, and GetCompositeScheduleResponse. Port the Python reference's decimal.Decimal re-parse (ocpp/messages.py::_validate_payload) by dropping multipleOf errors that are exact decimal multiples, decided on the shortest round-tripping decimal string of each f64 via integer mantissa/scale arithmetic. Genuine violations (e.g. 4.11) and every other keyword error are still reported; any unparsable number fails safe (keeps the error). Closes #18 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This was referenced Jun 14, 2026
This was referenced Jun 14, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
jsonschema0.17'sMultipleOfFloatValidatorchecksmultipleOfon raw f64bit values:
(item / 0.1) % 1.0 < f64::EPSILON. A valid one-decimal charginglimit like
21.4is stored as21.39999999999999857…, so21.4 / 0.1 = 213.9999…, remainder≈0.9999— wrongly rejected. This hits the three OCPP1.6J schemas that carry
multipleOf: 0.1:SetChargingProfile(CALL)RemoteStartTransaction(CALL)GetCompositeSchedule(CALLRESULT)Before this PR the two ported reference tests sidestepped the bug with integer
limits (
21.0,15.0) and a "known gap" note; now they use the real referencevalues (
21.4,15.2).What changed
run_validationdropsmultipleOferrors that are pure f64 representationartifacts and keeps everything else. "Artifact" is decided exactly (not by a
fuzzy tolerance) on the shortest round-tripping decimal string of each f64, via
integer mantissa/scale arithmetic:
This is the Rust counterpart of the Python reference's
decimal.Decimalre-parse: same verdict, no global feature flip.
Ported from
ocpp/messages.py—_validate_payload()(the# 3 OCPP 1.6 schedules have fields of type floatsblock)tests/test_messages.py—test_validate_set_charging_profile_payload(21.4),test_validate_get_composite_profile_payload(15.2)Scope / blast radius
multipleOferrors are ever forgiven.type,required,additionalProperties,enum,minimum, … are untouched.i128overflow / exponent-form / zero divisor-> keep the error (never accept a payload we can't prove valid).
crates/ocpp-messages/src/schema_validation.rs). No new deps, noserde_json/arbitrary_precision(which is workspace-wide and would risk thedispatcher's typed
from_value).Test plan
cargo fmt --check,cargo clippy --all-targets -- -D warnings,cargo test --workspace(235 passed, 0 failed), andRUSTDOCFLAGS="--deny warnings" cargo doc --workspace --no-deps --document-private-itemsall green locally.
Tests added / updated:
validate_set_charging_profile_with_decimal_limit_passes—21.4validate_get_composite_schedule_response_with_decimal_limit_passes—15.2validate_remote_start_transaction_with_decimal_limit_passes—21.4(3rd affected action, newly covered)validate_set_charging_profile_rejects_two_decimal_limit—4.11still rejectedprecision_filter_does_not_mask_sibling_errors— a genuine type error next to a forgivable21.4still failsexact_decimal_multiple_*/decimal_mantissa_scale_*— helper unit tests (one-decimal accepted, finer precision rejected, integer/zero divisors, NaN/Inf)Heads-up: a second implementation of #18 already exists
While picking this up I found an orphan branch
nightly/2026-06-14-issue-18(commit
b2bfae2, a prior nightly run at 00:26 UTC) that fixes the same issuebut never had a PR opened. It also passes its tests. The approaches differ:
b2bfae2(alternative)multipleOferrors after validationround(x/d)*dbefore validationdecimal.Decimal)1e-9tolerance + empirically-exactN*0.1IEEE-754 reconstructionmain(incl. #32)main(pre-#32)I went with the error-filter because it never touches the payload, is exact
rather than tolerance-based, and is the faithful analogue of the Python
reference. Your call which to merge — if you prefer
b2bfae2, close this andI'll open a PR for that branch instead. Either way the other branch should be
deleted (I left
b2bfae2untouched rather than rewrite a branch from anotherrun).
Notes for review
multipleOf, not scoped tothe 3 actions like the Python version. This is strictly safe: the exact check
still rejects genuine violations, including integer
multipleOf(e.g.3.5vs
multipleOf: 1).serde_json/arbitrary_precision(global blastradius) and a fuzzy float tolerance (magnitude-dependent boundary).
Closes #18
🤖 Generated with Claude Code