This guide provides comprehensive instructions for running tests, generating coverage reports, and interpreting security test results for the Checkmate-Escrow smart contract.
cd contracts/escrow
cargo test --libcd contracts/escrow
cargo test --lib security::cd contracts/escrow
cargo test --lib -- --nocaptureThe test suite is organized into logical modules:
- Pause/unpause functionality
- Token allowlist management
- Admin authorization checks
- Match creation
- Deposit handling
- Match completion and cancellation
- State transitions
- Player authorization
- Oracle authorization
- Admin-only functions
- Role-based access
- Token balance conservation
- Match state consistency
- Player validation
- Arithmetic safety
- Event emission verification
- Event data correctness
- Event sequencing
- Fuzz Testing: Stake amounts, game IDs, player addresses
- Authorization Fuzzing: Access control boundary testing
- Attack Vectors:
- Double deposit attacks
- Invalid state transition attacks
- Allowlist bypass attempts
- Contract pause bypass attempts
- Same player attacks
- Contract as player attacks
- Duplicate game ID attacks
- Overflow protection testing
- Invariant Validation: Token preservation, state consistency
Using tarpaulin (recommended):
cd contracts/escrow
cargo install cargo-tarpaulin # One-time setup
cargo tarpaulin --out Html --output-dir coverageUsing llvm-cov:
cd contracts/escrow
cargo install cargo-llvm-cov # One-time setup
cargo llvm-cov --htmlThe project enforces minimum coverage levels:
- Line Coverage: 90% minimum
- Branch Coverage: 85% minimum
These thresholds are enforced in CI/CD pipeline (coverage.yml).
-
Html Report: Open
coverage/index.htmlin a browser- Green: Fully covered
- Yellow/Orange: Partially covered
- Red: Not covered
-
LCOV Report: Can be imported into IDEs (VS Code, JetBrains)
cargo tarpaulin --out Lcov
-
Command Line Summary:
cargo tarpaulin --out Stdout
cargo test --lib admin
cargo test --lib security::test_security_unauthorized
cargo test --lib security::test_security_oraclecargo test --lib lifecycle
cargo test --lib invariantscargo test --lib token_allowlistcargo test --lib securitycargo test --lib create_matchcargo test --lib depositcargo test --lib submit_resultcargo test --lib cancel_matchThe security module includes comprehensive fuzz tests for critical inputs:
- Minimum valid amounts (1)
- Normal amounts (100)
- Large amounts (i128::MAX / 2)
- Invalid amounts (0, negative)
- Overflow boundary testing
- Empty strings (rejected)
- Minimum length (1 byte)
- Typical length (8 bytes for Lichess)
- Maximum length (64 bytes)
- Over-length strings (rejected)
- Same player validation
- Contract as player validation
- Invalid player rejection
Ensures players cannot deposit twice:
test_security_double_deposit_attack()Prevents operations in wrong states:
test_security_cancel_completed_match_attack()
test_security_cancel_active_match_attack()Validates token allowlist enforcement:
test_security_allowlist_bypass_attempt()Ensures pause blocks all critical operations:
test_security_create_match_when_paused()
test_security_deposit_when_paused()
test_security_submit_result_when_paused()Run invariant-based tests:
cargo test --lib invariantTwo workflows handle testing and coverage:
-
CI Workflow (
ci.yml)- Runs on: push to main/master, all PRs
- Tasks:
- Unit tests
- WASM build
- Link validation
- API reference checks
-
Coverage Workflow (
coverage.yml)- Runs on: push/PR to main/master with contract changes
- Tasks:
- Generates coverage report
- Enforces minimum thresholds (90% line coverage)
- Comments on PRs with coverage results
- Uploads coverage artifact
Run this before pushing:
#!/bin/bash
cd contracts/escrow
cargo test --lib || exit 1
cargo tarpaulin --out Stdout || exit 1
echo "✅ All tests passed and coverage meets requirements"-
Timeout errors: Increase timeout in Cargo.toml
[profile.test] opt-level = 1
-
Mock auth issues: Ensure
env.mock_all_auths()is calledenv.mock_all_auths(); // Required for Soroban tests
-
Token balance mismatches: Verify mint amounts in setup
asset_client.mint(&player1, &1000); // Sufficient balance required
-
Identify uncovered code:
cargo tarpaulin --out Html # Open coverage/index.html and look for red lines -
Add tests for uncovered paths:
- Look at red lines in coverage report
- Add test cases that exercise those paths
- Document why path is uncovered (if intentional)
-
Check if code is dead:
cargo dead-code # Or use clippy lints
- Use descriptive names:
test_security_double_deposit_attack - Test one thing: Each test should have a single assertion or tight group
- Use fixtures: Leverage
setup(),setup_with_funded_match() - Verify both success and failure paths
- Fuzz inputs: Test boundary values, invalid ranges
- Test authorization: Verify auth checks are enforced
- Test state transitions: Ensure invalid transitions are blocked
- Test invariants: Verify contract invariants hold
- Keep coverage > 90%: Non-negotiable minimum
- Document uncovered code: Add comments explaining why
- Prioritize security functions: Higher coverage for auth, state, transfers
- Regular audits: Periodically review coverage trends
Full test suite: ~10-15s
Security tests only: ~3-5s
Coverage generation: ~20-30s
Times vary based on machine specs and background processes.
let (env, contract_id, oracle, player1, player2, token, admin) = setup();Provides:
- Initialized contract
- Two players with 1000 tokens each
- Ready-to-use oracle and admin addresses
let (env, contract_id, oracle, player1, player2, token, admin, match_id) = setup_with_funded_match();Adds:
- Active match (both players deposited)
- Ready for result submission
Generate badge for README:
[]()Update after running coverage reports.