#[cfg(test)] mod test_role_assignments { use crate::permission::types::*; use crate::*; use near_sdk::test_utils::{accounts, VMContextBuilder}; use near_sdk::testing_env; fn setup_context() { let mut context = VMContextBuilder::new(); context .predecessor_account_id(accounts(0)) .signer_account_id(accounts(0)) .block_timestamp(1_000_000_000_000_000_000); testing_env!(context.build()); } #[test] fn test_get_all_role_assignments_multiple_roles() { setup_context(); let mut contract = Contract::default(); // Create a target and assign multiple roles to it let target = RoleTarget::AccountId(accounts(1)); // Grant multiple roles to the same target contract.internal_grant_role("role1".to_string(), target.clone()); contract.internal_grant_role("role2".to_string(), target.clone()); contract.internal_grant_role("role3".to_string(), target.clone()); // Also create another target with a single role to ensure it works let target2 = RoleTarget::AccountId(accounts(2)); contract.internal_grant_role("role4".to_string(), target2.clone()); // Get all role assignments let assignments = contract.get_all_role_assignments(); // Print debug information println!("All role assignments:"); for (target, roles) in &assignments { println!(" Target: {}, Roles: {:?}", target, roles); } // Verify we have 3 targets (alice gets owner role automatically) assert_eq!( assignments.len(), 3, "Should have 3 targets with role assignments" ); // Find assignments for each target let target1_assignment = assignments.iter().find(|(t, _)| *t == target); let target2_assignment = assignments.iter().find(|(t, _)| *t == target2); // Verify target1 has all 3 roles assert!( target1_assignment.is_some(), "Target1 should have role assignments" ); let target1_roles = &target1_assignment.unwrap().1; assert_eq!( target1_roles.len(), 3, "Target1 should have 3 roles, but got: {:?}", target1_roles ); // Check that all roles are present (order doesn't matter) assert!( target1_roles.contains(&"role1".to_string()), "Should contain role1" ); assert!( target1_roles.contains(&"role2".to_string()), "Should contain role2" ); assert!( target1_roles.contains(&"role3".to_string()), "Should contain role3" ); // Verify target2 has 1 role assert!( target2_assignment.is_some(), "Target2 should have role assignments" ); let target2_roles = &target2_assignment.unwrap().1; assert_eq!(target2_roles.len(), 1, "Target2 should have 1 role"); assert!( target2_roles.contains(&"role4".to_string()), "Should contain role4" ); } #[test] fn test_get_roles_for_target_multiple_roles() { setup_context(); let mut contract = Contract::default(); let target = RoleTarget::AccountId(accounts(1)); // Grant multiple roles to the same target contract.internal_grant_role("role1".to_string(), target.clone()); contract.internal_grant_role("role2".to_string(), target.clone()); contract.internal_grant_role("role3".to_string(), target.clone()); // Get roles for this specific target let roles = contract.get_roles_for_target(target.clone()); println!("Roles for target {}: {:?}", target, roles); // Verify we get all 3 roles assert_eq!(roles.len(), 3, "Should have 3 roles, but got: {:?}", roles); assert!(roles.contains(&"role1".to_string()), "Should contain role1"); assert!(roles.contains(&"role2".to_string()), "Should contain role2"); assert!(roles.contains(&"role3".to_string()), "Should contain role3"); } #[test] fn test_get_targets_with_role_works_correctly() { setup_context(); let mut contract = Contract::default(); let target1 = RoleTarget::AccountId(accounts(1)); let target2 = RoleTarget::AccountId(accounts(2)); // Grant the same role to multiple targets contract.internal_grant_role("moderator".to_string(), target1.clone()); contract.internal_grant_role("moderator".to_string(), target2.clone()); // Also grant additional roles to each target contract.internal_grant_role("admin".to_string(), target1.clone()); contract.internal_grant_role("user".to_string(), target2.clone()); // Test get_targets_with_role for the shared role let moderator_targets = contract.get_targets_with_role("moderator".to_string()); println!("Targets with 'moderator' role: {:?}", moderator_targets); assert_eq!( moderator_targets.len(), 2, "Should find 2 targets with moderator role" ); assert!( moderator_targets.contains(&target1), "Should include target1" ); assert!( moderator_targets.contains(&target2), "Should include target2" ); // Test get_targets_with_role for unique roles let admin_targets = contract.get_targets_with_role("admin".to_string()); assert_eq!( admin_targets.len(), 1, "Should find 1 target with admin role" ); assert!( admin_targets.contains(&target1), "Should include target1 for admin" ); let user_targets = contract.get_targets_with_role("user".to_string()); assert_eq!(user_targets.len(), 1, "Should find 1 target with user role"); assert!( user_targets.contains(&target2), "Should include target2 for user" ); // Now let's check what get_all_role_assignments returns let assignments = contract.get_all_role_assignments(); println!("All assignments: {:?}", assignments); // This should show target1 with both admin and moderator roles // and target2 with both user and moderator roles for (target, roles) in &assignments { match target { RoleTarget::AccountId(account) => { if *account == accounts(1) { println!("Target1 roles: {:?}", roles); // This should have 2 roles but likely only shows 1 due to the bug } else if *account == accounts(2) { println!("Target2 roles: {:?}", roles); // This should have 2 roles but likely only shows 1 due to the bug } } _ => {} } } } #[test] fn test_debug_role_storage_iteration() { setup_context(); let mut contract = Contract::default(); let target = RoleTarget::AccountId(accounts(1)); // Grant roles one by one and debug after each println!("=== Adding role1 ==="); contract.internal_grant_role("role1".to_string(), target.clone()); // Debug: check contains vs iter if let Some(roles_set) = contract.kernel_state.role_assignments.get(&target) { println!( "Contains role1: {}", roles_set.contains(&Role("role1".to_string())) ); let all_roles: Vec = roles_set.iter().map(|role| role.0.clone()).collect(); println!("All roles via iter: {:?}", all_roles); } println!("=== Adding role2 ==="); contract.internal_grant_role("role2".to_string(), target.clone()); if let Some(roles_set) = contract.kernel_state.role_assignments.get(&target) { println!( "Contains role1: {}", roles_set.contains(&Role("role1".to_string())) ); println!( "Contains role2: {}", roles_set.contains(&Role("role2".to_string())) ); let all_roles: Vec = roles_set.iter().map(|role| role.0.clone()).collect(); println!("All roles via iter: {:?}", all_roles); } println!("=== Adding role3 ==="); contract.internal_grant_role("role3".to_string(), target.clone()); if let Some(roles_set) = contract.kernel_state.role_assignments.get(&target) { println!( "Contains role1: {}", roles_set.contains(&Role("role1".to_string())) ); println!( "Contains role2: {}", roles_set.contains(&Role("role2".to_string())) ); println!( "Contains role3: {}", roles_set.contains(&Role("role3".to_string())) ); let all_roles: Vec = roles_set.iter().map(|role| role.0.clone()).collect(); println!("All roles via iter: {:?}", all_roles); } // Final check with get_all_role_assignments let assignments = contract.get_all_role_assignments(); println!("=== Final get_all_role_assignments ==="); for (target, roles) in &assignments { if let RoleTarget::AccountId(account) = target { if *account == accounts(1) { println!("Target {} roles: {:?}", account, roles); } } } } #[test] fn test_multiple_targets_with_multiple_roles() { setup_context(); let mut contract = Contract::default(); // Create multiple targets with multiple roles each let target1 = RoleTarget::AccountId(accounts(1)); let target2 = RoleTarget::AccountId(accounts(2)); let target3 = RoleTarget::Codehash(Codehash::from("hash123".to_string())); // Grant roles to target1 contract.internal_grant_role("admin".to_string(), target1.clone()); contract.internal_grant_role("moderator".to_string(), target1.clone()); // Grant roles to target2 contract.internal_grant_role("user".to_string(), target2.clone()); contract.internal_grant_role("moderator".to_string(), target2.clone()); contract.internal_grant_role("viewer".to_string(), target2.clone()); // Grant role to target3 contract.internal_grant_role("system".to_string(), target3.clone()); let assignments = contract.get_all_role_assignments(); println!("All assignments:"); for (target, roles) in &assignments { println!(" {}: {:?}", target, roles); } assert_eq!(assignments.len(), 4, "Should have 4 targets"); // Check each target for (target, roles) in &assignments { match target { RoleTarget::AccountId(account) => { if *account == accounts(1) { assert_eq!(roles.len(), 2, "Target1 should have 2 roles"); assert!(roles.contains(&"admin".to_string())); assert!(roles.contains(&"moderator".to_string())); } else if *account == accounts(2) { assert_eq!(roles.len(), 3, "Target2 should have 3 roles"); assert!(roles.contains(&"user".to_string())); assert!(roles.contains(&"moderator".to_string())); assert!(roles.contains(&"viewer".to_string())); } } RoleTarget::Codehash(hash) => { if hash.0 == "hash123" { assert_eq!(roles.len(), 1, "Target3 should have 1 role"); assert!(roles.contains(&"system".to_string())); } } } } } #[test] fn test_role_assignment_revocation_and_reassignment_sequence() { setup_context(); let mut contract = Contract::default(); let target = RoleTarget::AccountId(accounts(1)); println!("=== Initial state ==="); let initial_roles = contract.get_roles_for_target(target.clone()); println!("Initial roles: {:?}", initial_roles); assert_eq!(initial_roles.len(), 0, "Should start with no roles"); println!("\n=== Step 1: Grant role1 ==="); contract.internal_grant_role("role1".to_string(), target.clone()); // Check state after granting role1 let roles_contains_1 = contract.internal_has_role_via_target(&target, "role1"); let roles_via_iter = contract.get_roles_for_target(target.clone()); let all_assignments = contract.get_all_role_assignments(); println!("Contains role1: {}", roles_contains_1); println!("Roles via iter: {:?}", roles_via_iter); println!("All assignments: {:?}", all_assignments); assert!(roles_contains_1, "Should contain role1 via contains()"); assert_eq!(roles_via_iter.len(), 1, "Should have 1 role via iter()"); assert!( roles_via_iter.contains(&"role1".to_string()), "Should contain role1 via iter()" ); println!("\n=== Step 2: Revoke role1 ==="); contract.internal_revoke_role("role1".to_string(), target.clone()); // Check state after revoking role1 let roles_contains_1_after_revoke = contract.internal_has_role_via_target(&target, "role1"); let roles_via_iter_after_revoke = contract.get_roles_for_target(target.clone()); let all_assignments_after_revoke = contract.get_all_role_assignments(); println!( "Contains role1 after revoke: {}", roles_contains_1_after_revoke ); println!( "Roles via iter after revoke: {:?}", roles_via_iter_after_revoke ); println!( "All assignments after revoke: {:?}", all_assignments_after_revoke ); assert!( !roles_contains_1_after_revoke, "Should NOT contain role1 after revoke" ); assert_eq!( roles_via_iter_after_revoke.len(), 0, "Should have 0 roles after revoke" ); println!("\n=== Step 3: Grant role2 ==="); contract.internal_grant_role("role2".to_string(), target.clone()); // Check state after granting role2 let roles_contains_2 = contract.internal_has_role_via_target(&target, "role2"); let roles_contains_1_still = contract.internal_has_role_via_target(&target, "role1"); let roles_via_iter_after_role2 = contract.get_roles_for_target(target.clone()); let all_assignments_after_role2 = contract.get_all_role_assignments(); println!( "Contains role1 after role2 grant: {}", roles_contains_1_still ); println!("Contains role2 after role2 grant: {}", roles_contains_2); println!( "Roles via iter after role2 grant: {:?}", roles_via_iter_after_role2 ); println!( "All assignments after role2 grant: {:?}", all_assignments_after_role2 ); assert!(!roles_contains_1_still, "Should NOT contain role1"); assert!(roles_contains_2, "Should contain role2 via contains()"); assert_eq!( roles_via_iter_after_role2.len(), 1, "Should have 1 role via iter()" ); assert!( roles_via_iter_after_role2.contains(&"role2".to_string()), "Should contain role2 via iter()" ); println!("\n=== Step 4: Grant role3 (testing multiple roles) ==="); contract.internal_grant_role("role3".to_string(), target.clone()); // Check state after granting role3 (should have both role2 and role3) let roles_contains_2_final = contract.internal_has_role_via_target(&target, "role2"); let roles_contains_3_final = contract.internal_has_role_via_target(&target, "role3"); let roles_via_iter_final = contract.get_roles_for_target(target.clone()); let all_assignments_final = contract.get_all_role_assignments(); println!("Contains role2 final: {}", roles_contains_2_final); println!("Contains role3 final: {}", roles_contains_3_final); println!("Roles via iter final: {:?}", roles_via_iter_final); println!("All assignments final: {:?}", all_assignments_final); assert!( roles_contains_2_final, "Should contain role2 via contains()" ); assert!( roles_contains_3_final, "Should contain role3 via contains()" ); // This is where the bug should manifest - iter() should show both roles but likely only shows role3 assert_eq!( roles_via_iter_final.len(), 2, "Should have 2 roles via iter() - THIS IS WHERE THE BUG SHOWS" ); assert!( roles_via_iter_final.contains(&"role2".to_string()), "Should contain role2 via iter()" ); assert!( roles_via_iter_final.contains(&"role3".to_string()), "Should contain role3 via iter()" ); } }