diff --git a/contracts/assetsup/src/detokenization.rs b/contracts/assetsup/src/detokenization.rs index c6f2078..d297ce5 100644 --- a/contracts/assetsup/src/detokenization.rs +++ b/contracts/assetsup/src/detokenization.rs @@ -73,7 +73,8 @@ pub fn execute_detokenization(env: &Env, asset_id: u64, proposal_id: u64) -> Res // Get list of all token holders before clearing let holders_list_key = TokenDataKey::TokenHoldersList(asset_id); - let holders = store.get::<_, soroban_sdk::Vec
>(&holders_list_key) + let holders = store + .get::<_, soroban_sdk::Vec
>(&holders_list_key) .ok_or(Error::AssetNotTokenized)?; // Remove all token holder records diff --git a/contracts/assetsup/src/lib.rs b/contracts/assetsup/src/lib.rs index 8639201..f65ef03 100644 --- a/contracts/assetsup/src/lib.rs +++ b/contracts/assetsup/src/lib.rs @@ -822,7 +822,10 @@ impl AssetUpContract { } /// Get a specific policy - pub fn get_insurance_policy(env: Env, policy_id: BytesN<32>) -> Option { + pub fn get_insurance_policy( + env: Env, + policy_id: BytesN<32>, + ) -> Option { insurance::get_policy(env, policy_id) } diff --git a/contracts/assetsup/src/tests/admin.rs b/contracts/assetsup/src/tests/admin.rs new file mode 100644 index 0000000..e6cbd37 --- /dev/null +++ b/contracts/assetsup/src/tests/admin.rs @@ -0,0 +1,105 @@ +use crate::tests::helpers::*; +use soroban_sdk::{testutils::Address as _, Address}; + +#[test] +fn test_update_admin_success() { + let env = create_env(); + let (admin, new_admin, _, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + client.update_admin(&new_admin); + + // Verify admin was updated + assert_eq!(client.get_admin(), new_admin); + + // Verify new admin is authorized registrar + assert!(client.is_authorized_registrar(&new_admin)); + + // Verify old admin is no longer authorized registrar + assert!(!client.is_authorized_registrar(&admin)); +} + +#[test] +#[should_panic(expected = "Error(Contract, #39)")] +fn test_update_admin_zero_address() { + let env = create_env(); + let admin = Address::generate(&env); + let client = initialize_contract(&env, &admin); + + let zero_address = Address::from_string(&soroban_sdk::String::from_str( + &env, + "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF", + )); + + env.mock_all_auths(); + + // Should panic with InvalidOwnerAddress error + client.update_admin(&zero_address); +} + +#[test] +fn test_pause_unpause_contract() { + let env = create_env(); + let admin = Address::generate(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + // Initially not paused + assert!(!client.is_paused()); + + // Pause contract + client.pause_contract(); + assert!(client.is_paused()); + + // Unpause contract + client.unpause_contract(); + assert!(!client.is_paused()); +} + +#[test] +fn test_add_authorized_registrar() { + let env = create_env(); + let (admin, user1, _, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + // Initially user1 is not authorized + assert!(!client.is_authorized_registrar(&user1)); + + // Add user1 as authorized registrar + client.add_authorized_registrar(&user1); + assert!(client.is_authorized_registrar(&user1)); +} + +#[test] +fn test_remove_authorized_registrar() { + let env = create_env(); + let (admin, user1, _, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + // Add user1 as authorized registrar + client.add_authorized_registrar(&user1); + assert!(client.is_authorized_registrar(&user1)); + + // Remove user1 from authorized registrars + client.remove_authorized_registrar(&user1); + assert!(!client.is_authorized_registrar(&user1)); +} + +#[test] +#[should_panic(expected = "Error(Contract, #8)")] +fn test_remove_admin_from_registrars() { + let env = create_env(); + let admin = Address::generate(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + // Should panic with Unauthorized error - cannot remove admin + client.remove_authorized_registrar(&admin); +} diff --git a/contracts/assetsup/src/tests/asset.rs b/contracts/assetsup/src/tests/asset.rs new file mode 100644 index 0000000..79872cb --- /dev/null +++ b/contracts/assetsup/src/tests/asset.rs @@ -0,0 +1,340 @@ +use crate::tests::helpers::*; +use crate::types::AssetStatus; +use soroban_sdk::{testutils::Address as _, Address, String, Vec}; + +#[test] +fn test_register_asset_success() { + let env = create_env(); + let (admin, user1, _, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + let asset_id = generate_asset_id(&env, 1); + let asset = create_test_asset(&env, &user1, asset_id.clone()); + + env.mock_all_auths(); + client.register_asset(&asset, &admin); + + // Verify asset was registered + let stored_asset = client.get_asset(&asset_id); + assert_eq!(stored_asset.id, asset_id); + assert_eq!(stored_asset.owner, user1); + + // Verify total asset count increased + assert_eq!(client.get_total_asset_count(), 1); + + // Verify asset is in owner's registry + let owner_assets = client.get_assets_by_owner(&user1); + assert_eq!(owner_assets.len(), 1); + assert_eq!(owner_assets.get(0).unwrap(), asset_id); +} + +#[test] +#[should_panic(expected = "Error(Contract, #3)")] +fn test_register_asset_already_exists() { + let env = create_env(); + let (admin, user1, _, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + let asset_id = generate_asset_id(&env, 1); + let asset = create_test_asset(&env, &user1, asset_id.clone()); + + env.mock_all_auths(); + client.register_asset(&asset, &admin); + + // Try to register same asset again - should panic with AssetAlreadyExists + client.register_asset(&asset, &admin); +} + +#[test] +#[should_panic(expected = "Error(Contract, #34)")] +fn test_register_asset_when_paused() { + let env = create_env(); + let (admin, user1, _, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + // Pause contract + client.pause_contract(); + + let asset_id = generate_asset_id(&env, 1); + let asset = create_test_asset(&env, &user1, asset_id); + + // Should panic with ContractPaused error + client.register_asset(&asset, &admin); +} + +#[test] +#[should_panic(expected = "Error(Contract, #8)")] +fn test_register_asset_unauthorized() { + let env = create_env(); + let (admin, user1, user2, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + let asset_id = generate_asset_id(&env, 1); + let asset = create_test_asset(&env, &user1, asset_id); + + env.mock_all_auths(); + + // user2 is not authorized registrar - should panic with Unauthorized + client.register_asset(&asset, &user2); +} + +#[test] +#[should_panic(expected = "Error(Contract, #36)")] +fn test_register_asset_invalid_name_too_short() { + let env = create_env(); + let (admin, user1, _, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + let asset_id = generate_asset_id(&env, 1); + let mut asset = create_test_asset(&env, &user1, asset_id); + asset.name = String::from_str(&env, "AB"); // Too short (< 3 chars) + + env.mock_all_auths(); + + // Should panic with InvalidAssetName error + client.register_asset(&asset, &admin); +} + +#[test] +#[should_panic(expected = "Error(Contract, #37)")] +fn test_register_asset_invalid_purchase_value() { + let env = create_env(); + let (admin, user1, _, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + let asset_id = generate_asset_id(&env, 1); + let mut asset = create_test_asset(&env, &user1, asset_id); + asset.purchase_value = -100; // Negative value + + env.mock_all_auths(); + + // Should panic with InvalidPurchaseValue error + client.register_asset(&asset, &admin); +} + +#[test] +#[should_panic(expected = "Error(Contract, #39)")] +fn test_register_asset_zero_owner() { + let env = create_env(); + let admin = Address::generate(&env); + let client = initialize_contract(&env, &admin); + + let zero_address = Address::from_string(&String::from_str( + &env, + "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF", + )); + + let asset_id = generate_asset_id(&env, 1); + let asset = create_test_asset(&env, &zero_address, asset_id); + + env.mock_all_auths(); + + // Should panic with InvalidOwnerAddress error + client.register_asset(&asset, &admin); +} + +#[test] +fn test_update_asset_metadata_success() { + let env = create_env(); + let (admin, user1, _, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + let asset_id = generate_asset_id(&env, 1); + let asset = create_test_asset(&env, &user1, asset_id.clone()); + + env.mock_all_auths(); + client.register_asset(&asset, &admin); + + // Update metadata + let new_description = Some(String::from_str(&env, "Updated description")); + let new_uri = Some(String::from_str(&env, "ipfs://QmUpdated123")); + + client.update_asset_metadata(&asset_id, &new_description, &new_uri, &None, &user1); + + // Verify metadata was updated (just check it doesn't error) + let updated_asset = client.get_asset(&asset_id); + assert!(!updated_asset.description.is_empty()); + assert!(!updated_asset.metadata_uri.is_empty()); +} + +#[test] +#[should_panic(expected = "Error(Contract, #4)")] +fn test_update_asset_metadata_not_found() { + let env = create_env(); + let (admin, user1, _, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + let asset_id = generate_asset_id(&env, 999); + let new_description = Some(String::from_str(&env, "Updated")); + + env.mock_all_auths(); + + // Should panic with AssetNotFound error + client.update_asset_metadata(&asset_id, &new_description, &None, &None, &user1); +} + +#[test] +#[should_panic(expected = "Error(Contract, #8)")] +fn test_update_asset_metadata_unauthorized() { + let env = create_env(); + let (admin, user1, user2, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + let asset_id = generate_asset_id(&env, 1); + let asset = create_test_asset(&env, &user1, asset_id.clone()); + + env.mock_all_auths(); + client.register_asset(&asset, &admin); + + let new_description = Some(String::from_str(&env, "Hacked")); + + // user2 is not owner or admin - should panic with Unauthorized + client.update_asset_metadata(&asset_id, &new_description, &None, &None, &user2); +} + +#[test] +fn test_transfer_asset_ownership_success() { + let env = create_env(); + let (admin, user1, user2, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + let asset_id = generate_asset_id(&env, 1); + let asset = create_test_asset(&env, &user1, asset_id.clone()); + + env.mock_all_auths(); + client.register_asset(&asset, &admin); + + // Transfer ownership + client.transfer_asset_ownership(&asset_id, &user2, &user1); + + // Verify ownership was transferred + let transferred_asset = client.get_asset(&asset_id); + assert_eq!(transferred_asset.owner, user2); + assert_eq!(transferred_asset.status, AssetStatus::Transferred); + + // Verify asset is in new owner's registry + let user2_assets = client.get_assets_by_owner(&user2); + assert_eq!(user2_assets.len(), 1); + + // Verify asset is removed from old owner's registry + let user1_assets = client.get_assets_by_owner(&user1); + assert_eq!(user1_assets.len(), 0); +} + +#[test] +#[should_panic(expected = "Error(Contract, #8)")] +fn test_transfer_asset_ownership_unauthorized() { + let env = create_env(); + let (admin, user1, user2, user3) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + let asset_id = generate_asset_id(&env, 1); + let asset = create_test_asset(&env, &user1, asset_id.clone()); + + env.mock_all_auths(); + client.register_asset(&asset, &admin); + + // user3 is not owner - should panic with Unauthorized + client.transfer_asset_ownership(&asset_id, &user2, &user3); +} + +#[test] +fn test_retire_asset_success() { + let env = create_env(); + let (admin, user1, _, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + let asset_id = generate_asset_id(&env, 1); + let asset = create_test_asset(&env, &user1, asset_id.clone()); + + env.mock_all_auths(); + client.register_asset(&asset, &admin); + + // Retire asset + client.retire_asset(&asset_id, &user1); + + // Verify asset was retired + let retired_asset = client.get_asset(&asset_id); + assert_eq!(retired_asset.status, AssetStatus::Retired); +} + +#[test] +#[should_panic(expected = "Error(Contract, #8)")] +fn test_retire_asset_unauthorized() { + let env = create_env(); + let (admin, user1, user2, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + let asset_id = generate_asset_id(&env, 1); + let asset = create_test_asset(&env, &user1, asset_id.clone()); + + env.mock_all_auths(); + client.register_asset(&asset, &admin); + + // user2 is not owner or admin - should panic with Unauthorized + client.retire_asset(&asset_id, &user2); +} + +#[test] +fn test_check_asset_exists() { + let env = create_env(); + let (admin, user1, _, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + let asset_id = generate_asset_id(&env, 1); + let asset = create_test_asset(&env, &user1, asset_id.clone()); + + env.mock_all_auths(); + + // Asset doesn't exist yet + assert!(!client.check_asset_exists(&asset_id)); + + // Register asset + client.register_asset(&asset, &admin); + + // Asset now exists + assert!(client.check_asset_exists(&asset_id)); +} + +#[test] +fn test_get_asset_info() { + let env = create_env(); + let (admin, user1, _, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + let asset_id = generate_asset_id(&env, 1); + let asset = create_test_asset(&env, &user1, asset_id.clone()); + + env.mock_all_auths(); + client.register_asset(&asset, &admin); + + let info = client.get_asset_info(&asset_id); + assert_eq!(info.id, asset_id); + assert_eq!(info.owner, user1); + assert_eq!(info.status, AssetStatus::Active); +} + +#[test] +fn test_batch_get_asset_info() { + let env = create_env(); + let (admin, user1, _, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + let asset_id1 = generate_asset_id(&env, 1); + let asset_id2 = generate_asset_id(&env, 2); + let asset1 = create_test_asset(&env, &user1, asset_id1.clone()); + let asset2 = create_test_asset(&env, &user1, asset_id2.clone()); + + env.mock_all_auths(); + client.register_asset(&asset1, &admin); + client.register_asset(&asset2, &admin); + + let mut ids = Vec::new(&env); + ids.push_back(asset_id1.clone()); + ids.push_back(asset_id2.clone()); + + let infos = client.batch_get_asset_info(&ids); + assert_eq!(infos.len(), 2); +} diff --git a/contracts/assetsup/src/tests/detokenization.rs b/contracts/assetsup/src/tests/detokenization.rs new file mode 100644 index 0000000..0657603 --- /dev/null +++ b/contracts/assetsup/src/tests/detokenization.rs @@ -0,0 +1,235 @@ +use crate::tests::helpers::*; +use crate::types::{AssetType, DetokenizationProposal}; +use soroban_sdk::String; + +#[test] +fn test_propose_detokenization_success() { + let env = create_env(); + let (admin, user1, _, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + client.tokenize_asset( + &1u64, + &String::from_str(&env, "TST"), + &1000000i128, + &6u32, + &100i128, + &user1, + &String::from_str(&env, "Test Token"), + &String::from_str(&env, "A test tokenized asset"), + &AssetType::Physical, + ); + + // Propose detokenization + let proposal_id = client.propose_detokenization(&1u64, &user1); + + assert_eq!(proposal_id, 1); + + // Verify proposal is active + assert!(client.is_detokenization_active(&1u64)); +} + +#[test] +#[should_panic(expected = "Error(Contract, #29)")] +fn test_propose_detokenization_already_proposed() { + let env = create_env(); + let (admin, user1, _, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + client.tokenize_asset( + &1u64, + &String::from_str(&env, "TST"), + &1000000i128, + &6u32, + &100i128, + &user1, + &String::from_str(&env, "Test Token"), + &String::from_str(&env, "A test tokenized asset"), + &AssetType::Physical, + ); + + client.propose_detokenization(&1u64, &user1); + + // Try to propose again - should panic with DetokenizationAlreadyProposed + client.propose_detokenization(&1u64, &user1); +} + +#[test] +#[should_panic(expected = "Error(Contract, #11)")] +fn test_propose_detokenization_not_tokenized() { + let env = create_env(); + let (admin, user1, _, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + // Should panic with AssetNotTokenized error + client.propose_detokenization(&999u64, &user1); +} + +#[test] +fn test_execute_detokenization_success() { + let env = create_env(); + let (admin, user1, user2, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + client.tokenize_asset( + &1u64, + &String::from_str(&env, "TST"), + &1000000i128, + &6u32, + &100i128, + &user1, + &String::from_str(&env, "Test Token"), + &String::from_str(&env, "A test tokenized asset"), + &AssetType::Physical, + ); + + // Transfer 60% to user2 + client.transfer_tokens(&1u64, &user1, &user2, &600000i128); + + // Propose detokenization + let proposal_id = client.propose_detokenization(&1u64, &user1); + + // Vote with majority + client.cast_vote(&1u64, &proposal_id, &user2); + + // Execute detokenization + client.execute_detokenization(&1u64, &proposal_id); + + // Verify asset is no longer tokenized + assert!(!client.is_detokenization_active(&1u64)); +} + +#[test] +#[should_panic(expected = "Error(Contract, #28)")] +fn test_execute_detokenization_not_approved() { + let env = create_env(); + let (admin, user1, user2, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + client.tokenize_asset( + &1u64, + &String::from_str(&env, "TST"), + &1000000i128, + &6u32, + &100i128, + &user1, + &String::from_str(&env, "Test Token"), + &String::from_str(&env, "A test tokenized asset"), + &AssetType::Physical, + ); + + // Transfer 30% to user2 (not enough for majority) + client.transfer_tokens(&1u64, &user1, &user2, &300000i128); + + // Propose detokenization + let proposal_id = client.propose_detokenization(&1u64, &user1); + + // Vote with minority + client.cast_vote(&1u64, &proposal_id, &user2); + + // Should panic with DetokenizationNotApproved error + client.execute_detokenization(&1u64, &proposal_id); +} + +#[test] +#[should_panic(expected = "Error(Contract, #24)")] +fn test_execute_detokenization_no_proposal() { + let env = create_env(); + let (admin, user1, _, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + client.tokenize_asset( + &1u64, + &String::from_str(&env, "TST"), + &1000000i128, + &6u32, + &100i128, + &user1, + &String::from_str(&env, "Test Token"), + &String::from_str(&env, "A test tokenized asset"), + &AssetType::Physical, + ); + + // Should panic with InvalidProposal error + client.execute_detokenization(&1u64, &1u64); +} + +#[test] +fn test_get_detokenization_proposal() { + let env = create_env(); + let (admin, user1, _, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + client.tokenize_asset( + &1u64, + &String::from_str(&env, "TST"), + &1000000i128, + &6u32, + &100i128, + &user1, + &String::from_str(&env, "Test Token"), + &String::from_str(&env, "A test tokenized asset"), + &AssetType::Physical, + ); + + let proposal_id = client.propose_detokenization(&1u64, &user1); + + let proposal = client.get_detokenization_proposal(&1u64); + + match proposal { + DetokenizationProposal::Active(active) => { + assert_eq!(active.proposal_id, proposal_id); + assert_eq!(active.proposer, user1); + } + _ => panic!("Expected Active proposal"), + } +} + +#[test] +fn test_detokenization_clears_all_data() { + let env = create_env(); + let (admin, user1, user2, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + client.tokenize_asset( + &1u64, + &String::from_str(&env, "TST"), + &1000000i128, + &6u32, + &100i128, + &user1, + &String::from_str(&env, "Test Token"), + &String::from_str(&env, "A test tokenized asset"), + &AssetType::Physical, + ); + + // Set up some data + client.transfer_tokens(&1u64, &user1, &user2, &600000i128); + client.add_to_whitelist(&1u64, &user2); + client.enable_revenue_sharing(&1u64); + + // Propose and execute detokenization + let proposal_id = client.propose_detokenization(&1u64, &user1); + client.cast_vote(&1u64, &proposal_id, &user2); + client.execute_detokenization(&1u64, &proposal_id); + + // Verify whitelist is cleared + let whitelist = client.get_whitelist(&1u64); + assert_eq!(whitelist.len(), 0); +} diff --git a/contracts/assetsup/src/tests/detokenization_new.rs b/contracts/assetsup/src/tests/detokenization_new.rs index ba7a0b9..cea89e1 100644 --- a/contracts/assetsup/src/tests/detokenization_new.rs +++ b/contracts/assetsup/src/tests/detokenization_new.rs @@ -232,7 +232,12 @@ fn test_token_elimination_on_execution() { let holders_result = tokenization::get_token_holders(&env, asset_id); let holders_cleared = holders_result.is_err(); - (before_exists, after_exists, balance_cleared, holders_cleared) + ( + before_exists, + after_exists, + balance_cleared, + holders_cleared, + ) }); // Assert asset existed before diff --git a/contracts/assetsup/src/tests/dividends.rs b/contracts/assetsup/src/tests/dividends.rs new file mode 100644 index 0000000..dae3f9e --- /dev/null +++ b/contracts/assetsup/src/tests/dividends.rs @@ -0,0 +1,245 @@ +use crate::tests::helpers::*; +use crate::types::AssetType; +use soroban_sdk::String; + +#[test] +fn test_enable_revenue_sharing() { + let env = create_env(); + let (admin, user1, _, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + client.tokenize_asset( + &1u64, + &String::from_str(&env, "TST"), + &1000000i128, + &6u32, + &100i128, + &user1, + &String::from_str(&env, "Test Token"), + &String::from_str(&env, "A test tokenized asset"), + &AssetType::Physical, + ); + + // Initially disabled + let asset = client.get_tokenized_asset(&1u64); + assert!(!asset.revenue_sharing_enabled); + + // Enable revenue sharing + client.enable_revenue_sharing(&1u64); + + let asset = client.get_tokenized_asset(&1u64); + assert!(asset.revenue_sharing_enabled); +} + +#[test] +fn test_disable_revenue_sharing() { + let env = create_env(); + let (admin, user1, _, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + client.tokenize_asset( + &1u64, + &String::from_str(&env, "TST"), + &1000000i128, + &6u32, + &100i128, + &user1, + &String::from_str(&env, "Test Token"), + &String::from_str(&env, "A test tokenized asset"), + &AssetType::Physical, + ); + + client.enable_revenue_sharing(&1u64); + client.disable_revenue_sharing(&1u64); + + let asset = client.get_tokenized_asset(&1u64); + assert!(!asset.revenue_sharing_enabled); +} + +#[test] +fn test_distribute_dividends_success() { + let env = create_env(); + let (admin, user1, user2, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + client.tokenize_asset( + &1u64, + &String::from_str(&env, "TST"), + &1000000i128, + &6u32, + &100i128, + &user1, + &String::from_str(&env, "Test Token"), + &String::from_str(&env, "A test tokenized asset"), + &AssetType::Physical, + ); + + // Enable revenue sharing + client.enable_revenue_sharing(&1u64); + + // Transfer 30% to user2 + client.transfer_tokens(&1u64, &user1, &user2, &300000i128); + + // Distribute 10000 in dividends + client.distribute_dividends(&1u64, &10000i128); + + // Check unclaimed dividends + let unclaimed1 = client.get_unclaimed_dividends(&1u64, &user1); + let unclaimed2 = client.get_unclaimed_dividends(&1u64, &user2); + + assert_eq!(unclaimed1, 7000); // 70% of 10000 + assert_eq!(unclaimed2, 3000); // 30% of 10000 +} + +#[test] +#[should_panic(expected = "Error(Contract, #27)")] +fn test_distribute_dividends_invalid_amount() { + let env = create_env(); + let (admin, user1, _, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + client.tokenize_asset( + &1u64, + &String::from_str(&env, "TST"), + &1000000i128, + &6u32, + &100i128, + &user1, + &String::from_str(&env, "Test Token"), + &String::from_str(&env, "A test tokenized asset"), + &AssetType::Physical, + ); + + client.enable_revenue_sharing(&1u64); + + // Should panic with InvalidDividendAmount error + client.distribute_dividends(&1u64, &0i128); +} + +#[test] +#[should_panic(expected = "Error(Contract, #27)")] +fn test_distribute_dividends_not_enabled() { + let env = create_env(); + let (admin, user1, _, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + client.tokenize_asset( + &1u64, + &String::from_str(&env, "TST"), + &1000000i128, + &6u32, + &100i128, + &user1, + &String::from_str(&env, "Test Token"), + &String::from_str(&env, "A test tokenized asset"), + &AssetType::Physical, + ); + + // Revenue sharing not enabled - should panic with InvalidDividendAmount + client.distribute_dividends(&1u64, &10000i128); +} + +#[test] +fn test_claim_dividends_success() { + let env = create_env(); + let (admin, user1, user2, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + client.tokenize_asset( + &1u64, + &String::from_str(&env, "TST"), + &1000000i128, + &6u32, + &100i128, + &user1, + &String::from_str(&env, "Test Token"), + &String::from_str(&env, "A test tokenized asset"), + &AssetType::Physical, + ); + + client.enable_revenue_sharing(&1u64); + client.transfer_tokens(&1u64, &user1, &user2, &300000i128); + client.distribute_dividends(&1u64, &10000i128); + + // Claim dividends + let claimed = client.claim_dividends(&1u64, &user2); + assert_eq!(claimed, 3000); + + // After claiming, unclaimed should be 0 + let unclaimed = client.get_unclaimed_dividends(&1u64, &user2); + assert_eq!(unclaimed, 0); +} + +#[test] +#[should_panic(expected = "Error(Contract, #26)")] +fn test_claim_dividends_none_to_claim() { + let env = create_env(); + let (admin, user1, _, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + client.tokenize_asset( + &1u64, + &String::from_str(&env, "TST"), + &1000000i128, + &6u32, + &100i128, + &user1, + &String::from_str(&env, "Test Token"), + &String::from_str(&env, "A test tokenized asset"), + &AssetType::Physical, + ); + + // Should panic with NoDividendsToClaim error + client.claim_dividends(&1u64, &user1); +} + +#[test] +fn test_multiple_dividend_distributions() { + let env = create_env(); + let (admin, user1, user2, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + client.tokenize_asset( + &1u64, + &String::from_str(&env, "TST"), + &1000000i128, + &6u32, + &100i128, + &user1, + &String::from_str(&env, "Test Token"), + &String::from_str(&env, "A test tokenized asset"), + &AssetType::Physical, + ); + + client.enable_revenue_sharing(&1u64); + client.transfer_tokens(&1u64, &user1, &user2, &500000i128); + + // First distribution + client.distribute_dividends(&1u64, &10000i128); + + // Second distribution + client.distribute_dividends(&1u64, &5000i128); + + // Total unclaimed should be sum of both distributions + let unclaimed1 = client.get_unclaimed_dividends(&1u64, &user1); + let unclaimed2 = client.get_unclaimed_dividends(&1u64, &user2); + + assert_eq!(unclaimed1, 7500); // 50% of 15000 + assert_eq!(unclaimed2, 7500); // 50% of 15000 +} diff --git a/contracts/assetsup/src/tests/helpers.rs b/contracts/assetsup/src/tests/helpers.rs new file mode 100644 index 0000000..66ad205 --- /dev/null +++ b/contracts/assetsup/src/tests/helpers.rs @@ -0,0 +1,154 @@ +use crate::asset::Asset; +use crate::insurance::{ClaimStatus, InsuranceClaim, InsurancePolicy, PolicyStatus, PolicyType}; +use crate::types::{AssetStatus, AssetType, CustomAttribute, TokenMetadata}; +use crate::{AssetUpContract, AssetUpContractClient}; +use soroban_sdk::{testutils::Address as _, Address, BytesN, Env, String, Vec}; + +/// Create a fresh test environment +pub fn create_env() -> Env { + Env::default() +} + +/// Create mock addresses for testing +pub fn create_mock_addresses(env: &Env) -> (Address, Address, Address, Address) { + let admin = Address::generate(env); + let user1 = Address::generate(env); + let user2 = Address::generate(env); + let user3 = Address::generate(env); + (admin, user1, user2, user3) +} + +/// Initialize contract with admin +pub fn initialize_contract<'a>(env: &'a Env, admin: &Address) -> AssetUpContractClient<'a> { + let contract_id = env.register(AssetUpContract, ()); + let client = AssetUpContractClient::new(env, &contract_id); + + env.mock_all_auths(); + client.initialize(admin); + client +} + +/// Create a test asset +pub fn create_test_asset(env: &Env, owner: &Address, id: BytesN<32>) -> Asset { + let timestamp = env.ledger().timestamp(); + + Asset { + id, + name: String::from_str(env, "Test Asset"), + description: String::from_str(env, "A test asset for unit testing"), + category: String::from_str(env, "Electronics"), + owner: owner.clone(), + registration_timestamp: timestamp, + last_transfer_timestamp: timestamp, + status: AssetStatus::Active, + metadata_uri: String::from_str(env, "ipfs://QmTest123456789"), + purchase_value: 1000, + custom_attributes: Vec::new(env), + } +} + +/// Create a test asset with custom attributes +#[allow(dead_code)] +pub fn create_test_asset_with_attributes( + env: &Env, + owner: &Address, + id: BytesN<32>, + name: &str, + value: i128, +) -> Asset { + let timestamp = env.ledger().timestamp(); + let mut attributes = Vec::new(env); + attributes.push_back(CustomAttribute { + key: String::from_str(env, "serial_number"), + value: String::from_str(env, "SN123456"), + }); + + Asset { + id, + name: String::from_str(env, name), + description: String::from_str(env, "Test asset with attributes"), + category: String::from_str(env, "Equipment"), + owner: owner.clone(), + registration_timestamp: timestamp, + last_transfer_timestamp: timestamp, + status: AssetStatus::Active, + metadata_uri: String::from_str(env, "ipfs://QmTestWithAttrs"), + purchase_value: value, + custom_attributes: attributes, + } +} + +/// Generate a unique asset ID +pub fn generate_asset_id(env: &Env, seed: u32) -> BytesN<32> { + let mut bytes = [0u8; 32]; + bytes[0] = (seed >> 24) as u8; + bytes[1] = (seed >> 16) as u8; + bytes[2] = (seed >> 8) as u8; + bytes[3] = seed as u8; + BytesN::from_array(env, &bytes) +} + +/// Create token metadata for testing +#[allow(dead_code)] +pub fn create_test_token_metadata(env: &Env) -> TokenMetadata { + TokenMetadata { + name: String::from_str(env, "Test Token"), + description: String::from_str(env, "Test tokenized asset"), + asset_type: AssetType::Physical, + ipfs_uri: Some(String::from_str(env, "ipfs://QmTokenMetadata")), + legal_docs_hash: None, + valuation_report_hash: None, + accredited_investor_required: false, + geographic_restrictions: Vec::new(env), + } +} + +/// Create a test insurance policy +pub fn create_test_policy( + env: &Env, + policy_id: BytesN<32>, + holder: &Address, + insurer: &Address, + asset_id: BytesN<32>, +) -> InsurancePolicy { + let current_time = env.ledger().timestamp(); + + InsurancePolicy { + policy_id, + holder: holder.clone(), + insurer: insurer.clone(), + asset_id, + policy_type: PolicyType::Property, + coverage_amount: 10000, + deductible: 500, + premium: 100, + start_date: current_time, + end_date: current_time + 31536000, // 1 year + status: PolicyStatus::Active, + auto_renew: false, + last_payment: current_time, + } +} + +/// Create a test insurance claim +#[allow(dead_code)] +pub fn create_test_claim( + env: &Env, + claim_id: BytesN<32>, + policy_id: BytesN<32>, + asset_id: BytesN<32>, + claimant: &Address, +) -> InsuranceClaim { + let current_time = env.ledger().timestamp(); + + InsuranceClaim { + claim_id, + policy_id, + asset_id, + claimant: claimant.clone(), + amount: 5000, + status: ClaimStatus::Submitted, + filed_at: current_time, + approved_amount: 0, + } +} diff --git a/contracts/assetsup/src/tests/initialization.rs b/contracts/assetsup/src/tests/initialization.rs new file mode 100644 index 0000000..4430527 --- /dev/null +++ b/contracts/assetsup/src/tests/initialization.rs @@ -0,0 +1,80 @@ +use crate::tests::helpers::*; +use crate::{AssetUpContract, AssetUpContractClient}; +use soroban_sdk::{testutils::Address as _, Address}; + +#[test] +fn test_initialize_success() { + let env = create_env(); + let admin = Address::generate(&env); + + let contract_id = env.register(AssetUpContract, ()); + let client = AssetUpContractClient::new(&env, &contract_id); + + env.mock_all_auths(); + client.initialize(&admin); + + // Verify admin is set + let stored_admin = client.get_admin(); + assert_eq!(stored_admin, admin); + + // Verify contract is not paused + assert!(!client.is_paused()); + + // Verify total asset count is 0 + assert_eq!(client.get_total_asset_count(), 0); + + // Verify admin is authorized registrar + assert!(client.is_authorized_registrar(&admin)); +} + +#[test] +#[should_panic(expected = "Error(Contract, #1)")] +fn test_initialize_already_initialized() { + let env = create_env(); + let admin = Address::generate(&env); + + let contract_id = env.register(AssetUpContract, ()); + let client = AssetUpContractClient::new(&env, &contract_id); + + env.mock_all_auths(); + client.initialize(&admin); + + // Try to initialize again - should panic with AlreadyInitialized error + client.initialize(&admin); +} + +#[test] +fn test_get_contract_metadata() { + let env = create_env(); + let admin = Address::generate(&env); + let client = initialize_contract(&env, &admin); + + let metadata = client.get_contract_metadata(); + // Just verify metadata exists and has expected structure + assert!(!metadata.version.is_empty()); + assert!(!metadata.name.is_empty()); +} + +#[test] +#[should_panic(expected = "Error(Contract, #35)")] +fn test_get_contract_metadata_not_initialized() { + let env = create_env(); + + let contract_id = env.register(AssetUpContract, ()); + let client = AssetUpContractClient::new(&env, &contract_id); + + // Should panic with ContractNotInitialized error + client.get_contract_metadata(); +} + +#[test] +#[should_panic(expected = "Error(Contract, #2)")] +fn test_get_admin_not_found() { + let env = create_env(); + + let contract_id = env.register(AssetUpContract, ()); + let client = AssetUpContractClient::new(&env, &contract_id); + + // Should panic with AdminNotFound error + client.get_admin(); +} diff --git a/contracts/assetsup/src/tests/insurance.rs b/contracts/assetsup/src/tests/insurance.rs new file mode 100644 index 0000000..677982b --- /dev/null +++ b/contracts/assetsup/src/tests/insurance.rs @@ -0,0 +1,338 @@ +use crate::insurance::PolicyStatus; +use crate::tests::helpers::*; +use soroban_sdk::testutils::Ledger; + +#[test] +fn test_create_insurance_policy_success() { + let env = create_env(); + let (admin, user1, insurer, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + let policy_id = generate_asset_id(&env, 1); + let asset_id = generate_asset_id(&env, 100); + let policy = create_test_policy(&env, policy_id.clone(), &user1, &insurer, asset_id); + + env.mock_all_auths(); + client.create_insurance_policy(&policy); + + // Verify policy was created + let stored_policy = client.get_insurance_policy(&policy_id); + assert!(stored_policy.is_some()); + + let stored = stored_policy.unwrap(); + assert_eq!(stored.policy_id, policy_id); + assert_eq!(stored.holder, user1); + assert_eq!(stored.status, PolicyStatus::Active); +} + +#[test] +#[should_panic(expected = "Error(Contract, #3)")] +fn test_create_insurance_policy_already_exists() { + let env = create_env(); + let (admin, user1, insurer, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + let policy_id = generate_asset_id(&env, 1); + let asset_id = generate_asset_id(&env, 100); + let policy = create_test_policy(&env, policy_id.clone(), &user1, &insurer, asset_id); + + env.mock_all_auths(); + client.create_insurance_policy(&policy); + + // Try to create again - should panic with AssetAlreadyExists + client.create_insurance_policy(&policy); +} + +#[test] +#[should_panic(expected = "Error(Contract, #9)")] +fn test_create_insurance_policy_invalid_coverage() { + let env = create_env(); + let (admin, user1, insurer, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + let policy_id = generate_asset_id(&env, 1); + let asset_id = generate_asset_id(&env, 100); + let mut policy = create_test_policy(&env, policy_id, &user1, &insurer, asset_id); + + // Invalid: deductible >= coverage_amount + policy.deductible = 10000; + policy.coverage_amount = 10000; + + env.mock_all_auths(); + + // Should panic with InvalidPayment error + client.create_insurance_policy(&policy); +} + +#[test] +#[should_panic(expected = "Error(Contract, #9)")] +fn test_create_insurance_policy_invalid_dates() { + let env = create_env(); + let (admin, user1, insurer, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + let policy_id = generate_asset_id(&env, 1); + let asset_id = generate_asset_id(&env, 100); + let mut policy = create_test_policy(&env, policy_id, &user1, &insurer, asset_id); + + // Invalid: start_date >= end_date + policy.start_date = 1000; + policy.end_date = 1000; + + env.mock_all_auths(); + + // Should panic with InvalidPayment error + client.create_insurance_policy(&policy); +} + +#[test] +fn test_cancel_insurance_policy_by_holder() { + let env = create_env(); + let (admin, user1, insurer, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + let policy_id = generate_asset_id(&env, 1); + let asset_id = generate_asset_id(&env, 100); + let policy = create_test_policy(&env, policy_id.clone(), &user1, &insurer, asset_id); + + env.mock_all_auths(); + client.create_insurance_policy(&policy); + + // Cancel by holder + client.cancel_insurance_policy(&policy_id, &user1); + + // Verify policy was cancelled + let stored_policy = client.get_insurance_policy(&policy_id).unwrap(); + assert_eq!(stored_policy.status, PolicyStatus::Cancelled); +} + +#[test] +fn test_cancel_insurance_policy_by_insurer() { + let env = create_env(); + let (admin, user1, insurer, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + let policy_id = generate_asset_id(&env, 1); + let asset_id = generate_asset_id(&env, 100); + let policy = create_test_policy(&env, policy_id.clone(), &user1, &insurer, asset_id); + + env.mock_all_auths(); + client.create_insurance_policy(&policy); + + // Cancel by insurer + client.cancel_insurance_policy(&policy_id, &insurer); + + // Verify policy was cancelled + let stored_policy = client.get_insurance_policy(&policy_id).unwrap(); + assert_eq!(stored_policy.status, PolicyStatus::Cancelled); +} + +#[test] +#[should_panic(expected = "Error(Contract, #8)")] +fn test_cancel_insurance_policy_unauthorized() { + let env = create_env(); + let (admin, user1, insurer, user3) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + let policy_id = generate_asset_id(&env, 1); + let asset_id = generate_asset_id(&env, 100); + let policy = create_test_policy(&env, policy_id.clone(), &user1, &insurer, asset_id); + + env.mock_all_auths(); + client.create_insurance_policy(&policy); + + // user3 is neither holder nor insurer - should panic with Unauthorized + client.cancel_insurance_policy(&policy_id, &user3); +} + +#[test] +fn test_suspend_insurance_policy() { + let env = create_env(); + let (admin, user1, insurer, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + let policy_id = generate_asset_id(&env, 1); + let asset_id = generate_asset_id(&env, 100); + let policy = create_test_policy(&env, policy_id.clone(), &user1, &insurer, asset_id); + + env.mock_all_auths(); + client.create_insurance_policy(&policy); + + // Suspend policy + client.suspend_insurance_policy(&policy_id, &insurer); + + // Verify policy was suspended + let stored_policy = client.get_insurance_policy(&policy_id).unwrap(); + assert_eq!(stored_policy.status, PolicyStatus::Suspended); +} + +#[test] +#[should_panic(expected = "Error(Contract, #8)")] +fn test_suspend_insurance_policy_unauthorized() { + let env = create_env(); + let (admin, user1, insurer, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + let policy_id = generate_asset_id(&env, 1); + let asset_id = generate_asset_id(&env, 100); + let policy = create_test_policy(&env, policy_id.clone(), &user1, &insurer, asset_id); + + env.mock_all_auths(); + client.create_insurance_policy(&policy); + + // Holder cannot suspend - should panic with Unauthorized + client.suspend_insurance_policy(&policy_id, &user1); +} + +#[test] +fn test_expire_insurance_policy() { + let env = create_env(); + let (admin, user1, insurer, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + let policy_id = generate_asset_id(&env, 1); + let asset_id = generate_asset_id(&env, 100); + let mut policy = create_test_policy(&env, policy_id.clone(), &user1, &insurer, asset_id); + + // Set current time to 5000 + env.ledger().with_mut(|li| li.timestamp = 5000); + + // Set dates: start now, end in 1000 seconds + policy.start_date = 5000; + policy.end_date = 6000; + + env.mock_all_auths(); + client.create_insurance_policy(&policy); + + // Advance time past end_date + env.ledger().with_mut(|li| li.timestamp = 7000); + + // Expire policy (permissionless) + client.expire_insurance_policy(&policy_id); + + // Verify policy was expired + let stored_policy = client.get_insurance_policy(&policy_id).unwrap(); + assert_eq!(stored_policy.status, PolicyStatus::Expired); +} + +#[test] +#[should_panic(expected = "Error(Contract, #8)")] +fn test_expire_insurance_policy_not_yet_expired() { + let env = create_env(); + let (admin, user1, insurer, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + let policy_id = generate_asset_id(&env, 1); + let asset_id = generate_asset_id(&env, 100); + let policy = create_test_policy(&env, policy_id.clone(), &user1, &insurer, asset_id); + + env.mock_all_auths(); + client.create_insurance_policy(&policy); + + // Should panic with Unauthorized error - end date hasn't passed + client.expire_insurance_policy(&policy_id); +} + +#[test] +fn test_renew_insurance_policy() { + let env = create_env(); + let (admin, user1, insurer, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + let policy_id = generate_asset_id(&env, 1); + let asset_id = generate_asset_id(&env, 100); + let policy = create_test_policy(&env, policy_id.clone(), &user1, &insurer, asset_id); + + env.mock_all_auths(); + client.create_insurance_policy(&policy); + + // Renew policy + let new_end_date = env.ledger().timestamp() + 63072000; // 2 years + let new_premium = 150i128; + + client.renew_insurance_policy(&policy_id, &new_end_date, &new_premium, &insurer); + + // Verify policy was renewed + let stored_policy = client.get_insurance_policy(&policy_id).unwrap(); + assert_eq!(stored_policy.end_date, new_end_date); + assert_eq!(stored_policy.premium, new_premium); + assert_eq!(stored_policy.status, PolicyStatus::Active); +} + +#[test] +#[should_panic(expected = "Error(Contract, #8)")] +fn test_renew_insurance_policy_unauthorized() { + let env = create_env(); + let (admin, user1, insurer, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + let policy_id = generate_asset_id(&env, 1); + let asset_id = generate_asset_id(&env, 100); + let policy = create_test_policy(&env, policy_id.clone(), &user1, &insurer, asset_id); + + env.mock_all_auths(); + client.create_insurance_policy(&policy); + + let new_end_date = env.ledger().timestamp() + 63072000; + let new_premium = 150i128; + + // Holder cannot renew - should panic with Unauthorized + client.renew_insurance_policy(&policy_id, &new_end_date, &new_premium, &user1); +} + +#[test] +fn test_get_asset_insurance_policies() { + let env = create_env(); + let (admin, user1, insurer, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + let asset_id = generate_asset_id(&env, 100); + let policy_id1 = generate_asset_id(&env, 1); + let policy_id2 = generate_asset_id(&env, 2); + + let policy1 = create_test_policy(&env, policy_id1.clone(), &user1, &insurer, asset_id.clone()); + let policy2 = create_test_policy(&env, policy_id2.clone(), &user1, &insurer, asset_id.clone()); + + env.mock_all_auths(); + client.create_insurance_policy(&policy1); + client.create_insurance_policy(&policy2); + + // Get all policies for asset + let policies = client.get_asset_insurance_policies(&asset_id); + assert_eq!(policies.len(), 2); +} + +#[test] +fn test_policy_lifecycle_full() { + let env = create_env(); + let (admin, user1, insurer, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + let policy_id = generate_asset_id(&env, 1); + let asset_id = generate_asset_id(&env, 100); + let policy = create_test_policy(&env, policy_id.clone(), &user1, &insurer, asset_id); + + env.mock_all_auths(); + + // Create + client.create_insurance_policy(&policy); + let stored = client.get_insurance_policy(&policy_id).unwrap(); + assert_eq!(stored.status, PolicyStatus::Active); + + // Renew while active + let new_end_date = env.ledger().timestamp() + 63072000; + client.renew_insurance_policy(&policy_id, &new_end_date, &150i128, &insurer); + let stored = client.get_insurance_policy(&policy_id).unwrap(); + assert_eq!(stored.status, PolicyStatus::Active); + assert_eq!(stored.premium, 150); + + // Suspend + client.suspend_insurance_policy(&policy_id, &insurer); + let stored = client.get_insurance_policy(&policy_id).unwrap(); + assert_eq!(stored.status, PolicyStatus::Suspended); + + // Cancel from suspended state + client.cancel_insurance_policy(&policy_id, &user1); + let stored = client.get_insurance_policy(&policy_id).unwrap(); + assert_eq!(stored.status, PolicyStatus::Cancelled); +} diff --git a/contracts/assetsup/src/tests/insurance_new.rs b/contracts/assetsup/src/tests/insurance_new.rs index 6f98605..d2f3b9a 100644 --- a/contracts/assetsup/src/tests/insurance_new.rs +++ b/contracts/assetsup/src/tests/insurance_new.rs @@ -328,7 +328,8 @@ fn test_renew_expired_policy() { li.timestamp = 1000; }); - let mut policy = create_test_policy(&env, policy_id.clone(), holder, insurer.clone(), asset_id); + let mut policy = + create_test_policy(&env, policy_id.clone(), holder, insurer.clone(), asset_id); // Set policy to expire at timestamp 2000 policy.start_date = 1000; policy.end_date = 2000; @@ -343,14 +344,9 @@ fn test_renew_expired_policy() { insurance::expire_policy(env.clone(), policy_id.clone()).unwrap(); // Now renew it to timestamp 3500 - let renew_result = insurance::renew_policy( - env.clone(), - policy_id.clone(), - 3500, - 1500, - insurer.clone(), - ) - .is_ok(); + let renew_result = + insurance::renew_policy(env.clone(), policy_id.clone(), 3500, 1500, insurer.clone()) + .is_ok(); let final_policy = insurance::get_policy(env.clone(), policy_id.clone()).unwrap(); @@ -409,8 +405,13 @@ fn test_status_transition_validation() { let (suspend_ok, cancel_after_suspend_ok, suspend_cancelled_err) = env.as_contract(&contract_id, || { - let policy = - create_test_policy(&env, policy_id.clone(), holder.clone(), insurer.clone(), asset_id); + let policy = create_test_policy( + &env, + policy_id.clone(), + holder.clone(), + insurer.clone(), + asset_id, + ); insurance::create_policy(env.clone(), policy).unwrap(); // Active -> Suspended (should work) diff --git a/contracts/assetsup/src/tests/integration_full.rs b/contracts/assetsup/src/tests/integration_full.rs new file mode 100644 index 0000000..a3527da --- /dev/null +++ b/contracts/assetsup/src/tests/integration_full.rs @@ -0,0 +1,255 @@ +use crate::tests::helpers::*; +use crate::types::AssetType; +use soroban_sdk::String; + +#[test] +fn test_full_asset_tokenization_workflow() { + let env = create_env(); + let (admin, owner, investor1, investor2) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + // Step 1: Register asset + let asset_id_bytes = generate_asset_id(&env, 1); + let asset = create_test_asset(&env, &owner, asset_id_bytes.clone()); + client.register_asset(&asset, &admin); + + // Step 2: Tokenize asset + let asset_id = 1u64; + client.tokenize_asset( + &asset_id, + &String::from_str(&env, "PROP"), + &1000000i128, + &6u32, + &1000i128, + &owner, + &String::from_str(&env, "Property Token"), + &String::from_str(&env, "Tokenized real estate"), + &AssetType::Physical, + ); + + // Step 3: Distribute tokens to investors + client.transfer_tokens(&asset_id, &owner, &investor1, &400000i128); + client.transfer_tokens(&asset_id, &owner, &investor2, &300000i128); + + // Verify ownership distribution + assert_eq!(client.get_token_balance(&asset_id, &owner), 300000); + assert_eq!(client.get_token_balance(&asset_id, &investor1), 400000); + assert_eq!(client.get_token_balance(&asset_id, &investor2), 300000); + + // Step 4: Enable revenue sharing and distribute dividends + client.enable_revenue_sharing(&asset_id); + client.distribute_dividends(&asset_id, &10000i128); + + // Verify dividend distribution + assert_eq!(client.get_unclaimed_dividends(&asset_id, &owner), 3000); + assert_eq!(client.get_unclaimed_dividends(&asset_id, &investor1), 4000); + assert_eq!(client.get_unclaimed_dividends(&asset_id, &investor2), 3000); + + // Step 5: Claim dividends + let claimed = client.claim_dividends(&asset_id, &investor1); + assert_eq!(claimed, 4000); + assert_eq!(client.get_unclaimed_dividends(&asset_id, &investor1), 0); +} + +#[test] +fn test_governance_and_detokenization_workflow() { + let env = create_env(); + let (admin, owner, investor1, investor2) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + // Setup: Tokenize asset + let asset_id = 1u64; + client.tokenize_asset( + &asset_id, + &String::from_str(&env, "GOV"), + &1000000i128, + &6u32, + &1000i128, + &owner, + &String::from_str(&env, "Governance Token"), + &String::from_str(&env, "Token with voting"), + &AssetType::Physical, + ); + + // Distribute tokens + client.transfer_tokens(&asset_id, &owner, &investor1, &600000i128); + client.transfer_tokens(&asset_id, &owner, &investor2, &200000i128); + + // Propose detokenization + let proposal_id = client.propose_detokenization(&asset_id, &owner); + + // Vote on proposal + client.cast_vote(&asset_id, &proposal_id, &investor1); + + // Check if proposal passed + assert!(client.proposal_passed(&asset_id, &proposal_id)); + + // Execute detokenization + client.execute_detokenization(&asset_id, &proposal_id); + + // Verify asset is detokenized + assert!(!client.is_detokenization_active(&asset_id)); +} + +#[test] +fn test_transfer_restrictions_workflow() { + let env = create_env(); + let (admin, owner, investor1, investor2) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + // Setup: Tokenize asset + let asset_id = 1u64; + client.tokenize_asset( + &asset_id, + &String::from_str(&env, "REST"), + &1000000i128, + &6u32, + &1000i128, + &owner, + &String::from_str(&env, "Restricted Token"), + &String::from_str(&env, "Token with restrictions"), + &AssetType::Physical, + ); + + // Set transfer restrictions + client.set_transfer_restriction(&asset_id, &true); + + // Add investor1 to whitelist + client.add_to_whitelist(&asset_id, &investor1); + + // Transfer to whitelisted address should succeed + client.transfer_tokens(&asset_id, &owner, &investor1, &100000i128); + assert_eq!(client.get_token_balance(&asset_id, &investor1), 100000); + + // Verify whitelist + assert!(client.is_whitelisted(&asset_id, &investor1)); + assert!(!client.is_whitelisted(&asset_id, &investor2)); +} + +#[test] +fn test_multi_asset_management() { + let env = create_env(); + let (admin, owner, _, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + // Register multiple assets + for i in 1..=5 { + let asset_id = generate_asset_id(&env, i); + let asset = create_test_asset(&env, &owner, asset_id); + client.register_asset(&asset, &admin); + } + + // Verify total count + assert_eq!(client.get_total_asset_count(), 5); + + // Verify owner has all assets + let owner_assets = client.get_assets_by_owner(&owner); + assert_eq!(owner_assets.len(), 5); +} + +#[test] +fn test_token_locking_workflow() { + let env = create_env(); + let (admin, owner, investor, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + // Setup: Tokenize and transfer + let asset_id = 1u64; + client.tokenize_asset( + &asset_id, + &String::from_str(&env, "LOCK"), + &1000000i128, + &6u32, + &1000i128, + &owner, + &String::from_str(&env, "Lockable Token"), + &String::from_str(&env, "Token with locking"), + &AssetType::Physical, + ); + + client.transfer_tokens(&asset_id, &owner, &investor, &500000i128); + + // Lock investor's tokens + let lock_until = env.ledger().timestamp() + 1000; + client.lock_tokens(&asset_id, &investor, &lock_until, &owner); + + // Verify tokens are locked + assert!(client.is_tokens_locked(&asset_id, &investor)); + + // Unlock tokens + client.unlock_tokens(&asset_id, &investor); + + // Verify tokens are unlocked + assert!(!client.is_tokens_locked(&asset_id, &investor)); + + // Transfer should now succeed + client.transfer_tokens(&asset_id, &investor, &owner, &100000i128); +} + +#[test] +fn test_insurance_and_asset_integration() { + let env = create_env(); + let (admin, owner, insurer, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + // Register asset + let asset_id = generate_asset_id(&env, 1); + let asset = create_test_asset(&env, &owner, asset_id.clone()); + client.register_asset(&asset, &admin); + + // Create insurance policy for asset + let policy_id = generate_asset_id(&env, 100); + let policy = create_test_policy(&env, policy_id.clone(), &owner, &insurer, asset_id.clone()); + client.create_insurance_policy(&policy); + + // Verify policy exists + let stored_policy = client.get_insurance_policy(&policy_id); + assert!(stored_policy.is_some()); + + // Verify asset has policy + let asset_policies = client.get_asset_insurance_policies(&asset_id); + assert_eq!(asset_policies.len(), 1); +} + +#[test] +fn test_admin_operations_workflow() { + let env = create_env(); + let (admin, new_admin, registrar, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + // Add authorized registrar + client.add_authorized_registrar(®istrar); + assert!(client.is_authorized_registrar(®istrar)); + + // Pause contract + client.pause_contract(); + assert!(client.is_paused()); + + // Unpause contract + client.unpause_contract(); + assert!(!client.is_paused()); + + // Update admin + client.update_admin(&new_admin); + assert_eq!(client.get_admin(), new_admin); + + // Verify old admin is no longer authorized registrar + assert!(!client.is_authorized_registrar(&admin)); + + // Verify new admin is authorized registrar + assert!(client.is_authorized_registrar(&new_admin)); +} diff --git a/contracts/assetsup/src/tests/mod.rs b/contracts/assetsup/src/tests/mod.rs index fd8a933..0cded02 100644 --- a/contracts/assetsup/src/tests/mod.rs +++ b/contracts/assetsup/src/tests/mod.rs @@ -1,3 +1,25 @@ +// Test helper functions and utilities +mod helpers; + +// Core contract tests +mod admin; +mod asset; +mod initialization; + +// Tokenization and ownership tests +mod detokenization; +mod dividends; +mod tokenization; +mod transfer_restrictions; +mod voting; + +// Insurance tests +mod insurance; + +// Integration tests +mod integration_full; + +// Legacy test modules (if still needed) mod detokenization_new; mod dividends_new; mod insurance_new; @@ -5,9 +27,3 @@ mod integration; mod tokenization_new; mod transfer_restrictions_new; mod voting_new; -// mod access_control; -// mod asset; -// mod branch; -// mod initialize; -// mod transfer; -// mod types; diff --git a/contracts/assetsup/src/tests/tokenization.rs b/contracts/assetsup/src/tests/tokenization.rs new file mode 100644 index 0000000..bc1a024 --- /dev/null +++ b/contracts/assetsup/src/tests/tokenization.rs @@ -0,0 +1,463 @@ +use crate::tests::helpers::*; +use crate::types::AssetType; +use soroban_sdk::String; + +#[test] +fn test_tokenize_asset_success() { + let env = create_env(); + let (admin, user1, _, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + let result = client.tokenize_asset( + &1u64, + &String::from_str(&env, "TST"), + &1000000i128, + &6u32, + &100i128, + &user1, + &String::from_str(&env, "Test Token"), + &String::from_str(&env, "A test tokenized asset"), + &AssetType::Physical, + ); + + assert_eq!(result.asset_id, 1); + assert_eq!(result.total_supply, 1000000); + assert_eq!(result.tokenizer, user1); + assert_eq!(result.tokens_in_circulation, 1000000); +} + +#[test] +#[should_panic(expected = "Error(Contract, #10)")] +fn test_tokenize_asset_already_tokenized() { + let env = create_env(); + let (admin, user1, _, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + client.tokenize_asset( + &1u64, + &String::from_str(&env, "TST"), + &1000000i128, + &6u32, + &100i128, + &user1, + &String::from_str(&env, "Test Token"), + &String::from_str(&env, "A test tokenized asset"), + &AssetType::Physical, + ); + + // Try to tokenize again - should panic with AssetAlreadyTokenized + client.tokenize_asset( + &1u64, + &String::from_str(&env, "TST2"), + &500000i128, + &6u32, + &100i128, + &user1, + &String::from_str(&env, "Test Token 2"), + &String::from_str(&env, "Another test"), + &AssetType::Physical, + ); +} + +#[test] +#[should_panic(expected = "Error(Contract, #12)")] +fn test_tokenize_asset_invalid_supply() { + let env = create_env(); + let (admin, user1, _, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + // Should panic with InvalidTokenSupply error + client.tokenize_asset( + &1u64, + &String::from_str(&env, "TST"), + &0i128, // Invalid: zero supply + &6u32, + &100i128, + &user1, + &String::from_str(&env, "Test Token"), + &String::from_str(&env, "A test tokenized asset"), + &AssetType::Physical, + ); +} + +#[test] +fn test_mint_tokens_success() { + let env = create_env(); + let (admin, user1, _, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + client.tokenize_asset( + &1u64, + &String::from_str(&env, "TST"), + &1000000i128, + &6u32, + &100i128, + &user1, + &String::from_str(&env, "Test Token"), + &String::from_str(&env, "A test tokenized asset"), + &AssetType::Physical, + ); + + let result = client.mint_tokens(&1u64, &500000i128, &user1); + + assert_eq!(result.total_supply, 1500000); + assert_eq!(result.tokens_in_circulation, 1500000); +} + +#[test] +#[should_panic(expected = "Error(Contract, #8)")] +fn test_mint_tokens_unauthorized() { + let env = create_env(); + let (admin, user1, user2, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + client.tokenize_asset( + &1u64, + &String::from_str(&env, "TST"), + &1000000i128, + &6u32, + &100i128, + &user1, + &String::from_str(&env, "Test Token"), + &String::from_str(&env, "A test tokenized asset"), + &AssetType::Physical, + ); + + // user2 is not tokenizer - should panic with Unauthorized + client.mint_tokens(&1u64, &500000i128, &user2); +} + +#[test] +#[should_panic(expected = "Error(Contract, #11)")] +fn test_mint_tokens_not_tokenized() { + let env = create_env(); + let (admin, user1, _, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + // Should panic with AssetNotTokenized error + client.mint_tokens(&999u64, &500000i128, &user1); +} + +#[test] +fn test_burn_tokens_success() { + let env = create_env(); + let (admin, user1, _, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + client.tokenize_asset( + &1u64, + &String::from_str(&env, "TST"), + &1000000i128, + &6u32, + &100i128, + &user1, + &String::from_str(&env, "Test Token"), + &String::from_str(&env, "A test tokenized asset"), + &AssetType::Physical, + ); + + let result = client.burn_tokens(&1u64, &200000i128, &user1); + + assert_eq!(result.total_supply, 800000); + assert_eq!(result.tokens_in_circulation, 800000); +} + +#[test] +#[should_panic(expected = "Error(Contract, #14)")] +fn test_burn_tokens_insufficient_balance() { + let env = create_env(); + let (admin, user1, _, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + client.tokenize_asset( + &1u64, + &String::from_str(&env, "TST"), + &1000000i128, + &6u32, + &100i128, + &user1, + &String::from_str(&env, "Test Token"), + &String::from_str(&env, "A test tokenized asset"), + &AssetType::Physical, + ); + + // Should panic with InsufficientBalance error + client.burn_tokens(&1u64, &2000000i128, &user1); +} + +#[test] +fn test_transfer_tokens_success() { + let env = create_env(); + let (admin, user1, user2, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + client.tokenize_asset( + &1u64, + &String::from_str(&env, "TST"), + &1000000i128, + &6u32, + &100i128, + &user1, + &String::from_str(&env, "Test Token"), + &String::from_str(&env, "A test tokenized asset"), + &AssetType::Physical, + ); + + client.transfer_tokens(&1u64, &user1, &user2, &300000i128); + + // Verify balances + let balance1 = client.get_token_balance(&1u64, &user1); + let balance2 = client.get_token_balance(&1u64, &user2); + + assert_eq!(balance1, 700000); + assert_eq!(balance2, 300000); +} + +#[test] +#[should_panic(expected = "Error(Contract, #14)")] +fn test_transfer_tokens_insufficient_balance() { + let env = create_env(); + let (admin, user1, user2, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + client.tokenize_asset( + &1u64, + &String::from_str(&env, "TST"), + &1000000i128, + &6u32, + &100i128, + &user1, + &String::from_str(&env, "Test Token"), + &String::from_str(&env, "A test tokenized asset"), + &AssetType::Physical, + ); + + // Should panic with InsufficientBalance error + client.transfer_tokens(&1u64, &user1, &user2, &2000000i128); +} + +#[test] +#[should_panic(expected = "Error(Contract, #16)")] +fn test_transfer_tokens_locked() { + let env = create_env(); + let (admin, user1, user2, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + client.tokenize_asset( + &1u64, + &String::from_str(&env, "TST"), + &1000000i128, + &6u32, + &100i128, + &user1, + &String::from_str(&env, "Test Token"), + &String::from_str(&env, "A test tokenized asset"), + &AssetType::Physical, + ); + + // Lock tokens + let future_time = env.ledger().timestamp() + 1000; + client.lock_tokens(&1u64, &user1, &future_time, &user1); + + // Should panic with TokensAreLocked error + client.transfer_tokens(&1u64, &user1, &user2, &100000i128); +} + +#[test] +fn test_lock_unlock_tokens() { + let env = create_env(); + let (admin, user1, _, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + client.tokenize_asset( + &1u64, + &String::from_str(&env, "TST"), + &1000000i128, + &6u32, + &100i128, + &user1, + &String::from_str(&env, "Test Token"), + &String::from_str(&env, "A test tokenized asset"), + &AssetType::Physical, + ); + + // Initially not locked + assert!(!client.is_tokens_locked(&1u64, &user1)); + + // Lock tokens + let future_time = env.ledger().timestamp() + 1000; + client.lock_tokens(&1u64, &user1, &future_time, &user1); + + assert!(client.is_tokens_locked(&1u64, &user1)); + + // Unlock tokens + client.unlock_tokens(&1u64, &user1); + + assert!(!client.is_tokens_locked(&1u64, &user1)); +} + +#[test] +#[should_panic(expected = "Error(Contract, #8)")] +fn test_lock_tokens_unauthorized() { + let env = create_env(); + let (admin, user1, user2, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + client.tokenize_asset( + &1u64, + &String::from_str(&env, "TST"), + &1000000i128, + &6u32, + &100i128, + &user1, + &String::from_str(&env, "Test Token"), + &String::from_str(&env, "A test tokenized asset"), + &AssetType::Physical, + ); + + let future_time = env.ledger().timestamp() + 1000; + + // user2 is not tokenizer - should panic with Unauthorized + client.lock_tokens(&1u64, &user1, &future_time, &user2); +} + +#[test] +fn test_get_ownership_percentage() { + let env = create_env(); + let (admin, user1, user2, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + client.tokenize_asset( + &1u64, + &String::from_str(&env, "TST"), + &1000000i128, + &6u32, + &100i128, + &user1, + &String::from_str(&env, "Test Token"), + &String::from_str(&env, "A test tokenized asset"), + &AssetType::Physical, + ); + + // Transfer 30% to user2 + client.transfer_tokens(&1u64, &user1, &user2, &300000i128); + + // Check ownership percentages (in basis points) + let percentage1 = client.get_ownership_percentage(&1u64, &user1); + let percentage2 = client.get_ownership_percentage(&1u64, &user2); + + assert_eq!(percentage1, 7000); // 70% + assert_eq!(percentage2, 3000); // 30% +} + +#[test] +fn test_get_token_holders() { + let env = create_env(); + let (admin, user1, user2, user3) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + client.tokenize_asset( + &1u64, + &String::from_str(&env, "TST"), + &1000000i128, + &6u32, + &100i128, + &user1, + &String::from_str(&env, "Test Token"), + &String::from_str(&env, "A test tokenized asset"), + &AssetType::Physical, + ); + + // Initially only user1 + let holders = client.get_token_holders(&1u64); + assert_eq!(holders.len(), 1); + + // Transfer to user2 and user3 + client.transfer_tokens(&1u64, &user1, &user2, &300000i128); + client.transfer_tokens(&1u64, &user1, &user3, &200000i128); + + // Now should have 3 holders + let holders = client.get_token_holders(&1u64); + assert_eq!(holders.len(), 3); +} + +#[test] +fn test_update_valuation() { + let env = create_env(); + let (admin, user1, _, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + client.tokenize_asset( + &1u64, + &String::from_str(&env, "TST"), + &1000000i128, + &6u32, + &100i128, + &user1, + &String::from_str(&env, "Test Token"), + &String::from_str(&env, "A test tokenized asset"), + &AssetType::Physical, + ); + + client.update_valuation(&1u64, &2000000i128); + + let asset = client.get_tokenized_asset(&1u64); + assert_eq!(asset.valuation, 2000000); +} + +#[test] +#[should_panic(expected = "Error(Contract, #30)")] +fn test_update_valuation_invalid() { + let env = create_env(); + let (admin, user1, _, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + client.tokenize_asset( + &1u64, + &String::from_str(&env, "TST"), + &1000000i128, + &6u32, + &100i128, + &user1, + &String::from_str(&env, "Test Token"), + &String::from_str(&env, "A test tokenized asset"), + &AssetType::Physical, + ); + + // Should panic with InvalidValuation error + client.update_valuation(&1u64, &0i128); +} diff --git a/contracts/assetsup/src/tests/transfer_restrictions.rs b/contracts/assetsup/src/tests/transfer_restrictions.rs new file mode 100644 index 0000000..8cc39c2 --- /dev/null +++ b/contracts/assetsup/src/tests/transfer_restrictions.rs @@ -0,0 +1,198 @@ +use crate::tests::helpers::*; +use crate::types::AssetType; +use soroban_sdk::String; + +#[test] +fn test_add_to_whitelist() { + let env = create_env(); + let (admin, user1, user2, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + client.tokenize_asset( + &1u64, + &String::from_str(&env, "TST"), + &1000000i128, + &6u32, + &100i128, + &user1, + &String::from_str(&env, "Test Token"), + &String::from_str(&env, "A test tokenized asset"), + &AssetType::Physical, + ); + + // Initially not whitelisted + assert!(!client.is_whitelisted(&1u64, &user2)); + + // Add to whitelist + client.add_to_whitelist(&1u64, &user2); + + assert!(client.is_whitelisted(&1u64, &user2)); +} + +#[test] +fn test_remove_from_whitelist() { + let env = create_env(); + let (admin, user1, user2, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + client.tokenize_asset( + &1u64, + &String::from_str(&env, "TST"), + &1000000i128, + &6u32, + &100i128, + &user1, + &String::from_str(&env, "Test Token"), + &String::from_str(&env, "A test tokenized asset"), + &AssetType::Physical, + ); + + // Add to whitelist + client.add_to_whitelist(&1u64, &user2); + assert!(client.is_whitelisted(&1u64, &user2)); + + // Remove from whitelist + client.remove_from_whitelist(&1u64, &user2); + assert!(!client.is_whitelisted(&1u64, &user2)); +} + +#[test] +fn test_get_whitelist() { + let env = create_env(); + let (admin, user1, user2, user3) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + client.tokenize_asset( + &1u64, + &String::from_str(&env, "TST"), + &1000000i128, + &6u32, + &100i128, + &user1, + &String::from_str(&env, "Test Token"), + &String::from_str(&env, "A test tokenized asset"), + &AssetType::Physical, + ); + + // Add multiple addresses to whitelist + client.add_to_whitelist(&1u64, &user2); + client.add_to_whitelist(&1u64, &user3); + + let whitelist = client.get_whitelist(&1u64); + assert_eq!(whitelist.len(), 2); +} + +#[test] +fn test_add_duplicate_to_whitelist() { + let env = create_env(); + let (admin, user1, user2, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + client.tokenize_asset( + &1u64, + &String::from_str(&env, "TST"), + &1000000i128, + &6u32, + &100i128, + &user1, + &String::from_str(&env, "Test Token"), + &String::from_str(&env, "A test tokenized asset"), + &AssetType::Physical, + ); + + // Add to whitelist twice + client.add_to_whitelist(&1u64, &user2); + client.add_to_whitelist(&1u64, &user2); + + // Should still only have one entry + let whitelist = client.get_whitelist(&1u64); + assert_eq!(whitelist.len(), 1); +} + +#[test] +fn test_set_transfer_restriction() { + let env = create_env(); + let (admin, user1, _, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + client.tokenize_asset( + &1u64, + &String::from_str(&env, "TST"), + &1000000i128, + &6u32, + &100i128, + &user1, + &String::from_str(&env, "Test Token"), + &String::from_str(&env, "A test tokenized asset"), + &AssetType::Physical, + ); + + // Set transfer restriction + client.set_transfer_restriction(&1u64, &true); + + // Restriction should be set (no error means success) +} + +#[test] +fn test_transfer_with_whitelist() { + let env = create_env(); + let (admin, user1, user2, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + client.tokenize_asset( + &1u64, + &String::from_str(&env, "TST"), + &1000000i128, + &6u32, + &100i128, + &user1, + &String::from_str(&env, "Test Token"), + &String::from_str(&env, "A test tokenized asset"), + &AssetType::Physical, + ); + + // Add user2 to whitelist + client.add_to_whitelist(&1u64, &user2); + + // Transfer should succeed + client.transfer_tokens(&1u64, &user1, &user2, &100000i128); + + let balance = client.get_token_balance(&1u64, &user2); + assert_eq!(balance, 100000); +} + +#[test] +fn test_empty_whitelist() { + let env = create_env(); + let (admin, user1, _, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + client.tokenize_asset( + &1u64, + &String::from_str(&env, "TST"), + &1000000i128, + &6u32, + &100i128, + &user1, + &String::from_str(&env, "Test Token"), + &String::from_str(&env, "A test tokenized asset"), + &AssetType::Physical, + ); + + let whitelist = client.get_whitelist(&1u64); + assert_eq!(whitelist.len(), 0); +} diff --git a/contracts/assetsup/src/tests/voting.rs b/contracts/assetsup/src/tests/voting.rs new file mode 100644 index 0000000..0856870 --- /dev/null +++ b/contracts/assetsup/src/tests/voting.rs @@ -0,0 +1,187 @@ +use crate::tests::helpers::*; +use crate::types::AssetType; +use soroban_sdk::String; + +#[test] +fn test_cast_vote_success() { + let env = create_env(); + let (admin, user1, _, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + // Tokenize asset + client.tokenize_asset( + &1u64, + &String::from_str(&env, "TST"), + &1000000i128, + &6u32, + &100i128, + &user1, + &String::from_str(&env, "Test Token"), + &String::from_str(&env, "A test tokenized asset"), + &AssetType::Physical, + ); + + // Cast vote + client.cast_vote(&1u64, &1u64, &user1); + + // Verify vote was recorded + assert!(client.has_voted(&1u64, &1u64, &user1)); + + // Verify vote tally + let tally = client.get_vote_tally(&1u64, &1u64); + assert_eq!(tally, 1000000); // Full balance +} + +#[test] +#[should_panic(expected = "Error(Contract, #22)")] +fn test_cast_vote_already_voted() { + let env = create_env(); + let (admin, user1, _, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + client.tokenize_asset( + &1u64, + &String::from_str(&env, "TST"), + &1000000i128, + &6u32, + &100i128, + &user1, + &String::from_str(&env, "Test Token"), + &String::from_str(&env, "A test tokenized asset"), + &AssetType::Physical, + ); + + client.cast_vote(&1u64, &1u64, &user1); + + // Try to vote again - should panic with AlreadyVoted + client.cast_vote(&1u64, &1u64, &user1); +} + +#[test] +#[should_panic(expected = "Error(Contract, #21)")] +fn test_cast_vote_insufficient_voting_power() { + let env = create_env(); + let (admin, user1, user2, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + // Tokenize with high voting threshold + client.tokenize_asset( + &1u64, + &String::from_str(&env, "TST"), + &1000000i128, + &6u32, + &500000i128, // 50% threshold + &user1, + &String::from_str(&env, "Test Token"), + &String::from_str(&env, "A test tokenized asset"), + &AssetType::Physical, + ); + + // Transfer small amount to user2 + client.transfer_tokens(&1u64, &user1, &user2, &10000i128); + + // user2 doesn't have enough tokens - should panic with InsufficientVotingPower + client.cast_vote(&1u64, &1u64, &user2); +} + +#[test] +fn test_proposal_passed() { + let env = create_env(); + let (admin, user1, user2, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + client.tokenize_asset( + &1u64, + &String::from_str(&env, "TST"), + &1000000i128, + &6u32, + &100i128, + &user1, + &String::from_str(&env, "Test Token"), + &String::from_str(&env, "A test tokenized asset"), + &AssetType::Physical, + ); + + // Transfer 60% to user2 + client.transfer_tokens(&1u64, &user1, &user2, &600000i128); + + // user2 votes (60% of supply) + client.cast_vote(&1u64, &1u64, &user2); + + // Proposal should pass (>50% threshold) + assert!(client.proposal_passed(&1u64, &1u64)); +} + +#[test] +fn test_proposal_not_passed() { + let env = create_env(); + let (admin, user1, user2, _) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + client.tokenize_asset( + &1u64, + &String::from_str(&env, "TST"), + &1000000i128, + &6u32, + &100i128, + &user1, + &String::from_str(&env, "Test Token"), + &String::from_str(&env, "A test tokenized asset"), + &AssetType::Physical, + ); + + // Transfer 40% to user2 + client.transfer_tokens(&1u64, &user1, &user2, &400000i128); + + // user2 votes (40% of supply) + client.cast_vote(&1u64, &1u64, &user2); + + // Proposal should not pass (<50% threshold) + assert!(!client.proposal_passed(&1u64, &1u64)); +} + +#[test] +fn test_multiple_voters() { + let env = create_env(); + let (admin, user1, user2, user3) = create_mock_addresses(&env); + let client = initialize_contract(&env, &admin); + + env.mock_all_auths(); + + client.tokenize_asset( + &1u64, + &String::from_str(&env, "TST"), + &1000000i128, + &6u32, + &100i128, + &user1, + &String::from_str(&env, "Test Token"), + &String::from_str(&env, "A test tokenized asset"), + &AssetType::Physical, + ); + + // Distribute tokens + client.transfer_tokens(&1u64, &user1, &user2, &300000i128); + client.transfer_tokens(&1u64, &user1, &user3, &200000i128); + + // Multiple users vote + client.cast_vote(&1u64, &1u64, &user1); // 500000 + client.cast_vote(&1u64, &1u64, &user2); // 300000 + + // Total tally should be 800000 + let tally = client.get_vote_tally(&1u64, &1u64); + assert_eq!(tally, 800000); + + // Proposal should pass (80% > 50%) + assert!(client.proposal_passed(&1u64, &1u64)); +}