Summary
The Registry contract has tests in contracts/tests/registry_test.rs. However, the SpendingPolicy contract (contracts/src/policy.rs) has zero test coverage. This is a critical gap — the spending policy handles real USDC transfers and must be thoroughly tested before any testnet or mainnet deployment.
Background
The SpendingPolicy contract (contracts/src/policy.rs) has the following public functions that all need tests:
Functions: create_policy(), execute_payment(), revoke_policy(), get_policy(), is_authorized(), remaining_allowance()
The key error cases defined in PolicyError (line 21–29) also need negative tests:
rust
pub enum PolicyError {
PolicyNotFound = 1,
NotOwner = 2,
PolicyInactive = 3,
ExceedsPerTxLimit = 4,
ExceedsDailyLimit = 5,
InvalidAmount = 6,
UnauthorizedAgent = 7,
RecipientNotAllowed = 8,
}
What Needs to Be Built
Create the file contracts/tests/policy_test.rs with the following test cases:
Happy path tests:
test_create_policy_success — create a policy, verify all fields are stored correctly
test_execute_payment_success — agent executes a payment within limits, verify token transfer happens
test_revoke_policy_success — owner revokes policy, verify is_active becomes false
test_remaining_allowance_full — new policy has full daily limit available
test_remaining_allowance_after_spend — after a payment, remaining allowance decreases correctly
test_daily_reset — advance ledger by 17,280+ sequences, verify spent_today resets to 0
test_is_authorized_true — correct agent returns true
test_is_authorized_false — wrong agent or inactive policy returns false
test_policy_count — increments correctly after each create_policy call
Error / negative tests:
10. test_payment_exceeds_per_tx_limit — amount > max_per_tx → ExceedsPerTxLimit
11. test_payment_exceeds_daily_limit — cumulative spend > daily_limit → ExceedsDailyLimit
12. test_payment_on_revoked_policy — execute_payment on inactive policy → PolicyInactive
13. test_revoke_nonexistent_policy → PolicyNotFound
14. test_payment_wrong_recipient — policy has allowed_recipient set, different address passed → RecipientNotAllowed
15. test_create_policy_zero_amount — max_per_tx = 0 → InvalidAmount
Relevant Files
File:
- contracts/src/policy.rs: The contract being tested — read this carefully before writing tests
- contracts/src/types.rs: SpendingPolicy, PaymentRecord, DataKey structs
- contracts/tests/registry_test.rsReference — use the same test structure and helpers
- contracts/Cargo.tomlAlready has soroban-sdk = { features = ["testutils"] } — no changes needed
How to Run Tests
bash
cd contracts
cargo test
# or to run only policy tests:
cargo test policy
Acceptance Criteria
File contracts/tests/policy_test.rs created
All 15 test cases listed above are implemented
All tests pass: cargo test returns 0 failures
No compiler warnings: cargo clippy -- -D warnings passes
Each test has a comment explaining what it's verifying
Tips
Look at how registry_test.rs uses Env::default(), env.register_contract(), and ContractClient — follow the same pattern
For testing token transfers, use soroban_sdk::testutils::token::Client to create a mock USDC token
For advancing ledgers (daily reset test), use env.ledger().with_mut(|l| l.sequence_number += 17_281)
The #[cfg(test)] attribute must be at the top of the test file
Estimated Effort
Medium (4–8 hours)
//Please don't submit a PR without being assigned.
Summary
The Registry contract has tests in contracts/tests/registry_test.rs. However, the SpendingPolicy contract (contracts/src/policy.rs) has zero test coverage. This is a critical gap — the spending policy handles real USDC transfers and must be thoroughly tested before any testnet or mainnet deployment.
Background
The SpendingPolicy contract (contracts/src/policy.rs) has the following public functions that all need tests:
Functions: create_policy(), execute_payment(), revoke_policy(), get_policy(), is_authorized(), remaining_allowance()
The key error cases defined in PolicyError (line 21–29) also need negative tests:
What Needs to Be Built
Create the file contracts/tests/policy_test.rs with the following test cases:
Happy path tests:
test_create_policy_success — create a policy, verify all fields are stored correctly
test_execute_payment_success — agent executes a payment within limits, verify token transfer happens
test_revoke_policy_success — owner revokes policy, verify is_active becomes false
test_remaining_allowance_full — new policy has full daily limit available
test_remaining_allowance_after_spend — after a payment, remaining allowance decreases correctly
test_daily_reset — advance ledger by 17,280+ sequences, verify spent_today resets to 0
test_is_authorized_true — correct agent returns true
test_is_authorized_false — wrong agent or inactive policy returns false
test_policy_count — increments correctly after each create_policy call
Error / negative tests:
10. test_payment_exceeds_per_tx_limit — amount > max_per_tx → ExceedsPerTxLimit
11. test_payment_exceeds_daily_limit — cumulative spend > daily_limit → ExceedsDailyLimit
12. test_payment_on_revoked_policy — execute_payment on inactive policy → PolicyInactive
13. test_revoke_nonexistent_policy → PolicyNotFound
14. test_payment_wrong_recipient — policy has allowed_recipient set, different address passed → RecipientNotAllowed
15. test_create_policy_zero_amount — max_per_tx = 0 → InvalidAmount
Relevant Files
File:
How to Run Tests
Acceptance Criteria
File contracts/tests/policy_test.rs created
All 15 test cases listed above are implemented
All tests pass: cargo test returns 0 failures
No compiler warnings: cargo clippy -- -D warnings passes
Each test has a comment explaining what it's verifying
Tips
Look at how registry_test.rs uses Env::default(), env.register_contract(), and ContractClient — follow the same pattern
For testing token transfers, use soroban_sdk::testutils::token::Client to create a mock USDC token
For advancing ledgers (daily reset test), use env.ledger().with_mut(|l| l.sequence_number += 17_281)
The #[cfg(test)] attribute must be at the top of the test file
Estimated Effort
Medium (4–8 hours)
//Please don't submit a PR without being assigned.