From 7a7ff8e7c1f48e8aceb2357965228de84d04b642 Mon Sep 17 00:00:00 2001 From: clintjeff2 Date: Wed, 25 Feb 2026 07:52:07 +0100 Subject: [PATCH 01/22] test: completed test coverage task --- contracts/vault/src/lib.rs | 28 +- contracts/vault/src/test.rs | 465 ++++++++++++++++++++------------- coverage/cobertura.xml | 2 +- coverage/tarpaulin-report.html | 4 +- 4 files changed, 294 insertions(+), 205 deletions(-) diff --git a/contracts/vault/src/lib.rs b/contracts/vault/src/lib.rs index 00a4738..ce0d4b7 100644 --- a/contracts/vault/src/lib.rs +++ b/contracts/vault/src/lib.rs @@ -84,6 +84,10 @@ impl CalloraVault { inst.set(&Symbol::new(&env, META_KEY), &meta); inst.set(&Symbol::new(&env, USDC_KEY), &usdc_token); inst.set(&Symbol::new(&env, ADMIN_KEY), &owner); + if let Some(pool) = revenue_pool { + inst.set(&Symbol::new(&env, REVENUE_POOL_KEY), &pool); + } + inst.set(&Symbol::new(&env, MAX_DEDUCT_KEY), &max_deduct_val); env.events() .publish((Symbol::new(&env, "init"), owner), balance); @@ -236,17 +240,6 @@ impl CalloraVault { let mut meta = Self::get_meta(env.clone()); assert!(meta.balance >= amount, "insufficient balance"); - let usdc_address: Address = env - .storage() - .instance() - .get(&Symbol::new(&env, USDC_KEY)) - .unwrap_or_else(|| panic!("vault not initialized")); - let revenue_pool: Option
= env - .storage() - .instance() - .get(&Symbol::new(&env, REVENUE_POOL_KEY)) - .unwrap_or(None); - meta.balance -= amount; let inst = env.storage().instance(); inst.set(&Symbol::new(&env, "meta"), &meta); @@ -274,7 +267,6 @@ impl CalloraVault { let n = items.len(); assert!(n > 0, "batch_deduct requires at least one item"); - let mut total_deduct = 0i128; let mut running = meta.balance; for item in items.iter() { assert!(item.amount > 0, "amount must be positive"); @@ -284,20 +276,8 @@ impl CalloraVault { ); assert!(running >= item.amount, "insufficient balance"); running -= item.amount; - total_deduct += item.amount; } - let usdc_address: Address = env - .storage() - .instance() - .get(&Symbol::new(&env, USDC_KEY)) - .unwrap_or_else(|| panic!("vault not initialized")); - let revenue_pool: Option
= env - .storage() - .instance() - .get(&Symbol::new(&env, REVENUE_POOL_KEY)) - .unwrap_or(None); - let mut balance = meta.balance; for item in items.iter() { balance -= item.amount; diff --git a/contracts/vault/src/test.rs b/contracts/vault/src/test.rs index 5addb60..b276748 100644 --- a/contracts/vault/src/test.rs +++ b/contracts/vault/src/test.rs @@ -29,8 +29,6 @@ fn fund_vault( usdc_admin_client.mint(vault_address, &amount); } -/// Full vault lifecycle integration test: init → deposit → batch_deduct → -/// set_admin → withdraw_to, verifying state at each step. #[test] fn vault_full_lifecycle() { let env = Env::default(); @@ -40,23 +38,25 @@ fn vault_full_lifecycle() { let recipient = Address::generate(&env); let contract_id = env.register(CalloraVault {}, ()); let client = CalloraVaultClient::new(&env, &contract_id); - let (usdc, _, _) = create_usdc(&env, &owner); + let (usdc, _, usdc_admin) = create_usdc(&env, &owner); env.mock_all_auths(); - // 1. Initialise with 500 balance and min_deposit of 10. - let meta = client.init(&owner, &usdc, &Some(500), &Some(10)); + fund_vault(&usdc_admin, &contract_id, 500); + let meta = client.init(&owner, &usdc, &Some(500), &Some(10), &None, &None); assert_eq!(meta.balance, 500); assert_eq!(meta.owner, owner); assert_eq!(client.balance(), 500); assert_eq!(client.get_admin(), owner); - // 2. Deposit – must be ≥ min_deposit. - let after_deposit = client.deposit(&200); + let depositor = Address::generate(&env); + fund_vault(&usdc_admin, &depositor, 200); + let usdc_client = token::Client::new(&env, &usdc); + usdc_client.approve(&depositor, &contract_id, &200, &1000); + let after_deposit = client.deposit(&depositor, &200); assert_eq!(after_deposit, 700); assert_eq!(client.balance(), 700); - // 3. Batch deduct three items in one call. let items = soroban_sdk::vec![ &env, DeductItem { @@ -73,51 +73,39 @@ fn vault_full_lifecycle() { }, ]; let after_batch = client.batch_deduct(&caller, &items); - assert_eq!(after_batch, 525); // 700 - 175 + assert_eq!(after_batch, 525); assert_eq!(client.balance(), 525); - // 4. Single deduct. let after_deduct = client.deduct(&caller, &25, &Some(Symbol::new(&env, "r4"))); assert_eq!(after_deduct, 500); - // 5. Transfer admin to new_admin, then verify. client.set_admin(&owner, &new_admin); assert_eq!(client.get_admin(), new_admin); - // 6. Withdraw to a recipient address. let after_withdraw = client.withdraw_to(&recipient, &100); assert_eq!(after_withdraw, 400); assert_eq!(client.balance(), 400); - // 7. Direct withdraw (to owner). let after_withdraw2 = client.withdraw(&50); assert_eq!(after_withdraw2, 350); assert_eq!(client.balance(), 350); - // 8. get_meta round-trip. let final_meta = client.get_meta(); assert_eq!(final_meta.balance, 350); assert_eq!(final_meta.owner, owner); assert_eq!(final_meta.min_deposit, 10); } -// --------------------------------------------------------------------------- -// init / balance -// --------------------------------------------------------------------------- - -/// Initialising with an explicit balance stores that value and emits the event. #[test] fn init_with_balance_emits_event() { let env = Env::default(); let owner = Address::generate(&env); let contract_id = env.register(CalloraVault {}, ()); + let (usdc_token, _, usdc_admin) = create_usdc(&env, &owner); - let usdc_token = Address::generate(&env); - - // Mock all auth checks so init can proceed without signatures env.mock_all_auths(); + fund_vault(&usdc_admin, &contract_id, 1000); - // Invoke init inside as_contract so the SDK captures the published event. let events = env.as_contract(&contract_id, || { CalloraVault::init( env.clone(), @@ -125,21 +113,18 @@ fn init_with_balance_emits_event() { usdc_token.clone(), Some(1000), None, + None, + None, ); env.events().all() }); - // Balance must reflect the initial value. let client = CalloraVaultClient::new(&env, &contract_id); assert_eq!(client.balance(), 1000); - // Exactly one event should have been emitted. let last_event = events.last().expect("expected at least one event"); - - // Emitting contract must be our vault. assert_eq!(last_event.0, contract_id); - // Topics: (Symbol("init"), owner) let topics = &last_event.1; assert_eq!(topics.len(), 2); let topic0: Symbol = topics.get(0).unwrap().into_val(&env); @@ -147,60 +132,46 @@ fn init_with_balance_emits_event() { assert_eq!(topic0, Symbol::new(&env, "init")); assert_eq!(topic1, owner); - // Event data carries the starting balance. let data: i128 = last_event.2.into_val(&env); assert_eq!(data, 1000); } -/// When no initial balance is provided the vault should default to zero. #[test] fn init_defaults_balance_to_zero() { let env = Env::default(); let owner = Address::generate(&env); let contract_id = env.register(CalloraVault {}, ()); let client = CalloraVaultClient::new(&env, &contract_id); - - let usdc_token = Address::generate(&env); + let (usdc_token, _, _) = create_usdc(&env, &owner); env.mock_all_auths(); - - // Pass None — exercises the `unwrap_or(0)` branch in lib.rs. - client.init(&owner, &usdc_token, &None, &None); + client.init(&owner, &usdc_token, &None, &None, &None, &None); assert_eq!(client.balance(), 0); } -// --------------------------------------------------------------------------- -// get_meta -// --------------------------------------------------------------------------- - -/// get_meta returns both the stored owner address and balance correctly. #[test] fn get_meta_returns_owner_and_balance() { let env = Env::default(); let owner = Address::generate(&env); let contract_id = env.register(CalloraVault {}, ()); let client = CalloraVaultClient::new(&env, &contract_id); - - let usdc_token = Address::generate(&env); + let (usdc_token, _, usdc_admin) = create_usdc(&env, &owner); env.mock_all_auths(); - - client.init(&owner, &usdc_token, &Some(500), &None); + fund_vault(&usdc_admin, &contract_id, 500); + client.init(&owner, &usdc_token, &Some(500), &None, &None, &None); let meta = client.get_meta(); assert_eq!(meta.owner, owner); assert_eq!(meta.balance, 500); } -/// Calling get_meta before init must return an error (not a panic that kills -/// the test process) — exercises the `unwrap_or_else(panic)` error path. #[test] fn get_meta_before_init_fails() { let env = Env::default(); let contract_id = env.register(CalloraVault {}, ()); let client = CalloraVaultClient::new(&env, &contract_id); - // try_get_meta() is the Result-returning variant generated by the SDK. let result = client.try_get_meta(); assert!( result.is_err(), @@ -208,24 +179,23 @@ fn get_meta_before_init_fails() { ); } -// --------------------------------------------------------------------------- -// deposit -// --------------------------------------------------------------------------- - -/// Depositing accumulates correctly and the returned value matches balance(). #[test] fn deposit_and_balance_match() { let env = Env::default(); let owner = Address::generate(&env); + let depositor = Address::generate(&env); let contract_id = env.register(CalloraVault {}, ()); let client = CalloraVaultClient::new(&env, &contract_id); - - let usdc_token = Address::generate(&env); + let (usdc_token, _, usdc_admin) = create_usdc(&env, &owner); env.mock_all_auths(); + fund_vault(&usdc_admin, &contract_id, 100); + client.init(&owner, &usdc_token, &Some(100), &None, &None, &None); - client.init(&owner, &usdc_token, &Some(100), &None); - let returned = client.deposit(&200); + fund_vault(&usdc_admin, &depositor, 200); + let usdc_client = token::Client::new(&env, &usdc_token); + usdc_client.approve(&depositor, &contract_id, &200, &1000); + let returned = client.deposit(&depositor, &200); assert_eq!( returned, 300, @@ -234,24 +204,24 @@ fn deposit_and_balance_match() { assert_eq!(client.balance(), 300); } -// --------------------------------------------------------------------------- -// deduct -// --------------------------------------------------------------------------- - -/// A valid deduction reduces the balance by exactly the requested amount. #[test] fn deduct_reduces_balance() { let env = Env::default(); let owner = Address::generate(&env); let contract_id = env.register(CalloraVault {}, ()); let client = CalloraVaultClient::new(&env, &contract_id); - - let (usdc, _, _) = create_usdc(&env, &owner); + let (usdc, _, usdc_admin) = create_usdc(&env, &owner); let caller = Address::generate(&env); + let depositor = Address::generate(&env); env.mock_all_auths(); - client.init(&owner, &usdc, &Some(100), &None); - client.deposit(&200); + fund_vault(&usdc_admin, &contract_id, 100); + client.init(&owner, &usdc, &Some(100), &None, &None, &None); + + fund_vault(&usdc_admin, &depositor, 200); + let usdc_client = token::Client::new(&env, &usdc); + usdc_client.approve(&depositor, &contract_id, &200, &1000); + client.deposit(&depositor, &200); assert_eq!(client.balance(), 300); let returned = client.deduct(&caller, &50, &None); @@ -259,88 +229,177 @@ fn deduct_reduces_balance() { assert_eq!(client.balance(), 250); } -/// Deduct with request_id for idempotency tracking. #[test] fn deduct_with_request_id() { let env = Env::default(); let owner = Address::generate(&env); let contract_id = env.register(CalloraVault {}, ()); let client = CalloraVaultClient::new(&env, &contract_id); - - let (usdc, _, _) = create_usdc(&env, &owner); + let (usdc, _, usdc_admin) = create_usdc(&env, &owner); let caller = Address::generate(&env); env.mock_all_auths(); - client.init(&owner, &usdc, &Some(1000), &None); + fund_vault(&usdc_admin, &contract_id, 1000); + client.init(&owner, &usdc, &Some(1000), &None, &None, &None); let request_id = Symbol::new(&env, "req123"); let remaining = client.deduct(&caller, &100, &Some(request_id)); assert_eq!(remaining, 900); } -/// Deducting more than the available balance must be rejected — exercises the -/// `assert!(meta.balance >= amount, "insufficient balance")` guard. #[test] fn deduct_insufficient_balance_fails() { let env = Env::default(); let owner = Address::generate(&env); let contract_id = env.register(CalloraVault {}, ()); let client = CalloraVaultClient::new(&env, &contract_id); - - let usdc_token = Address::generate(&env); + let (usdc_token, _, usdc_admin) = create_usdc(&env, &owner); let caller = Address::generate(&env); env.mock_all_auths(); + fund_vault(&usdc_admin, &contract_id, 10); + client.init(&owner, &usdc_token, &Some(10), &None, &None, &None); - client.init(&owner, &usdc_token, &Some(10), &None); - - // try_deduct() returns Result so we can assert on the error without - // unwinding the test runner. let result = client.try_deduct(&caller, &100, &None); assert!(result.is_err(), "expected error for insufficient balance"); } -/// Deducting exactly the full balance should succeed and leave zero. #[test] fn deduct_exact_balance_succeeds() { let env = Env::default(); let owner = Address::generate(&env); let contract_id = env.register(CalloraVault {}, ()); let client = CalloraVaultClient::new(&env, &contract_id); - - let usdc_token = Address::generate(&env); + let (usdc_token, _, usdc_admin) = create_usdc(&env, &owner); let caller = Address::generate(&env); env.mock_all_auths(); - - client.init(&owner, &usdc_token, &Some(75), &None); + fund_vault(&usdc_admin, &contract_id, 75); + client.init(&owner, &usdc_token, &Some(75), &None, &None, &None); let remaining = client.deduct(&caller, &75, &None); assert_eq!(remaining, 0); assert_eq!(client.balance(), 0); } -// --------------------------------------------------------------------------- -// admin management -// --------------------------------------------------------------------------- +#[test] +fn deduct_event_contains_request_id() { + let env = Env::default(); + let owner = Address::generate(&env); + let caller = Address::generate(&env); + let contract_id = env.register(CalloraVault {}, ()); + let client = CalloraVaultClient::new(&env, &contract_id); + let (usdc_token, _, usdc_admin) = create_usdc(&env, &owner); + + env.mock_all_auths(); + fund_vault(&usdc_admin, &contract_id, 500); + client.init(&owner, &usdc_token, &Some(500), &None, &None, &None); + + let request_id = Symbol::new(&env, "api_call_42"); + client.deduct(&caller, &150, &Some(request_id.clone())); + + let events = env.events().all(); + let ev = events.last().expect("expected deduct event"); + + let topic0: Symbol = ev.1.get(0).unwrap().into_val(&env); + let topic1: Address = ev.1.get(1).unwrap().into_val(&env); + let topic2: Symbol = ev.1.get(2).unwrap().into_val(&env); + + assert_eq!(topic0, Symbol::new(&env, "deduct")); + assert_eq!(topic1, caller); + assert_eq!(topic2, request_id); + + let (emitted_amount, remaining): (i128, i128) = ev.2.into_val(&env); + assert_eq!(emitted_amount, 150); + assert_eq!(remaining, 350); +} + +#[test] +fn deduct_event_no_request_id_uses_empty_symbol() { + let env = Env::default(); + let owner = Address::generate(&env); + let caller = Address::generate(&env); + let contract_id = env.register(CalloraVault {}, ()); + let client = CalloraVaultClient::new(&env, &contract_id); + let (usdc_token, _, usdc_admin) = create_usdc(&env, &owner); + + env.mock_all_auths(); + fund_vault(&usdc_admin, &contract_id, 300); + client.init(&owner, &usdc_token, &Some(300), &None, &None, &None); + client.deduct(&caller, &100, &None); + + let events = env.events().all(); + let ev = events.last().expect("expected deduct event"); + + let topic0: Symbol = ev.1.get(0).unwrap().into_val(&env); + let topic2: Symbol = ev.1.get(2).unwrap().into_val(&env); + + assert_eq!(topic0, Symbol::new(&env, "deduct")); + assert_eq!(topic2, Symbol::new(&env, "")); +} + +#[test] +fn batch_deduct_events_contain_request_ids() { + let env = Env::default(); + let owner = Address::generate(&env); + let caller = Address::generate(&env); + let contract_id = env.register(CalloraVault {}, ()); + let client = CalloraVaultClient::new(&env, &contract_id); + let (usdc_token, _, usdc_admin) = create_usdc(&env, &owner); + + env.mock_all_auths(); + fund_vault(&usdc_admin, &contract_id, 1000); + client.init(&owner, &usdc_token, &Some(1000), &None, &None, &None); + + let rid_a = Symbol::new(&env, "batch_a"); + let rid_b = Symbol::new(&env, "batch_b"); + + let items = soroban_sdk::vec![ + &env, + DeductItem { + amount: 200, + request_id: Some(rid_a.clone()), + }, + DeductItem { + amount: 300, + request_id: Some(rid_b.clone()), + }, + ]; + client.batch_deduct(&caller, &items); + + let all_events = env.events().all(); + assert_eq!(all_events.len(), 2); + + let ev_a = all_events.get(0).unwrap(); + let ev_b = all_events.get(1).unwrap(); + + let req_a: Symbol = ev_a.1.get(2).unwrap().into_val(&env); + let req_b: Symbol = ev_b.1.get(2).unwrap().into_val(&env); + assert_eq!(req_a, rid_a); + assert_eq!(req_b, rid_b); + + let (amt_a, _): (i128, i128) = ev_a.2.into_val(&env); + let (amt_b, _): (i128, i128) = ev_b.2.into_val(&env); + assert_eq!(amt_a, 200); + assert_eq!(amt_b, 300); +} -/// get_admin returns the admin address set during init. #[test] fn get_admin_returns_correct_address() { let env = Env::default(); let owner = Address::generate(&env); let contract_id = env.register(CalloraVault {}, ()); let client = CalloraVaultClient::new(&env, &contract_id); - let usdc_token = Address::generate(&env); + let (usdc_token, _, usdc_admin) = create_usdc(&env, &owner); env.mock_all_auths(); - client.init(&owner, &usdc_token, &Some(100), &None); + fund_vault(&usdc_admin, &contract_id, 100); + client.init(&owner, &usdc_token, &Some(100), &None, &None, &None); let admin = client.get_admin(); assert_eq!(admin, owner); } -/// Only the current admin can update the admin address. #[test] fn set_admin_updates_admin() { let env = Env::default(); @@ -348,16 +407,16 @@ fn set_admin_updates_admin() { let new_admin = Address::generate(&env); let contract_id = env.register(CalloraVault {}, ()); let client = CalloraVaultClient::new(&env, &contract_id); - let usdc_token = Address::generate(&env); + let (usdc_token, _, usdc_admin) = create_usdc(&env, &owner); env.mock_all_auths(); - client.init(&owner, &usdc_token, &Some(100), &None); + fund_vault(&usdc_admin, &contract_id, 100); + client.init(&owner, &usdc_token, &Some(100), &None, &None, &None); client.set_admin(&owner, &new_admin); assert_eq!(client.get_admin(), new_admin); } -/// Non-admin callers cannot change the admin. #[test] fn set_admin_unauthorized_fails() { let env = Env::default(); @@ -366,10 +425,11 @@ fn set_admin_unauthorized_fails() { let new_admin = Address::generate(&env); let contract_id = env.register(CalloraVault {}, ()); let client = CalloraVaultClient::new(&env, &contract_id); - let usdc_token = Address::generate(&env); + let (usdc_token, _, usdc_admin) = create_usdc(&env, &owner); env.mock_all_auths(); - client.init(&owner, &usdc_token, &Some(100), &None); + fund_vault(&usdc_admin, &contract_id, 100); + client.init(&owner, &usdc_token, &Some(100), &None, &None, &None); let result = client.try_set_admin(&intruder, &new_admin); assert!( @@ -378,36 +438,25 @@ fn set_admin_unauthorized_fails() { ); } -// --------------------------------------------------------------------------- -// distribute -// --------------------------------------------------------------------------- - -/// Admin can distribute USDC from vault to a developer. #[test] fn distribute_transfers_usdc_to_developer() { let env = Env::default(); let admin = Address::generate(&env); let developer = Address::generate(&env); let (vault_address, client) = create_vault(&env); - let (usdc, _, usdc_admin_client) = create_usdc(&env, &admin); + let (usdc, usdc_client, usdc_admin_client) = create_usdc(&env, &admin); env.mock_all_auths(); - client.init(&admin, &usdc, &Some(0), &None); + fund_vault(&usdc_admin_client, &vault_address, 1000); + client.init(&admin, &usdc, &Some(0), &None, &None, &None); - // Mint 1000 USDC into the vault - fund_vault(&env, &usdc_admin_client, &vault_address, 1000); - - // Distribute 300 to developer client.distribute(&admin, &developer, &300); - // Verify developer received the funds - let usdc_client = token::Client::new(&env, &usdc); assert_eq!(usdc_client.balance(&developer), 300); assert_eq!(usdc_client.balance(&vault_address), 700); } -/// Non-admin cannot distribute funds. #[test] fn distribute_unauthorized_fails() { let env = Env::default(); @@ -419,8 +468,8 @@ fn distribute_unauthorized_fails() { env.mock_all_auths(); - client.init(&admin, &usdc, &Some(0), &None); - fund_vault(&env, &usdc_admin_client, &vault_address, 1000); + fund_vault(&usdc_admin_client, &vault_address, 1000); + client.init(&admin, &usdc, &Some(0), &None, &None, &None); let result = client.try_distribute(&intruder, &developer, &300); assert!( @@ -429,7 +478,6 @@ fn distribute_unauthorized_fails() { ); } -/// Distributing more than vault balance fails. #[test] fn distribute_insufficient_usdc_fails() { let env = Env::default(); @@ -440,8 +488,8 @@ fn distribute_insufficient_usdc_fails() { env.mock_all_auths(); - client.init(&admin, &usdc, &Some(0), &None); - fund_vault(&env, &usdc_admin_client, &vault_address, 100); + fund_vault(&usdc_admin_client, &vault_address, 100); + client.init(&admin, &usdc, &Some(0), &None, &None, &None); let result = client.try_distribute(&admin, &developer, &500); assert!( @@ -450,7 +498,6 @@ fn distribute_insufficient_usdc_fails() { ); } -/// Distributing zero or negative amount fails. #[test] fn distribute_zero_amount_fails() { let env = Env::default(); @@ -461,18 +508,13 @@ fn distribute_zero_amount_fails() { env.mock_all_auths(); - client.init(&admin, &usdc, &Some(0), &None); - fund_vault(&env, &usdc_admin_client, &vault_address, 1000); + fund_vault(&usdc_admin_client, &vault_address, 1000); + client.init(&admin, &usdc, &Some(0), &None, &None, &None); let result = client.try_distribute(&admin, &developer, &0); assert!(result.is_err(), "expected error for zero amount"); } -// --------------------------------------------------------------------------- -// batch_deduct -// --------------------------------------------------------------------------- - -/// Batch deduct processes multiple items and emits multiple events. #[test] fn batch_deduct_multiple_items() { let env = Env::default(); @@ -480,10 +522,11 @@ fn batch_deduct_multiple_items() { let caller = Address::generate(&env); let contract_id = env.register(CalloraVault {}, ()); let client = CalloraVaultClient::new(&env, &contract_id); - let usdc_token = Address::generate(&env); + let (usdc_token, _, usdc_admin) = create_usdc(&env, &owner); env.mock_all_auths(); - client.init(&owner, &usdc_token, &Some(1000), &None); + fund_vault(&usdc_admin, &contract_id, 1000); + client.init(&owner, &usdc_token, &Some(1000), &None, &None, &None); let items = soroban_sdk::vec![ &env, @@ -502,11 +545,10 @@ fn batch_deduct_multiple_items() { ]; let remaining = client.batch_deduct(&caller, &items); - assert_eq!(remaining, 650); // 1000 - 100 - 200 - 50 + assert_eq!(remaining, 650); assert_eq!(client.balance(), 650); } -/// Batch deduct fails if any item would overdraw. #[test] fn batch_deduct_insufficient_balance_fails() { let env = Env::default(); @@ -514,10 +556,11 @@ fn batch_deduct_insufficient_balance_fails() { let caller = Address::generate(&env); let contract_id = env.register(CalloraVault {}, ()); let client = CalloraVaultClient::new(&env, &contract_id); - let usdc_token = Address::generate(&env); + let (usdc_token, _, usdc_admin) = create_usdc(&env, &owner); env.mock_all_auths(); - client.init(&owner, &usdc_token, &Some(100), &None); + fund_vault(&usdc_admin, &contract_id, 100); + client.init(&owner, &usdc_token, &Some(100), &None, &None, &None); let items = soroban_sdk::vec![ &env, @@ -526,18 +569,16 @@ fn batch_deduct_insufficient_balance_fails() { request_id: None }, DeductItem { - amount: 80, // would overdraw + amount: 80, request_id: None } ]; let result = client.try_batch_deduct(&caller, &items); assert!(result.is_err(), "expected error for batch overdraw"); - // Balance should remain unchanged after failed batch assert_eq!(client.balance(), 100); } -/// Empty batch fails. #[test] fn batch_deduct_empty_fails() { let env = Env::default(); @@ -545,10 +586,11 @@ fn batch_deduct_empty_fails() { let caller = Address::generate(&env); let contract_id = env.register(CalloraVault {}, ()); let client = CalloraVaultClient::new(&env, &contract_id); - let usdc_token = Address::generate(&env); + let (usdc_token, _, usdc_admin) = create_usdc(&env, &owner); env.mock_all_auths(); - client.init(&owner, &usdc_token, &Some(100), &None); + fund_vault(&usdc_admin, &contract_id, 100); + client.init(&owner, &usdc_token, &Some(100), &None, &None, &None); let items: soroban_sdk::Vec = soroban_sdk::vec![&env]; @@ -556,7 +598,6 @@ fn batch_deduct_empty_fails() { assert!(result.is_err(), "expected error for empty batch"); } -/// Batch deduct with zero amount fails. #[test] fn batch_deduct_zero_amount_fails() { let env = Env::default(); @@ -564,10 +605,11 @@ fn batch_deduct_zero_amount_fails() { let caller = Address::generate(&env); let contract_id = env.register(CalloraVault {}, ()); let client = CalloraVaultClient::new(&env, &contract_id); - let usdc_token = Address::generate(&env); + let (usdc_token, _, usdc_admin) = create_usdc(&env, &owner); env.mock_all_auths(); - client.init(&owner, &usdc_token, &Some(100), &None); + fund_vault(&usdc_admin, &contract_id, 100); + client.init(&owner, &usdc_token, &Some(100), &None, &None, &None); let items = soroban_sdk::vec![ &env, @@ -581,64 +623,55 @@ fn batch_deduct_zero_amount_fails() { assert!(result.is_err(), "expected error for zero amount"); } -// --------------------------------------------------------------------------- -// withdraw -// --------------------------------------------------------------------------- - -/// Owner can withdraw funds. #[test] fn withdraw_reduces_balance() { let env = Env::default(); let owner = Address::generate(&env); let contract_id = env.register(CalloraVault {}, ()); let client = CalloraVaultClient::new(&env, &contract_id); - let usdc_token = Address::generate(&env); + let (usdc_token, _, usdc_admin) = create_usdc(&env, &owner); env.mock_all_auths(); - client.init(&owner, &usdc_token, &Some(500), &None); + fund_vault(&usdc_admin, &contract_id, 500); + client.init(&owner, &usdc_token, &Some(500), &None, &None, &None); let remaining = client.withdraw(&200); assert_eq!(remaining, 300); assert_eq!(client.balance(), 300); } -/// Withdrawing more than balance fails. #[test] fn withdraw_insufficient_balance_fails() { let env = Env::default(); let owner = Address::generate(&env); let contract_id = env.register(CalloraVault {}, ()); let client = CalloraVaultClient::new(&env, &contract_id); - let usdc_token = Address::generate(&env); + let (usdc_token, _, usdc_admin) = create_usdc(&env, &owner); env.mock_all_auths(); - client.init(&owner, &usdc_token, &Some(100), &None); + fund_vault(&usdc_admin, &contract_id, 100); + client.init(&owner, &usdc_token, &Some(100), &None, &None, &None); let result = client.try_withdraw(&500); assert!(result.is_err(), "expected error for insufficient balance"); } -/// Withdrawing zero or negative fails. #[test] fn withdraw_zero_fails() { let env = Env::default(); let owner = Address::generate(&env); let contract_id = env.register(CalloraVault {}, ()); let client = CalloraVaultClient::new(&env, &contract_id); - let usdc_token = Address::generate(&env); + let (usdc_token, _, usdc_admin) = create_usdc(&env, &owner); env.mock_all_auths(); - client.init(&owner, &usdc_token, &Some(100), &None); + fund_vault(&usdc_admin, &contract_id, 100); + client.init(&owner, &usdc_token, &Some(100), &None, &None, &None); let result = client.try_withdraw(&0); assert!(result.is_err(), "expected error for zero amount"); } -// --------------------------------------------------------------------------- -// withdraw_to -// --------------------------------------------------------------------------- - -/// Owner can withdraw to a specified address. #[test] fn withdraw_to_reduces_balance() { let env = Env::default(); @@ -646,17 +679,17 @@ fn withdraw_to_reduces_balance() { let recipient = Address::generate(&env); let contract_id = env.register(CalloraVault {}, ()); let client = CalloraVaultClient::new(&env, &contract_id); - let usdc_token = Address::generate(&env); + let (usdc_token, _, usdc_admin) = create_usdc(&env, &owner); env.mock_all_auths(); - client.init(&owner, &usdc_token, &Some(500), &None); + fund_vault(&usdc_admin, &contract_id, 500); + client.init(&owner, &usdc_token, &Some(500), &None, &None, &None); let remaining = client.withdraw_to(&recipient, &150); assert_eq!(remaining, 350); assert_eq!(client.balance(), 350); } -/// Withdrawing to address with insufficient balance fails. #[test] fn withdraw_to_insufficient_balance_fails() { let env = Env::default(); @@ -664,67 +697,143 @@ fn withdraw_to_insufficient_balance_fails() { let recipient = Address::generate(&env); let contract_id = env.register(CalloraVault {}, ()); let client = CalloraVaultClient::new(&env, &contract_id); - let usdc_token = Address::generate(&env); + let (usdc_token, _, usdc_admin) = create_usdc(&env, &owner); env.mock_all_auths(); - client.init(&owner, &usdc_token, &Some(100), &None); + fund_vault(&usdc_admin, &contract_id, 100); + client.init(&owner, &usdc_token, &Some(100), &None, &None, &None); let result = client.try_withdraw_to(&recipient, &500); assert!(result.is_err(), "expected error for insufficient balance"); } -// --------------------------------------------------------------------------- -// min_deposit -// --------------------------------------------------------------------------- - -/// Deposits below min_deposit are rejected. #[test] fn deposit_below_minimum_fails() { let env = Env::default(); let owner = Address::generate(&env); + let depositor = Address::generate(&env); let contract_id = env.register(CalloraVault {}, ()); let client = CalloraVaultClient::new(&env, &contract_id); - let usdc_token = Address::generate(&env); + let (usdc_token, _, usdc_admin) = create_usdc(&env, &owner); env.mock_all_auths(); - client.init(&owner, &usdc_token, &Some(100), &Some(50)); // min_deposit = 50 + fund_vault(&usdc_admin, &contract_id, 100); + client.init(&owner, &usdc_token, &Some(100), &Some(50), &None, &None); - let result = client.try_deposit(&30); // below minimum + fund_vault(&usdc_admin, &depositor, 30); + let usdc_client = token::Client::new(&env, &usdc_token); + usdc_client.approve(&depositor, &contract_id, &30, &1000); + let result = client.try_deposit(&depositor, &30); assert!(result.is_err(), "expected error for deposit below minimum"); } -/// Deposits at or above min_deposit succeed. #[test] fn deposit_at_minimum_succeeds() { let env = Env::default(); let owner = Address::generate(&env); + let depositor = Address::generate(&env); let contract_id = env.register(CalloraVault {}, ()); let client = CalloraVaultClient::new(&env, &contract_id); - let usdc_token = Address::generate(&env); + let (usdc_token, _, usdc_admin) = create_usdc(&env, &owner); env.mock_all_auths(); - client.init(&owner, &usdc_token, &Some(100), &Some(50)); // min_deposit = 50 + fund_vault(&usdc_admin, &contract_id, 100); + client.init(&owner, &usdc_token, &Some(100), &Some(50), &None, &None); - let new_balance = client.deposit(&50); + fund_vault(&usdc_admin, &depositor, 50); + let usdc_client = token::Client::new(&env, &usdc_token); + usdc_client.approve(&depositor, &contract_id, &50, &1000); + let new_balance = client.deposit(&depositor, &50); assert_eq!(new_balance, 150); } -// --------------------------------------------------------------------------- -// double init guard -// --------------------------------------------------------------------------- - -/// Calling init twice fails. #[test] fn double_init_fails() { let env = Env::default(); let owner = Address::generate(&env); let contract_id = env.register(CalloraVault {}, ()); let client = CalloraVaultClient::new(&env, &contract_id); - let usdc_token = Address::generate(&env); + let (usdc_token, _, usdc_admin) = create_usdc(&env, &owner); env.mock_all_auths(); - client.init(&owner, &usdc_token, &Some(100), &None); + fund_vault(&usdc_admin, &contract_id, 100); + client.init(&owner, &usdc_token, &Some(100), &None, &None, &None); - let result = client.try_init(&owner, &usdc_token, &Some(200), &None); + let result = client.try_init(&owner, &usdc_token, &Some(200), &None, &None, &None); assert!(result.is_err(), "expected error for double init"); } + +#[test] +fn init_insufficient_usdc_balance_fails() { + let env = Env::default(); + let owner = Address::generate(&env); + let contract_id = env.register(CalloraVault {}, ()); + let client = CalloraVaultClient::new(&env, &contract_id); + let (usdc_token, _, usdc_admin) = create_usdc(&env, &owner); + + env.mock_all_auths(); + fund_vault(&usdc_admin, &contract_id, 50); + + let result = client.try_init(&owner, &usdc_token, &Some(100), &None, &None, &None); + assert!(result.is_err(), "expected error when initial_balance exceeds contract USDC"); +} + +#[test] +fn init_with_zero_max_deduct_fails() { + let env = Env::default(); + let owner = Address::generate(&env); + let contract_id = env.register(CalloraVault {}, ()); + let client = CalloraVaultClient::new(&env, &contract_id); + let (usdc_token, _, _) = create_usdc(&env, &owner); + + env.mock_all_auths(); + + let result = client.try_init(&owner, &usdc_token, &None, &None, &None, &Some(0)); + assert!(result.is_err(), "expected error for max_deduct <= 0"); +} + +#[test] +fn init_with_revenue_pool_and_get_revenue_pool() { + let env = Env::default(); + let owner = Address::generate(&env); + let revenue_pool = Address::generate(&env); + let contract_id = env.register(CalloraVault {}, ()); + let client = CalloraVaultClient::new(&env, &contract_id); + let (usdc_token, _, _) = create_usdc(&env, &owner); + + env.mock_all_auths(); + client.init(&owner, &usdc_token, &None, &None, &Some(revenue_pool.clone()), &None); + + let retrieved_pool = client.get_revenue_pool(); + assert_eq!(retrieved_pool, Some(revenue_pool)); +} + +#[test] +fn get_revenue_pool_returns_none_when_not_set() { + let env = Env::default(); + let owner = Address::generate(&env); + let contract_id = env.register(CalloraVault {}, ()); + let client = CalloraVaultClient::new(&env, &contract_id); + let (usdc_token, _, _) = create_usdc(&env, &owner); + + env.mock_all_auths(); + client.init(&owner, &usdc_token, &None, &None, &None, &None); + + let retrieved_pool = client.get_revenue_pool(); + assert_eq!(retrieved_pool, None); +} + +#[test] +fn get_max_deduct_returns_configured_value() { + let env = Env::default(); + let owner = Address::generate(&env); + let contract_id = env.register(CalloraVault {}, ()); + let client = CalloraVaultClient::new(&env, &contract_id); + let (usdc_token, _, _) = create_usdc(&env, &owner); + + env.mock_all_auths(); + client.init(&owner, &usdc_token, &None, &None, &None, &Some(5000)); + + let max_deduct = client.get_max_deduct(); + assert_eq!(max_deduct, 5000); +} diff --git a/coverage/cobertura.xml b/coverage/cobertura.xml index a604edb..0817e0e 100644 --- a/coverage/cobertura.xml +++ b/coverage/cobertura.xml @@ -1 +1 @@ -/home/jeffersonyouashi/Documents/DRIPS/Callora-Contracts \ No newline at end of file +/home/jeffersonyouashi/Documents/DRIPS/Callora-Contracts \ No newline at end of file diff --git a/coverage/tarpaulin-report.html b/coverage/tarpaulin-report.html index 719cb7e..dd1615d 100644 --- a/coverage/tarpaulin-report.html +++ b/coverage/tarpaulin-report.html @@ -193,8 +193,8 @@