Rust SDK: wire-level tests, generator fixtures and the pedantic pass

Merged
JE
2mo ago
+6722-326

Stacked on the generator PR. Tests for the Rust SDK at the wire, golden-output fixtures for the generator, and the clippy-pedantic pass.

Generated per-operation tests. The generator now also emits rust/fizzy-sdk/tests/generated_calls/ — one wiremock test per operation (112) that mounts the path the model names, matches the body and required query parameters the method serializes, answers a schema-valid body and asserts the decoded value. The generic conformance runner sends fixture-authored paths and bodies, so it proves the transport; these prove the generated methods. --check covers both generated directories.

Wire-level tests (rust/fizzy-sdk/tests/*.rs), derived from the conformance fixtures and the Go client: per-route retry budgets (three attempts on 503, exhaustion, Retry-After waited out, Retry-After past the ceiling handed back, 404 and 502 never resent, POST once, idempotent POST/PATCH/DELETE resent, DestroySession once, the client's max_retries as a ceiling, raw-verb defaults and RequestOptions, the policy carried across pages); error mapping with request ids and bodies kept; Link pagination (relative links, cross-origin refused for raw and typed walks, limits, page cap, pages/items streams); HTTPS enforcement, bearer and cookie headers, the User-Agent, redaction; the magic-link cookie round-trip and a resumed flow; hooks (one operation per call, one request per attempt, a refusing gate, the circuit breaker and bulkhead through the client); the account split; the ETag cache; body caps; the route table pinned at 112 routes / 18 services / 9 account-free / 13 paginated; Send + Sync and Clone guarantees.

Generator fixtures (rust/generator/tests/fixtures.rs): a mini model exercising sensitive fields from both spellings, a recursive field, a keyword field, required-vs-optional, array and scalar query parameters, typed path parameters, the account split and retry/pagination facts, asserted against golden output (UPDATE_FIXTURES=1 rewrites after a deliberate change); plus the refusals — an operation no rule covers, a service without a resource type, an operation missing from the behavior model, a redaction entry on a non-sensitive field, two operations landing on one method, an unsupported query serialization, a keyword method name.

Pedantic pass. cargo clippy --workspace --all-targets --all-features -- -D warnings is clean: each allow-list entry carries a reason, poisoned locks are recovered rather than unwrapped, unwrap/expect are warned in library code and allowed per test module. Service structs are BoardsService and friends, as the other SDKs name them.

Local gates on this head: cargo fmt --check, clippy -D warnings, cargo test --workspace (252 passed), cargo doc -D warnings, cargo deny check, generator --check, --no-default-features tests, MSRV 1.88.0 build+tests, cargo publish --dry-run --locked.


Summary by cubic

Adds wire-level tests for all 112 Rust SDK operations, golden generator fixtures, and a clippy-pedantic pass so generated methods and runtime behavior are checked against the wire. Request handling also changes: max_attempts previously counted retries but now counts total sends (three by default), pagination yields each page before fetching the next instead of fetching ahead, and off-origin or credential-bearing URLs are refused before credentials leave the client.

Generated coverage

  • --check validates both generated source and wire-test directories; fixtures cover sensitive and recursive fields, parameter serialization, account scoping, retry and pagination metadata, and generator refusal cases.
  • Handwritten WireMock tests cover retries, errors, pagination, authentication, security, hooks, caching, body limits, account clients, and public trait guarantees.

Runtime hardening

  • Retry budgets respect the client ceiling; cancellations and refused redirects are not retried, and the circuit breaker starts a fresh failure window after closing.
  • Failures retain status, request ID, Retry-After, and a body prefix; oversized responses are rejected, diagnostics redact credentials and bodies, and cookies use Rack-compatible escaping. A body that breaks off is reported as resendable whether or not an attempt remains.
  • Credential-like generated fields now use SensitiveString; raw account verbs include the account path, manual route misuse returns usage errors, and the crate includes its crates.io README.