#[cfg(test)] mod tests { use crate::teller::TellerState; use crate::types::{Deposit, Withdraw}; use common_data::asset::Asset; use near_sdk::json_types::U128; use near_sdk::test_utils::accounts; use near_sdk::AccountId; // ============================================================ // Test Helpers // ============================================================ /// Create a fresh TellerState for testing fn create_test_teller() -> TellerState { TellerState::new() } /// Create a USDC fungible token asset fn create_usdc_asset() -> Asset { Asset::FungibleToken { contract_id: accounts(1), } } /// Create a test deposit fn create_test_deposit(owner: AccountId, asset: Asset, amount: u128) -> Deposit { Deposit::new( asset, U128(amount), U128(amount * 95 / 100), // 5% slippage (min_shares) owner.clone(), owner, // receiver_id Some("test deposit".to_string()), ) } /// Create a test withdrawal fn create_test_withdraw( owner: AccountId, asset: Asset, shares: u128, activation_ns: u128, ) -> Withdraw { Withdraw::new( asset, U128(shares), U128(shares * 95 / 100), // 5% slippage (min_asset_amount) owner.clone(), owner, // receiver_id Some("test withdraw".to_string()), U128(activation_ns), ) } /// Test account helpers fn alice() -> AccountId { accounts(0) } fn bob() -> AccountId { accounts(1) } fn charlie() -> AccountId { accounts(2) } // ============================================================ // Operation Lifecycle Tests // ============================================================ #[test] fn test_sequential_id_assignment_for_deposits() { let mut teller = create_test_teller(); let asset = create_usdc_asset(); let owner = alice(); let deposit1 = create_test_deposit(owner.clone(), asset.clone(), 1000); let deposit2 = create_test_deposit(owner.clone(), asset.clone(), 2000); let deposit3 = create_test_deposit(owner.clone(), asset.clone(), 3000); let id1 = teller.add_deposit(deposit1); let id2 = teller.add_deposit(deposit2); let id3 = teller.add_deposit(deposit3); assert_eq!(id1, 1, "First deposit should get ID 1"); assert_eq!(id2, 2, "Second deposit should get ID 2"); assert_eq!(id3, 3, "Third deposit should get ID 3"); } #[test] fn test_sequential_id_assignment_for_withdrawals() { let mut teller = create_test_teller(); let asset = create_usdc_asset(); let owner = alice(); let withdraw1 = create_test_withdraw(owner.clone(), asset.clone(), 1000, 1000); let withdraw2 = create_test_withdraw(owner.clone(), asset.clone(), 2000, 2000); let withdraw3 = create_test_withdraw(owner.clone(), asset.clone(), 3000, 3000); let id1 = teller.add_withdraw(withdraw1); let id2 = teller.add_withdraw(withdraw2); let id3 = teller.add_withdraw(withdraw3); assert_eq!(id1, 1, "First withdrawal should get ID 1"); assert_eq!(id2, 2, "Second withdrawal should get ID 2"); assert_eq!(id3, 3, "Third withdrawal should get ID 3"); } #[test] fn test_shared_counter_between_deposits_and_withdrawals() { let mut teller = create_test_teller(); let asset = create_usdc_asset(); let owner = alice(); let deposit1 = create_test_deposit(owner.clone(), asset.clone(), 1000); let withdraw1 = create_test_withdraw(owner.clone(), asset.clone(), 2000, 1000); let deposit2 = create_test_deposit(owner.clone(), asset.clone(), 3000); let deposit_id1 = teller.add_deposit(deposit1); let withdraw_id1 = teller.add_withdraw(withdraw1); let deposit_id2 = teller.add_deposit(deposit2); assert_eq!(deposit_id1, 1, "First deposit gets ID 1"); assert_eq!(withdraw_id1, 2, "First withdrawal gets ID 2"); assert_eq!(deposit_id2, 3, "Second deposit gets ID 3"); } #[test] fn test_add_and_retrieve_deposit_with_correct_data() { let mut teller = create_test_teller(); let asset = create_usdc_asset(); let owner = alice(); let amount = 1_000_000u128; let deposit = create_test_deposit(owner.clone(), asset.clone(), amount); let operation_id = teller.add_deposit(deposit.clone()); let retrieved = teller.get_deposit(operation_id); assert!(retrieved.is_some(), "Deposit should be retrievable"); let retrieved_deposit = retrieved.unwrap(); assert_eq!( retrieved_deposit.deposit_amount.0, amount, "Deposit amount should match" ); assert_eq!(retrieved_deposit.owner_id, owner, "Owner ID should match"); assert_eq!(retrieved_deposit.asset, asset, "Asset should match"); } #[test] fn test_add_and_retrieve_withdrawal_with_correct_data() { let mut teller = create_test_teller(); let asset = create_usdc_asset(); let owner = alice(); let shares = 2_000_000u128; let activation = 1_000_000_000u128; let withdraw = create_test_withdraw(owner.clone(), asset.clone(), shares, activation); let operation_id = teller.add_withdraw(withdraw.clone()); let retrieved = teller.get_withdraw(operation_id); assert!(retrieved.is_some(), "Withdrawal should be retrievable"); let retrieved_withdraw = retrieved.unwrap(); assert_eq!(retrieved_withdraw.shares.0, shares, "Shares should match"); assert_eq!(retrieved_withdraw.owner_id, owner, "Owner ID should match"); assert_eq!(retrieved_withdraw.asset, asset, "Asset should match"); assert_eq!( retrieved_withdraw.activation_timestamp_nanosec.0, activation, "Activation timestamp should match" ); } #[test] fn test_remove_deposit_successfully() { let mut teller = create_test_teller(); let asset = create_usdc_asset(); let owner = alice(); let deposit = create_test_deposit(owner.clone(), asset.clone(), 1000); let operation_id = teller.add_deposit(deposit); assert_eq!(teller.deposits_count(), 1, "Should have 1 deposit"); let removed = teller.remove_deposit(operation_id, &owner); assert!(removed.is_some(), "Should successfully remove deposit"); assert_eq!( teller.deposits_count(), 0, "Should have 0 deposits after removal" ); } #[test] fn test_remove_withdrawal_successfully() { let mut teller = create_test_teller(); let asset = create_usdc_asset(); let owner = alice(); let withdraw = create_test_withdraw(owner.clone(), asset.clone(), 2000, 1000); let operation_id = teller.add_withdraw(withdraw); assert_eq!(teller.withdraws_count(), 1, "Should have 1 withdrawal"); let removed = teller.remove_withdraw(operation_id, &owner); assert!(removed.is_some(), "Should successfully remove withdrawal"); assert_eq!( teller.withdraws_count(), 0, "Should have 0 withdrawals after removal" ); } #[test] fn test_double_removal_returns_none() { let mut teller = create_test_teller(); let asset = create_usdc_asset(); let owner = alice(); let deposit = create_test_deposit(owner.clone(), asset.clone(), 1000); let operation_id = teller.add_deposit(deposit); let first_removal = teller.remove_deposit(operation_id, &owner); assert!(first_removal.is_some(), "First removal should succeed"); let second_removal = teller.remove_deposit(operation_id, &owner); assert!( second_removal.is_none(), "Second removal should return None" ); } #[test] fn test_get_non_existent_operation_returns_none() { let teller = create_test_teller(); assert!( teller.get_deposit(999).is_none(), "Non-existent deposit should return None" ); assert!( teller.get_withdraw(999).is_none(), "Non-existent withdrawal should return None" ); } #[test] fn test_counts_update_correctly_after_add_remove() { let mut teller = create_test_teller(); let asset = create_usdc_asset(); let owner = alice(); assert_eq!( teller.deposits_count(), 0, "Initial deposit count should be 0" ); assert_eq!( teller.withdraws_count(), 0, "Initial withdrawal count should be 0" ); // Add deposits let deposit1 = create_test_deposit(owner.clone(), asset.clone(), 1000); let deposit2 = create_test_deposit(owner.clone(), asset.clone(), 2000); let id1 = teller.add_deposit(deposit1); let _id2 = teller.add_deposit(deposit2); assert_eq!(teller.deposits_count(), 2, "Should have 2 deposits"); // Add withdrawals let withdraw1 = create_test_withdraw(owner.clone(), asset.clone(), 1000, 1000); let id3 = teller.add_withdraw(withdraw1); assert_eq!(teller.withdraws_count(), 1, "Should have 1 withdrawal"); // Remove one deposit teller.remove_deposit(id1, &owner); assert_eq!( teller.deposits_count(), 1, "Should have 1 deposit after removal" ); // Remove withdrawal teller.remove_withdraw(id3, &owner); assert_eq!( teller.withdraws_count(), 0, "Should have 0 withdrawals after removal" ); } #[test] fn test_first_operation_gets_id_one() { let mut teller = create_test_teller(); let asset = create_usdc_asset(); let owner = alice(); let deposit = create_test_deposit(owner.clone(), asset.clone(), 1000); let operation_id = teller.add_deposit(deposit); assert_eq!(operation_id, 1, "First operation should get ID 1 (not 0)"); } #[test] fn test_counter_doesnt_overflow_with_many_operations() { let mut teller = create_test_teller(); let asset = create_usdc_asset(); let owner = alice(); // Add 150 operations to test counter stability for i in 0..150 { let deposit = create_test_deposit(owner.clone(), asset.clone(), i as u128 * 1000); teller.add_deposit(deposit); } assert_eq!(teller.deposits_count(), 150, "Should have 150 deposits"); assert_eq!( teller.next_operation_id, 151, "Next operation ID should be 151" ); } // ============================================================ // User Tracking Tests // ============================================================ #[test] fn test_single_user_multiple_deposits_tracked_correctly() { let mut teller = create_test_teller(); let asset = create_usdc_asset(); let owner = alice(); let deposit1 = create_test_deposit(owner.clone(), asset.clone(), 1000); let deposit2 = create_test_deposit(owner.clone(), asset.clone(), 2000); let deposit3 = create_test_deposit(owner.clone(), asset.clone(), 3000); let id1 = teller.add_deposit(deposit1); let id2 = teller.add_deposit(deposit2); let id3 = teller.add_deposit(deposit3); let user_deposits = teller.get_user_deposit_ids(&owner); assert_eq!( user_deposits.len(), 3, "User should have 3 deposit operations" ); assert!( user_deposits.contains(&id1), "Should contain first deposit ID" ); assert!( user_deposits.contains(&id2), "Should contain second deposit ID" ); assert!( user_deposits.contains(&id3), "Should contain third deposit ID" ); } #[test] fn test_single_user_multiple_withdrawals_tracked_correctly() { let mut teller = create_test_teller(); let asset = create_usdc_asset(); let owner = alice(); let withdraw1 = create_test_withdraw(owner.clone(), asset.clone(), 1000, 1000); let withdraw2 = create_test_withdraw(owner.clone(), asset.clone(), 2000, 2000); let id1 = teller.add_withdraw(withdraw1); let id2 = teller.add_withdraw(withdraw2); let user_withdrawals = teller.get_user_withdraw_ids(&owner); assert_eq!( user_withdrawals.len(), 2, "User should have 2 withdrawal operations" ); assert!( user_withdrawals.contains(&id1), "Should contain first withdrawal ID" ); assert!( user_withdrawals.contains(&id2), "Should contain second withdrawal ID" ); } #[test] fn test_multiple_users_tracked_independently() { let mut teller = create_test_teller(); let asset = create_usdc_asset(); // Alice deposits let alice_deposit = create_test_deposit(alice(), asset.clone(), 1000); let alice_id = teller.add_deposit(alice_deposit); // Bob deposits let bob_deposit1 = create_test_deposit(bob(), asset.clone(), 2000); let bob_deposit2 = create_test_deposit(bob(), asset.clone(), 3000); let bob_id1 = teller.add_deposit(bob_deposit1); let bob_id2 = teller.add_deposit(bob_deposit2); // Charlie withdraws let charlie_withdraw = create_test_withdraw(charlie(), asset.clone(), 4000, 1000); let charlie_id = teller.add_withdraw(charlie_withdraw); // Verify Alice's operations let alice_deposits = teller.get_user_deposit_ids(&alice()); assert_eq!(alice_deposits.len(), 1, "Alice should have 1 deposit"); assert!(alice_deposits.contains(&alice_id)); // Verify Bob's operations let bob_deposits = teller.get_user_deposit_ids(&bob()); assert_eq!(bob_deposits.len(), 2, "Bob should have 2 deposits"); assert!(bob_deposits.contains(&bob_id1)); assert!(bob_deposits.contains(&bob_id2)); // Verify Charlie's operations let charlie_withdrawals = teller.get_user_withdraw_ids(&charlie()); assert_eq!( charlie_withdrawals.len(), 1, "Charlie should have 1 withdrawal" ); assert!(charlie_withdrawals.contains(&charlie_id)); } #[test] fn test_user_with_no_operations_returns_empty_vec() { let teller = create_test_teller(); let owner = alice(); let user_deposits = teller.get_user_deposit_ids(&owner); let user_withdrawals = teller.get_user_withdraw_ids(&owner); assert!( user_deposits.is_empty(), "User with no deposits should return empty vec" ); assert!( user_withdrawals.is_empty(), "User with no withdrawals should return empty vec" ); } #[test] fn test_user_tracking_cleanup_after_last_operation_removed() { let mut teller = create_test_teller(); let asset = create_usdc_asset(); let owner = alice(); // Add two deposits let deposit1 = create_test_deposit(owner.clone(), asset.clone(), 1000); let deposit2 = create_test_deposit(owner.clone(), asset.clone(), 2000); let id1 = teller.add_deposit(deposit1); let id2 = teller.add_deposit(deposit2); assert_eq!( teller.get_user_deposit_ids(&owner).len(), 2, "Should have 2 deposits" ); // Remove first deposit teller.remove_deposit(id1, &owner); assert_eq!( teller.get_user_deposit_ids(&owner).len(), 1, "Should have 1 deposit after first removal" ); // Remove second deposit (last one) teller.remove_deposit(id2, &owner); let user_deposits = teller.get_user_deposit_ids(&owner); assert!( user_deposits.is_empty(), "User tracking should be cleaned up after last operation removed" ); } #[test] fn test_user_tracking_persists_across_add_remove_cycles() { let mut teller = create_test_teller(); let asset = create_usdc_asset(); let owner = alice(); // First cycle let deposit1 = create_test_deposit(owner.clone(), asset.clone(), 1000); let id1 = teller.add_deposit(deposit1); teller.remove_deposit(id1, &owner); // Second cycle let deposit2 = create_test_deposit(owner.clone(), asset.clone(), 2000); let id2 = teller.add_deposit(deposit2); let user_deposits = teller.get_user_deposit_ids(&owner); assert_eq!( user_deposits.len(), 1, "User should have 1 deposit from second cycle" ); assert!(user_deposits.contains(&id2)); } #[test] fn test_large_number_of_operations_per_user() { let mut teller = create_test_teller(); let asset = create_usdc_asset(); let owner = alice(); // Add 100 deposits for one user let mut expected_ids = Vec::new(); for i in 0..100 { let deposit = create_test_deposit(owner.clone(), asset.clone(), i * 1000); let id = teller.add_deposit(deposit); expected_ids.push(id); } let user_deposits = teller.get_user_deposit_ids(&owner); assert_eq!( user_deposits.len(), 100, "User should have 100 deposit operations" ); // Verify all IDs are present for expected_id in expected_ids { assert!( user_deposits.contains(&expected_id), "Should contain deposit ID {}", expected_id ); } } // ============================================================ // Confirmation System Tests (Withdrawals Only) // ============================================================ #[test] fn test_new_withdrawals_start_unconfirmed() { let mut teller = create_test_teller(); let asset = create_usdc_asset(); let owner = alice(); let withdraw = create_test_withdraw(owner.clone(), asset.clone(), 1000, 1000); let operation_id = teller.add_withdraw(withdraw); assert!( !teller.is_withdraw_confirmed(operation_id), "New withdrawal should start unconfirmed" ); let retrieved = teller.get_withdraw(operation_id).unwrap(); assert!(!retrieved.confirmed, "Confirmed flag should be false"); assert!( retrieved.confirmed_share_price.is_none(), "Confirmed share price should be None" ); } #[test] fn test_mark_withdrawal_as_confirmed_stores_share_price() { let mut teller = create_test_teller(); let asset = create_usdc_asset(); let owner = alice(); let mut withdraw = create_test_withdraw(owner.clone(), asset.clone(), 1000, 1000); let operation_id = teller.add_withdraw(withdraw.clone()); // Mark as confirmed with share price let locked_price = U128(1_500_000); withdraw.confirmed = true; withdraw.confirmed_share_price = Some(locked_price); // Update in teller (simulate confirmation) teller.pending_withdraws.insert(operation_id, withdraw); assert!( teller.is_withdraw_confirmed(operation_id), "Withdrawal should be confirmed" ); assert_eq!( teller.get_confirmed_share_price(operation_id), Some(locked_price), "Should return locked share price" ); } #[test] fn test_get_unconfirmed_withdraws_returns_only_unconfirmed() { let mut teller = create_test_teller(); let asset = create_usdc_asset(); let owner = alice(); // Add unconfirmed withdrawal let withdraw1 = create_test_withdraw(owner.clone(), asset.clone(), 1000, 1000); let id1 = teller.add_withdraw(withdraw1); // Add confirmed withdrawal let mut withdraw2 = create_test_withdraw(owner.clone(), asset.clone(), 2000, 2000); withdraw2.confirmed = true; withdraw2.confirmed_share_price = Some(U128(1_500_000)); let id2 = teller.add_withdraw(withdraw2.clone()); teller.pending_withdraws.insert(id2, withdraw2); let unconfirmed = teller.get_unconfirmed_withdraws(); assert_eq!( unconfirmed.len(), 1, "Should return only 1 unconfirmed withdrawal" ); assert_eq!(unconfirmed[0].0, id1, "Should be the first withdrawal ID"); } #[test] fn test_get_confirmed_withdraws_returns_only_confirmed() { let mut teller = create_test_teller(); let asset = create_usdc_asset(); let owner = alice(); // Add unconfirmed withdrawal let withdraw1 = create_test_withdraw(owner.clone(), asset.clone(), 1000, 1000); teller.add_withdraw(withdraw1); // Add confirmed withdrawals let mut withdraw2 = create_test_withdraw(owner.clone(), asset.clone(), 2000, 2000); withdraw2.confirmed = true; withdraw2.confirmed_share_price = Some(U128(1_500_000)); let id2 = teller.add_withdraw(withdraw2.clone()); teller.pending_withdraws.insert(id2, withdraw2); let mut withdraw3 = create_test_withdraw(owner.clone(), asset.clone(), 3000, 3000); withdraw3.confirmed = true; withdraw3.confirmed_share_price = Some(U128(1_600_000)); let id3 = teller.add_withdraw(withdraw3.clone()); teller.pending_withdraws.insert(id3, withdraw3); let confirmed = teller.get_confirmed_withdraws(); assert_eq!(confirmed.len(), 2, "Should return 2 confirmed withdrawals"); } #[test] fn test_is_withdraw_confirmed_returns_correct_state() { let mut teller = create_test_teller(); let asset = create_usdc_asset(); let owner = alice(); // Add unconfirmed let withdraw1 = create_test_withdraw(owner.clone(), asset.clone(), 1000, 1000); let id1 = teller.add_withdraw(withdraw1); // Add confirmed let mut withdraw2 = create_test_withdraw(owner.clone(), asset.clone(), 2000, 2000); withdraw2.confirmed = true; let id2 = teller.add_withdraw(withdraw2.clone()); teller.pending_withdraws.insert(id2, withdraw2); assert!( !teller.is_withdraw_confirmed(id1), "First withdrawal should be unconfirmed" ); assert!( teller.is_withdraw_confirmed(id2), "Second withdrawal should be confirmed" ); assert!( !teller.is_withdraw_confirmed(999), "Non-existent withdrawal should return false" ); } #[test] fn test_get_confirmed_share_price_returns_correct_value() { let mut teller = create_test_teller(); let asset = create_usdc_asset(); let owner = alice(); let locked_price = U128(1_750_000); let mut withdraw = create_test_withdraw(owner.clone(), asset.clone(), 1000, 1000); withdraw.confirmed = true; withdraw.confirmed_share_price = Some(locked_price); let operation_id = teller.add_withdraw(withdraw.clone()); teller.pending_withdraws.insert(operation_id, withdraw); assert_eq!( teller.get_confirmed_share_price(operation_id), Some(locked_price), "Should return the locked share price" ); } #[test] fn test_get_confirmed_share_price_returns_none_for_unconfirmed() { let mut teller = create_test_teller(); let asset = create_usdc_asset(); let owner = alice(); let withdraw = create_test_withdraw(owner.clone(), asset.clone(), 1000, 1000); let operation_id = teller.add_withdraw(withdraw); assert_eq!( teller.get_confirmed_share_price(operation_id), None, "Unconfirmed withdrawal should return None for share price" ); } #[test] fn test_confirmation_does_not_affect_deposits() { let mut teller = create_test_teller(); let asset = create_usdc_asset(); let owner = alice(); let deposit = create_test_deposit(owner.clone(), asset.clone(), 1000); let deposit_id = teller.add_deposit(deposit); // Deposits don't have confirmation state // This test verifies that querying withdrawal confirmation doesn't affect deposits assert_eq!( teller.get_confirmed_share_price(deposit_id), None, "Deposits don't have confirmed share price" ); assert!( !teller.is_withdraw_confirmed(deposit_id), "Deposit ID should return false for withdrawal confirmation" ); } // ============================================================ // Restoration Tests // ============================================================ #[test] fn test_restore_deposit_with_new_id_succeeds() { let mut teller = create_test_teller(); let asset = create_usdc_asset(); let owner = alice(); let deposit = create_test_deposit(owner.clone(), asset.clone(), 1000); let operation_id = 42; let success = teller.restore_deposit(operation_id, deposit.clone()); assert!(success, "Restore should succeed with new ID"); assert_eq!( teller.deposits_count(), 1, "Should have 1 deposit after restoration" ); let retrieved = teller.get_deposit(operation_id); assert!( retrieved.is_some(), "Restored deposit should be retrievable" ); assert_eq!( retrieved.unwrap().deposit_amount.0, deposit.deposit_amount.0 ); } #[test] fn test_restore_withdraw_with_new_id_succeeds() { let mut teller = create_test_teller(); let asset = create_usdc_asset(); let owner = alice(); let withdraw = create_test_withdraw(owner.clone(), asset.clone(), 1000, 1000); let operation_id = 99; let success = teller.restore_withdraw(operation_id, withdraw.clone()); assert!(success, "Restore should succeed with new ID"); assert_eq!( teller.withdraws_count(), 1, "Should have 1 withdrawal after restoration" ); let retrieved = teller.get_withdraw(operation_id); assert!( retrieved.is_some(), "Restored withdrawal should be retrievable" ); assert_eq!(retrieved.unwrap().shares.0, withdraw.shares.0); } #[test] fn test_restore_deposit_with_existing_id_fails() { let mut teller = create_test_teller(); let asset = create_usdc_asset(); let owner = alice(); let deposit1 = create_test_deposit(owner.clone(), asset.clone(), 1000); let operation_id = teller.add_deposit(deposit1); let deposit2 = create_test_deposit(owner.clone(), asset.clone(), 2000); let success = teller.restore_deposit(operation_id, deposit2); assert!(!success, "Restore should fail with existing ID"); assert_eq!( teller.deposits_count(), 1, "Should still have only 1 deposit" ); // Original deposit should remain unchanged let retrieved = teller.get_deposit(operation_id).unwrap(); assert_eq!(retrieved.deposit_amount.0, 1000); } #[test] fn test_restore_withdraw_with_existing_id_fails() { let mut teller = create_test_teller(); let asset = create_usdc_asset(); let owner = alice(); let withdraw1 = create_test_withdraw(owner.clone(), asset.clone(), 1000, 1000); let operation_id = teller.add_withdraw(withdraw1); let withdraw2 = create_test_withdraw(owner.clone(), asset.clone(), 2000, 2000); let success = teller.restore_withdraw(operation_id, withdraw2); assert!(!success, "Restore should fail with existing ID"); assert_eq!( teller.withdraws_count(), 1, "Should still have only 1 withdrawal" ); // Original withdrawal should remain unchanged let retrieved = teller.get_withdraw(operation_id).unwrap(); assert_eq!(retrieved.shares.0, 1000); } #[test] fn test_restored_operations_appear_in_user_tracking() { let mut teller = create_test_teller(); let asset = create_usdc_asset(); let owner = alice(); let deposit = create_test_deposit(owner.clone(), asset.clone(), 1000); let operation_id = 50; teller.restore_deposit(operation_id, deposit); let user_deposits = teller.get_user_deposit_ids(&owner); assert_eq!( user_deposits.len(), 1, "Restored deposit should appear in user tracking" ); assert!(user_deposits.contains(&operation_id)); } #[test] fn test_restored_operations_retrievable_via_get_methods() { let mut teller = create_test_teller(); let asset = create_usdc_asset(); let owner = alice(); let deposit = create_test_deposit(owner.clone(), asset.clone(), 1000); let withdraw = create_test_withdraw(owner.clone(), asset.clone(), 2000, 1000); teller.restore_deposit(10, deposit.clone()); teller.restore_withdraw(20, withdraw.clone()); assert!( teller.get_deposit(10).is_some(), "Restored deposit should be retrievable" ); assert!( teller.get_withdraw(20).is_some(), "Restored withdrawal should be retrievable" ); } #[test] fn test_restoration_does_not_affect_operation_counter() { let mut teller = create_test_teller(); let asset = create_usdc_asset(); let owner = alice(); // Add normal deposit (ID 1) let deposit1 = create_test_deposit(owner.clone(), asset.clone(), 1000); teller.add_deposit(deposit1); // Restore with high ID let deposit2 = create_test_deposit(owner.clone(), asset.clone(), 2000); teller.restore_deposit(999, deposit2); // Next normal deposit should still get ID 2 let deposit3 = create_test_deposit(owner.clone(), asset.clone(), 3000); let next_id = teller.add_deposit(deposit3); assert_eq!( next_id, 2, "Counter should not be affected by restoration with high ID" ); } #[test] fn test_restore_then_remove_then_restore_same_id() { let mut teller = create_test_teller(); let asset = create_usdc_asset(); let owner = alice(); let deposit1 = create_test_deposit(owner.clone(), asset.clone(), 1000); let operation_id = 25; // First restoration let success1 = teller.restore_deposit(operation_id, deposit1.clone()); assert!(success1, "First restoration should succeed"); // Remove teller.remove_deposit(operation_id, &owner); assert!( teller.get_deposit(operation_id).is_none(), "Deposit should be removed" ); // Second restoration with same ID let deposit2 = create_test_deposit(owner.clone(), asset.clone(), 2000); let success2 = teller.restore_deposit(operation_id, deposit2); assert!(success2, "Second restoration should succeed after removal"); let retrieved = teller.get_deposit(operation_id).unwrap(); assert_eq!( retrieved.deposit_amount.0, 2000, "Should have the new deposit amount" ); } // ============================================================ // Batch Operations Tests // ============================================================ #[test] fn test_get_all_pending_deposits_returns_all_deposits() { let mut teller = create_test_teller(); let asset = create_usdc_asset(); let owner = alice(); let deposit1 = create_test_deposit(owner.clone(), asset.clone(), 1000); let deposit2 = create_test_deposit(owner.clone(), asset.clone(), 2000); let deposit3 = create_test_deposit(owner.clone(), asset.clone(), 3000); teller.add_deposit(deposit1); teller.add_deposit(deposit2); teller.add_deposit(deposit3); let all_deposits = teller.get_all_pending_deposits(); assert_eq!( all_deposits.len(), 3, "Should return all 3 pending deposits" ); } #[test] fn test_get_all_pending_withdraws_returns_all_withdrawals() { let mut teller = create_test_teller(); let asset = create_usdc_asset(); let owner = alice(); let withdraw1 = create_test_withdraw(owner.clone(), asset.clone(), 1000, 1000); let withdraw2 = create_test_withdraw(owner.clone(), asset.clone(), 2000, 2000); teller.add_withdraw(withdraw1); teller.add_withdraw(withdraw2); let all_withdraws = teller.get_all_pending_withdraws(); assert_eq!( all_withdraws.len(), 2, "Should return all 2 pending withdrawals" ); } #[test] fn test_empty_queue_returns_empty_vec() { let teller = create_test_teller(); let deposits = teller.get_all_pending_deposits(); let withdraws = teller.get_all_pending_withdraws(); assert!(deposits.is_empty(), "Empty queue should return empty vec"); assert!(withdraws.is_empty(), "Empty queue should return empty vec"); } #[test] fn test_batch_retrieval_with_mixed_operations() { let mut teller = create_test_teller(); let asset = create_usdc_asset(); // Add mix of deposits and withdrawals from different users let deposit1 = create_test_deposit(alice(), asset.clone(), 1000); let withdraw1 = create_test_withdraw(bob(), asset.clone(), 2000, 1000); let deposit2 = create_test_deposit(charlie(), asset.clone(), 3000); teller.add_deposit(deposit1); teller.add_withdraw(withdraw1); teller.add_deposit(deposit2); let all_deposits = teller.get_all_pending_deposits(); let all_withdraws = teller.get_all_pending_withdraws(); assert_eq!(all_deposits.len(), 2, "Should have 2 deposits"); assert_eq!(all_withdraws.len(), 1, "Should have 1 withdrawal"); } #[test] fn test_withdraws_iter_covers_all_withdrawals() { let mut teller = create_test_teller(); let asset = create_usdc_asset(); let owner = alice(); let withdraw1 = create_test_withdraw(owner.clone(), asset.clone(), 1000, 1000); let withdraw2 = create_test_withdraw(owner.clone(), asset.clone(), 2000, 2000); let withdraw3 = create_test_withdraw(owner.clone(), asset.clone(), 3000, 3000); let id1 = teller.add_withdraw(withdraw1); let id2 = teller.add_withdraw(withdraw2); let id3 = teller.add_withdraw(withdraw3); let mut collected_ids: Vec = teller.withdraws_iter().map(|(id, _)| id).collect(); collected_ids.sort(); assert_eq!( collected_ids.len(), 3, "Iterator should cover all 3 withdrawals" ); assert_eq!(collected_ids, vec![id1, id2, id3]); } #[test] fn test_large_queue_retrieval() { let mut teller = create_test_teller(); let asset = create_usdc_asset(); let owner = alice(); // Add 100 deposits for i in 0..100 { let deposit = create_test_deposit(owner.clone(), asset.clone(), i * 1000); teller.add_deposit(deposit); } let all_deposits = teller.get_all_pending_deposits(); assert_eq!(all_deposits.len(), 100, "Should retrieve all 100 deposits"); } // ============================================================ // Edge Cases & Boundaries Tests // ============================================================ #[test] fn test_operation_id_zero_handling() { let teller = create_test_teller(); // ID 0 should return None (IDs start at 1) assert!( teller.get_deposit(0).is_none(), "Operation ID 0 should return None" ); assert!( teller.get_withdraw(0).is_none(), "Operation ID 0 should return None" ); } #[test] fn test_empty_state_initialization() { let teller = create_test_teller(); assert_eq!(teller.deposits_count(), 0, "Initial deposits should be 0"); assert_eq!( teller.withdraws_count(), 0, "Initial withdrawals should be 0" ); assert_eq!( teller.next_operation_id, 1, "Initial operation ID should be 1" ); } #[test] fn test_remove_with_non_existent_user_id() { let mut teller = create_test_teller(); let asset = create_usdc_asset(); let deposit = create_test_deposit(alice(), asset.clone(), 1000); let operation_id = teller.add_deposit(deposit); // Try to remove with different user ID let removed = teller.remove_deposit(operation_id, &bob()); // Teller doesn't validate ownership - it will still remove assert!( removed.is_some(), "Remove succeeds regardless of owner_id parameter" ); } #[test] fn test_query_user_operations_with_similar_account_ids() { let mut teller = create_test_teller(); let asset = create_usdc_asset(); // Create similar account IDs let user1: AccountId = "alice.near".parse().unwrap(); let user2: AccountId = "alice.near".parse().unwrap(); // Same ID let deposit1 = create_test_deposit(user1.clone(), asset.clone(), 1000); let deposit2 = create_test_deposit(user2.clone(), asset.clone(), 2000); let id1 = teller.add_deposit(deposit1); let id2 = teller.add_deposit(deposit2); let user1_ops = teller.get_user_deposit_ids(&user1); // Same account ID should have both operations assert_eq!( user1_ops.len(), 2, "Same account ID should have both deposits" ); assert!(user1_ops.contains(&id1)); assert!(user1_ops.contains(&id2)); } #[test] fn test_retrieve_after_removal_returns_none() { let mut teller = create_test_teller(); let asset = create_usdc_asset(); let owner = alice(); let deposit = create_test_deposit(owner.clone(), asset.clone(), 1000); let operation_id = teller.add_deposit(deposit); // Verify it exists assert!(teller.get_deposit(operation_id).is_some()); // Remove it teller.remove_deposit(operation_id, &owner); // Should return None after removal assert!( teller.get_deposit(operation_id).is_none(), "Should return None after removal" ); } #[test] fn test_very_large_operation_id() { let teller = create_test_teller(); // Query with very large ID (shouldn't panic, just return None) let result = teller.get_deposit(u64::MAX); assert!( result.is_none(), "Very large ID should return None without panic" ); } }