mirror of
https://github.com/block/buzz.git
synced 2026-11-12 04:12:21 +00:00
Two P2 review findings on #6742, both reproduced from an out-of-crate consumer
before being fixed, and each fix falsified by reverting it.
1. Identity spellings broke correlation (bugs-00, correlate.rs:42).
`channel()` validated a UUID with `Uuid::parse_str` and returned the caller's
spelling untouched, while `correlate_identities` compared the request's and the
outcome's `channelId` as strings. An uppercase request against a host's
canonical lowercase echo of the same channel therefore failed `validate_for` —
a *correct* answer rejected, which is worse than the mismatch the check exists
to catch, because it makes a working host unusable.
Fixed as a class, not as a UUID case. The finding named case; the validator
also admits the unhyphenated, `{braced}`, and `urn:uuid:` forms, and all four
froze un-canonicalized. Hex has the same property. So:
- Canonicalize where a value enters. `channel()` now returns the lowercase
hyphenated form, and every member holding an identity canonicalizes on
deserialize too (`channel_id`, `hex64_field`, `absent_or_valued_hex64`). The
wire is the door no `validated()` covers: those fields are public `String`s,
so a parsed payload reaches a caller without passing through any validator.
Both doors delegate to the same function, so they cannot drift.
- Compare parsed identities, not bytes. `correlate_identities` parses each
`channelId` as a `Uuid`; pubkeys are compared as `PubkeyHex`, which
lowercases in its only constructor and its serde path.
The two guards are deliberately independent and asserted separately, so neither
is the only thing holding. A genuinely different identity is still rejected.
Coverage is derived as well as enumerated: one test walks the real fixtures,
re-spells every identity-named member, and requires the canonical value back,
so a member added later with the wrong `deserialize_with` fails without anyone
extending a list.
2. `BrokerResult` was a second, lax wire door (bugs-00, mod.rs:586).
The envelope's strict reader requires the exact key set the declared status
admits, but the exported result type also derived its own reader, and that one
accepted and dropped arbitrary siblings: `status: failed` beside an `error` and
a `secretKey`, or a succeeded result beside an `error`. A consumer parsing the
result type directly got an `Ok` value whose complete wire shape had never been
vetted, from bytes that fail through the envelope.
Decision: take the type off the wire — it is no longer `Deserialize`. The
alternative, its own strict reader, would be a second copy of the per-status
contradiction rules, and two copies of a strictness check drift; this hole
exists precisely because one layer's strictness did not reach another's.
Removing the door leaves one implementation and nothing to keep in sync.
Nothing is lost: a bare `{"status": …}` object is not a payload this contract
defines, and the whole module is new in this PR, so it had no consumers.
`Serialize` is retained — it produces the envelope's flattened wire form — so
the wire form is unchanged and this is a read-side restriction only.
The absence is pinned at compile time, since a runtime test cannot call a
`Deserialize` impl that does not exist: a probe resolves to an inherent method
only when the bound holds, and it is first asserted against two types that *do*
have readers, so a broken probe cannot pass silently.
The contract docs gain a section on identity spelling, `validate_for`'s
nine-row table records that comparison is on parsed identities, and the
strictness section records the one-door rule.
Also: adding the identity docs pushed mod.rs to 1,021 lines, past the repo's
1,000-line ceiling, so the strict response reader moved to `broker/wire.rs`
(mod 913 / wire 121) — a pure move, same treatment `correlate.rs` already got.
The derived-coverage test matches identity members case-insensitively. An
earlier revision of it matched the suffix `"EventId"` exactly, which silently
skipped the outcome member spelled `eventId` and left every response-side wire
door unpinned — mutation testing caught it by removing the outcome `channelId`
door and surviving. It now walks response fixtures as well as request ones, and
floors the two directions separately, because a single combined floor would be
satisfied by the request side alone.
Validation: 299 tests pass in buzz-sdk (295 before), workspace clippy `-D
warnings` clean, fmt clean, `cargo doc` warning-free with the new section anchor
verified in the generated HTML, every touched file under 1,000 lines. Seven
mutations, one per guard, each killed by a named test. `cargo test --workspace`
is green except `test_120s_timeout` / `test_cancellation_immediate` in
buzz-pair-relay, a pre-existing flake reproduced 2-in-8 on unmodified main at
c5166f216 where no broker module exists.
Signed-off-by: ss-dev-00 <a02c4e0850e5e612b4ddf95dbe2f5c56467cf27c6552203bc833ff438fb31971@buzz.block.builderlab.xyz>
Co-authored-by: Bradley Axen <baxen@squareup.com>
Signed-off-by: Bradley Axen <baxen@squareup.com>
Signed-off-by: ss-dev-00 <a02c4e0850e5e612b4ddf95dbe2f5c56467cf27c6552203bc833ff438fb31971@buzz.block.builderlab.xyz>