Skip to content

Latest commit

 

History

History
246 lines (200 loc) · 7.65 KB

File metadata and controls

246 lines (200 loc) · 7.65 KB

Family Wallet Threshold Change Tests - Quick Reference

Quick Start

Run All Threshold Change Tests

cd family_wallet
cargo test threshold_change -- --nocapture

Run Specific Test

cargo test test_threshold_change_lower_allows_execution -- --nocapture

Run All Family Wallet Tests

cargo test -p family_wallet

Run Tests with Output Capture

cargo test -p family_wallet threshold_change

Test List at a Glance

# 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 Patterns & Naming

Test Function Naming Convention

test_threshold_change_{scenario}[_{detail}]

Examples:

  • test_threshold_change_lower_allows_execution - Lowering threshold scenario
  • test_threshold_change_raise_blocks_execution - Raising threshold scenario
  • test_threshold_change_invalid_threshold_exceeds_signer_count - Boundary error scenario

Common Test Structure

#[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!(...);
}

Key Functions Used in Tests

Contract Initialization

client.init(&owner, &initial_members);

Configuration

client.configure_multisig(
    &caller,                        // Admin or Owner
    &TransactionType::...,          // Transaction type
    &threshold,                     // Required signatures (u32)
    &signers,                       // Vec of authorized signers
    &spending_limit                 // Spending cap (i128)
);

Proposal Creation

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);

Signature Collection

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 fails

Query Pending Proposals

let pending = client.get_pending_transaction(&tx_id);
// Returns: Option<PendingTransaction>
// None = executed/removed, Some = still pending

Threshold Revalidation

let invalidated_count = client.revalidate_proposals(&admin);
// Returns: u32 count of invalidated proposals

Multisig Configuration Query

let config = client.get_multisig_config(&TransactionType::...);
// Returns: Option<MultiSigConfig>
// Contains: threshold, signers, spending_limit

Role Expiry

client.set_role_expiry(&caller, &member, &Some(timestamp));
// Used to simulate member removal/inactivity

Common Assertions

Proposal Status

assert!(client.get_pending_transaction(&tx_id).is_some());  // Still pending
assert!(client.get_pending_transaction(&tx_id).is_none());   // Executed

Signature Count

let pending_tx = client.get_pending_transaction(&tx_id).unwrap();
assert_eq!(pending_tx.signatures.len(), 2);  // Check sig count

Token Transfer Verification

let token_client = TokenClient::new(&env, &token_contract.address());
assert_eq!(token_client.balance(&recipient), amount);  // Verify transfer

Error Assertions

let result = client.try_configure_multisig(...);
assert_eq!(result, Err(Ok(Error::InvalidThreshold)));

// or for panic-based errors:
#[should_panic(expected = "...")]
fn test_...() { ... }

Expected Error Codes

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

Test Execution Guarantees

✅ 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

Debugging Failed Tests

1. Check Test Output

cargo test test_name -- --nocapture --test-threads=1

2. Add Debug Assertions

println!("Pending count: {:?}", client.get_pending_transaction(&tx_id));

3. Verify Contract State

let config = client.get_multisig_config(&tx_type);
println!("Current threshold: {:?}", config.unwrap().threshold);

4. Check Signer Authorization

let config = client.get_multisig_config(&tx_type);
for signer in config.unwrap().signers.iter() {
    println!("Authorized: {:?}", signer);
}

Performance Notes

  • 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 --release flag for large test runs

File Locations

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

Integration Notes

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_wallet workflow