feat(assert): assertion macros for JSON-RPC test bodies (#8) #26
No reviewers
Labels
No labels
area:assertions
area:cli
area:client
area:harness
area:meta
area:reporting
area:runner
type:user-story
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
charles/ws-rpc-test!26
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/8-assertion-macros"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #8. Stacked on #25.
Summary
13
#[macro_export]assertion macros insrc/assert.rs. All returnErr(TestError::Assertion { expected, got, context, location })from the enclosing function on failure, so they must be invoked inside a function returningTestResult. Each captures the source location viaconcat!(file!(), ":", line!()).Macros
Value / field
assert_ok!(v [, ctx])— fails ifv.errorexistsassert_field!(v, k, expected)— single-keyassert_field!(v, k1, k2, expected)— 2-level nestedassert_field!(v, k1, k2, k3, expected)— 3-level nestedassert_field_exists!(v, field [, ctx])assert_field_absent!(v, field [, ctx])assert_field_type!(v, field, "string"|"number"|"bool"|"array"|"object"|"null" [, ctx])Array
assert_array!(v, range [, ctx])— anyRangeBounds<usize>(1..,..=5,3..=3, etc.)assert_array_contains!(v, pred [, ctx])assert_array_all!(v, pred [, ctx])Numeric
assert_in_range!(v, range [, ctx])—RangeBounds<f64>assert_greater_than!(v, n [, ctx])assert_less_than!(v, n [, ctx])Error
assert_error!(v, code [, ctx])assert_error_contains!(v, needle [, ctx])assert_field!arity trade-offThe spec asks for both variadic nested paths AND an optional trailing context string. These are fundamentally ambiguous in
macro_rules!:Both match
macro_rules!patterns with equal strength. I chose to support nested paths (explicit spec requirement from §3) and drop ctx support specifically onassert_field!. Everywhere else the optional, "ctx"still works. A design note in the macro doc comment tells users to useassert_field_exists!or another macro if they need context in that spot.Failure rendering
serde_json::to_string_prettywith aDebugfallback.contextis anOption<String>populated from the trailing arg.locationisconcat!(file!(), ":", line!())captured at the macro call site.Checklist (from issue #8)
#[macro_export]; early-returnErr(TestError::Assertion { .. })concat!(file!(), ":", line!()), "ctx"on all macros exceptassert_field!(documented trade-off)assert_ok!/assert_field!(1/2/3 nesting levels) — resolves spec review §3assert_field_exists!,assert_field_absent!,assert_field_type!assert_array!with anyRangeBounds<usize>;assert_array_contains!,assert_array_all!assert_in_range!,assert_greater_than!,assert_less_than!assert_error!,assert_error_contains!$crate::pathsTest plan
src/assert.rscovering passing + failing paths for each macroassert_ok_supports_context_messageverifies ctx populatesTestError::Assertion.contextjust qagreen locally — 62/62 unit testsNotes for the reviewer
$crate::assert::__private::*helpers for the pretty printer and type-name lookup. The__privatemodule is#[doc(hidden)]so it doesn't appear in rustdoc's public API.assert_array!andassert_in_range!fully-qualify::core::ops::RangeBoundscalls so users don't need an explicituseat the call site.::core::/::std::prefixes to avoid colliding with re-declared items in the caller's module.assert_fieldpattern order is Level 3 → Level 2 → Level 1 so 5-arg invocations match the longest path first (otherwise(v, k1, k2, k3, expected)would try to match L2 withexpected=k3andctx=expected— which is exactly what was failing during development).Review — assertion macros pour JSON-RPC (#8)
Limitation documentée de
assert_field!Le macro ne peut pas accepter un contexte string à cause de l'ambiguïté avec les key paths. C'est documenté explicitement dans le doc-comment. Acceptable pour v0.1, mais les utilisateurs qui veulent un contexte sur des assertions imbriquées devront envelopper dans une variable intermédiaire ou utiliser
assert_field_exists!.assert_field_exists!— traitenullcomme absentUn champ JSON présent avec une valeur
nullest considéré "absent". C'est une décision de design raisonnable pour des APIs JSON-RPC qui utilisentnullcomme absence, mais elle devrait être documentée — quelqu'un qui veut vérifier qu'un champ existe avec la valeur null explicite sera surpris.assert_array!— format des bornes dans le message d'erreurDebugsurBound<usize>afficheIncluded(3)et non3. Le message d'erreur sera"r length in Included(3)..Included(3)"là où3..=3serait attendu. Cosmétique mais pas idéal pour la lisibilité.Module
__privateBon pattern — exposer
prettyettype_namepour les macros sans les mettre dans la surface API publique.Tests
assert_ok!— vérifie que le champcontextest bien propagé.Aucun bloquant.
✅ Pas de bloquant. Points à documenter :
assert_field_exists!considèrenullcomme absent ;assert_array!affiche les bornes en formatIncluded(n)dans les messages d'erreur.