test(client): happy-path tests for governance mutators (#416)#466
Merged
intendednull merged 1 commit intoApr 28, 2026
Conversation
Adds five tests in crates/client/src/tests/governance.rs covering
the mutators previously only Playwright-covered (or with zero
coverage anywhere):
* propose_grant_admin -> Propose { GrantAdmin }
* propose_revoke_admin -> Propose { RevokeAdmin }
* propose_kick_member -> Propose { KickMember }
* propose_set_threshold -> Propose { SetVoteThreshold }
* delete_role -> DeleteRole
Each test drives the mutator against a `test_client()` fixture
(genesis author = owner = automatic admin) and inspects the
managed DAG to assert the expected EventKind variant and payload.
Downstream materialisation (vote tally, role removal) is tier-1
state-machine territory and stays out of scope, mirroring the
convention from voice.rs (#464).
Refs #416
This was referenced Apr 28, 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.
what
five new tests in
crates/client/src/tests/governance.rs. one per mutator that lacked tier-2 coverage:propose_grant_admin_emits_propose_grant_admin_event-> assertsEventKind::Propose { ProposedAction::GrantAdmin { peer_id } }lands in DAG with right peerpropose_revoke_admin_emits_propose_revoke_admin_event-> assertsEventKind::Propose { ProposedAction::RevokeAdmin { peer_id } }(mutator also fires a follow-upRevokePermission(SendMessages)per its docs; we just assert the propose is there)propose_kick_member_emits_propose_kick_member_event-> assertsEventKind::Propose { ProposedAction::KickMember { peer_id } }propose_set_threshold_emits_propose_set_threshold_event-> assertsEventKind::Propose { ProposedAction::SetVoteThreshold { threshold } }carries the threshold the caller passed (Unanimous)delete_role_emits_delete_role_event-> assertsEventKind::DeleteRole { role_id }carries the requested id, authored by local identityhow
test_client()genesis author = server owner = automatic admin + root-of-trust override. so all five mutators succeed without further permission setup.each test reads the managed DAG via
client.dag_addrand matches the kind variant. why DAG and not wire?test_client()doesn't subscribe to any topic sobroadcast_eventdrops the bytes with a warning. the same signedEventvalue is fed to bothapply_eventandbroadcast_eventin the mutator, so DAG inspection is a faithful proxy.scope is tier-2 only: mutator -> right
EventKind. downstream effects (vote tally, role removal in materialised state) belong in tier-1 state-machine tests; not duplicated here. mirrors the voice.rs (#464) convention.tradeoffs considered
MemHuband intercept the wire broadcast -- rejected. would test serialisation + topic plumbing alongside the mutator. the issue specifically asks to assert the resultingEventKind; DAG inspection isolates that. wire-level path is already covered bymulti_peer_sync.rs.ClientEvent::ProposalCreatedvia broker -- rejected. that variant carriesaction_description: format!("{:?}", action), a stringified debug. matching against debug strings is brittle. matching the typedEventKindenum is precise.CreateRolebeforedelete_role-- rejected for this tier. state-machine acceptsDeleteRoleregardless of whether the role exists; that's tier-1's problem. tier-2 just asserts the event carries the id we asked for.verification
local on worktree (just was available):
cargo test -p willow-client governance-> 5 okcargo fmt --check-> cleancargo clippy --workspace --all-targets -- -D warnings-> cleancargo test --workspace-> all greencargo check --target wasm32-unknown-unknown -p willow-client-> cleanRefs #416
Generated by Claude Code