@@ -1474,5 +1474,60 @@ mod tests {
14741474 // Attempt to spend 50 gems which is more than the balance; should panic
14751475 store . spend_gems (player_address , 50_u64 );
14761476 }
1477- }
14781477
1478+ #[test]
1479+ #[should_panic(expected: ' Trap not active' )]
1480+ fn test_store_trigger_trap_concurrent_access () {
1481+ let player_system_contract_address = create_test_player_system ();
1482+ let world = create_test_world (player_system_contract_address );
1483+ let mut store_tx1 : Store = StoreTrait :: new (world . clone ()); // Simulate transaction 1
1484+ let mut store_tx2 : Store = StoreTrait :: new (world . clone ()); // Simulate transaction 2
1485+
1486+ let trap = create_sample_trap ();
1487+ store_tx1 . place_trap (trap ); // Place trap using one store instance
1488+
1489+ let enemy_pos = Vec2 { x : 12_u32 , y : 16_u32 }; // Within radius
1490+
1491+ // Scenario: Both transactions read the active trap, then try to trigger
1492+ // Transaction 1 triggers
1493+ let damage_tx1 = store_tx1 . trigger_trap (1_u32 , enemy_pos );
1494+
1495+ // Transaction 2 tries to trigger, but the trap is already inactive due to TX1
1496+ let damage_tx2 = store_tx2 . trigger_trap (1_u32 , enemy_pos );
1497+
1498+ // Assertions
1499+ assert (damage_tx1 == 50_u16 , ' TX1 should deal 50 damage' );
1500+ assert! (damage_tx2 == 0_u16 , " TX2 should deal 0 damage due to race condition protection" );
1501+
1502+ let final_trap_state = store_tx1 . read_trap (1_u32 );
1503+ assert! (final_trap_state . is_active == false , " Trap should be consumed after one trigger" );
1504+
1505+ // Verify that the second store instance also reflects the inactive state
1506+ let final_trap_state_tx2 = store_tx2 . read_trap (1_u32 );
1507+ assert! (final_trap_state_tx2 . is_active == false , " TX2 view should also show inactive trap" );
1508+ }
1509+
1510+ #[test]
1511+ #[should_panic(expected: ' Trap not active' )]
1512+ fn test_store_trigger_trap_already_inactive_returns_zero () {
1513+ let player_system_contract_address = create_test_player_system ();
1514+ let world = create_test_world (player_system_contract_address );
1515+ let mut store : Store = StoreTrait :: new (world );
1516+
1517+ let mut trap = create_sample_trap ();
1518+ store . place_trap (trap );
1519+
1520+ let enemy_pos = Vec2 { x : 12_u32 , y : 16_u32 }; // Within radius
1521+
1522+ // First trigger works
1523+ let damage1 = store . trigger_trap (1_u32 , enemy_pos );
1524+ assert! (damage1 == 50_u16 , " First trigger should deal damage" );
1525+
1526+ // Second trigger (trap is already inactive) should return 0, no panic
1527+ let damage2 = store . trigger_trap (1_u32 , enemy_pos );
1528+ assert! (damage2 == 0_u16 , " Second trigger on inactive trap should return 0" );
1529+
1530+ let final_trap = store . read_trap (1_u32 );
1531+ assert (final_trap . is_active == false , ' Trap should remain inactive' );
1532+ }
1533+ }
0 commit comments