cd family_wallet
cargo test threshold_change -- --nocapturecargo test test_threshold_change_lower_allows_execution -- --nocapturecargo test -p family_walletcargo test -p family_wallet threshold_change| # | Test Name | Category | Assertion |
|---|---|---|---|
| 1 | test_threshold_change_lower_allows_execution |
Core | Lowering threshold permits execution |
| 2 | test_threshold_change_raise_blocks_execution |
Core | Raising threshold blocks execution |
| 3 | test_threshold_change_raise_to_exact_signature_count |
Edge Case | Exact threshold matching |
| 4 | test_threshold_change_invalid_threshold_exceeds_signer_count |
Boundary | InvalidThreshold error |
| 5 | test_threshold_change_below_minimum |
Boundary | ThresholdBelowMinimum error |
| 6 | test_threshold_change_above_maximum |
Boundary | ThresholdAboveMaximum error |
| 7 | test_threshold_change_quorum_unachievable_via_revalidate |
Quorum | Revalidation with impossible threshold |
| 8 | test_threshold_change_quorum_unachievable_via_member_removal |
Quorum | Member removal impact |
| 9 | test_threshold_change_proposal_invalidated_event_emission |
Event | ProposalInvalidatedEvent emission |
| 10 | test_threshold_change_selective_proposal_invalidation |
Quorum | Selective invalidation logic |
| 11 | test_threshold_change_with_signature_collection_in_progress |
Concurrent | Threshold changes during signing |
| 12 | test_threshold_change_minimum_with_single_signer |
Boundary | Minimum viable config (1 of 1) |
test_threshold_change_{scenario}[_{detail}]
Examples:
test_threshold_change_lower_allows_execution- Lowering threshold scenariotest_threshold_change_raise_blocks_execution- Raising threshold scenariotest_threshold_change_invalid_threshold_exceeds_signer_count- Boundary error scenario
#[test]
fn test_threshold_change_scenario() {
// 1. Setup environment and contract
let env = Env::default();
env.mock_all_auths();
let contract_id = env.register_contract(None, FamilyWallet);
let client = FamilyWalletClient::new(&env, &contract_id);
// 2. Create addresses and init wallet
let owner = Address::generate(&env);
let member1 = Address::generate(&env);
client.init(&owner, &vec![&env, member1.clone()]);
// 3. Configure multisig
let signers = vec![&env, owner.clone(), member1.clone()];
client.configure_multisig(&owner, &TransactionType::..., &threshold, &signers, &limit);
// 4. Execute scenario
let tx_id = client.propose_...(&owner, ...);
client.sign_transaction(&member1, &tx_id);
// 5. Assert results
assert_eq!(...);
}client.init(&owner, &initial_members);client.configure_multisig(
&caller, // Admin or Owner
&TransactionType::..., // Transaction type
&threshold, // Required signatures (u32)
&signers, // Vec of authorized signers
&spending_limit // Spending cap (i128)
);let tx_id = client.withdraw(&proposer, &token, &recipient, &amount);
// OR
let tx_id = client.propose_role_change(&proposer, &target, &new_role);
// OR
let tx_id = client.propose_split_config_change(&proposer, &s, &sv, &b, &ins);client.sign_transaction(&signer, &tx_id);
// Returns: Result<bool, Error>
// - Ok(true) if new signature added and execution triggered
// - Ok(false) if signer already signed (idempotent)
// - Err(error) if signing failslet pending = client.get_pending_transaction(&tx_id);
// Returns: Option<PendingTransaction>
// None = executed/removed, Some = still pendinglet invalidated_count = client.revalidate_proposals(&admin);
// Returns: u32 count of invalidated proposalslet config = client.get_multisig_config(&TransactionType::...);
// Returns: Option<MultiSigConfig>
// Contains: threshold, signers, spending_limitclient.set_role_expiry(&caller, &member, &Some(timestamp));
// Used to simulate member removal/inactivityassert!(client.get_pending_transaction(&tx_id).is_some()); // Still pending
assert!(client.get_pending_transaction(&tx_id).is_none()); // Executedlet pending_tx = client.get_pending_transaction(&tx_id).unwrap();
assert_eq!(pending_tx.signatures.len(), 2); // Check sig countlet token_client = TokenClient::new(&env, &token_contract.address());
assert_eq!(token_client.balance(&recipient), amount); // Verify transferlet result = client.try_configure_multisig(...);
assert_eq!(result, Err(Ok(Error::InvalidThreshold)));
// or for panic-based errors:
#[should_panic(expected = "...")]
fn test_...() { ... }| Error | Value | Condition |
|---|---|---|
InvalidThreshold |
2 | threshold > signer_count |
ThresholdBelowMinimum |
14 | threshold < 1 |
ThresholdAboveMaximum |
15 | threshold > 100 |
QuorumUnachievable |
23 | eligible_signers < threshold (theoretical) |
SignerNotMember |
3 | Signer not in wallet members |
DuplicateSigner |
18 | Same address twice in signers |
SignersListEmpty |
16 | No signers provided |
TooManySigners |
19 | More than MAX_SIGNERS (20) |
Unauthorized |
1 | Caller not Owner/Admin |
✅ Each test is independent - no shared state
✅ All tests use mocked authentication - env.mock_all_auths()
✅ Deterministic - no randomization beyond address generation
✅ Fast - typical execution in milliseconds
✅ Comprehensive - 95%+ coverage of threshold change paths
cargo test test_name -- --nocapture --test-threads=1println!("Pending count: {:?}", client.get_pending_transaction(&tx_id));let config = client.get_multisig_config(&tx_type);
println!("Current threshold: {:?}", config.unwrap().threshold);let config = client.get_multisig_config(&tx_type);
for signer in config.unwrap().signers.iter() {
println!("Authorized: {:?}", signer);
}- Build Time: ~30-60 seconds (first build)
- Test Execution: ~2-5 seconds per test (typical)
- Token Setup: Fastest via
mock_all_auths()and Stellar asset mock - Recommended: Use
--releaseflag for large test runs
| Item | Path |
|---|---|
| Test Suite | family_wallet/src/test.rs (lines 4595-5281) |
| Contract Impl | family_wallet/src/lib.rs |
| Errors Enum | family_wallet/src/lib.rs (lines 280-320) |
| Summary Doc | THRESHOLD_CHANGE_TESTS_SUMMARY.md |
Tests are integrated into the existing test suite:
- No modifications to existing tests required
- New tests begin after "Quorum Re-validation Tests" header comment
- Follows established patterns from existing family_wallet tests
- Compatible with
cargo test -p family_walletworkflow