"""Tests for power commands.""" import asyncio import time import pytest from mudlib.combat.encounter import CombatEncounter from mudlib.combat.engine import active_encounters from mudlib.commands.power import cmd_power @pytest.mark.asyncio async def test_power_down_instantly_lowers_pl(player): """Test power down instantly lowers PL to minimum.""" player.pl = 100.0 player.max_pl = 100.0 await cmd_power(player, "down") assert player.pl == 1.0 messages = [call[0][0] for call in player.writer.write.call_args_list] assert any("power down" in msg.lower() for msg in messages) @pytest.mark.asyncio async def test_power_to_number_lowers_instantly(player): """Test power instantly lowers when target < current.""" player.pl = 100.0 player.max_pl = 100.0 await cmd_power(player, "50") assert player.pl == 50.0 messages = [call[0][0] for call in player.writer.write.call_args_list] assert any("50" in msg for msg in messages) @pytest.mark.asyncio async def test_power_to_number_rejects_zero(player): """Test power rejects 0 or negative values.""" player.pl = 100.0 await cmd_power(player, "0") assert player.pl == 100.0 # unchanged messages = [call[0][0] for call in player.writer.write.call_args_list] assert any("must be greater than 0" in msg.lower() for msg in messages) @pytest.mark.asyncio async def test_power_to_number_rejects_above_max(player): """Test power rejects values above max_pl.""" player.pl = 100.0 player.max_pl = 100.0 await cmd_power(player, "150") assert player.pl == 100.0 # unchanged messages = [call[0][0] for call in player.writer.write.call_args_list] assert any("maximum" in msg.lower() for msg in messages) @pytest.mark.asyncio async def test_power_up_starts_increasing_pl(player): """Test power up starts a loop that increases PL over time.""" player.pl = 10.0 player.max_pl = 100.0 player.stamina = 100.0 await cmd_power(player, "up") # Give it a moment to run a few ticks await asyncio.sleep(0.15) assert player.pl > 10.0 assert player.pl < player.max_pl @pytest.mark.asyncio async def test_power_up_deducts_stamina(player): """Test power up deducts stamina per tick.""" player.pl = 10.0 player.max_pl = 100.0 player.stamina = 100.0 await cmd_power(player, "up") # Give it a moment to run a few ticks await asyncio.sleep(0.15) assert player.stamina < 100.0 @pytest.mark.asyncio async def test_power_up_stops_at_max_pl(player): """Test power up stops when PL reaches max_pl.""" player.pl = 95.0 player.max_pl = 100.0 player.stamina = 100.0 await cmd_power(player, "up") # Wait for it to complete await asyncio.sleep(0.3) assert player.pl == 100.0 assert player._power_task is None or player._power_task.done() @pytest.mark.asyncio async def test_power_up_stops_when_stamina_depleted(player): """Test power up stops when stamina runs out.""" player.pl = 10.0 player.max_pl = 100.0 player.stamina = 5.0 # Very low stamina await cmd_power(player, "up") # Wait for stamina to deplete await asyncio.sleep(0.3) # PL should have increased a little, but stopped assert player.pl > 10.0 assert player.pl < 100.0 assert player.stamina <= 0.0 assert player._power_task is None or player._power_task.done() @pytest.mark.asyncio async def test_power_stop_cancels_power_up(player): """Test power stop cancels an ongoing power-up.""" player.pl = 10.0 player.max_pl = 100.0 player.stamina = 100.0 await cmd_power(player, "up") await asyncio.sleep(0.05) # Let it start await cmd_power(player, "stop") pl_at_stop = player.pl await asyncio.sleep(0.1) # Wait a bit more # PL should not have increased after stop assert player.pl == pl_at_stop assert player._power_task is None or player._power_task.done() @pytest.mark.asyncio async def test_power_up_rejects_during_combat(player, nearby_player): """Test power up is rejected when player is in combat.""" player.pl = 50.0 player.stamina = 100.0 # Put player in combat encounter = CombatEncounter( attacker=player, defender=nearby_player, last_action_at=time.monotonic() ) active_encounters.append(encounter) await cmd_power(player, "up") messages = [call[0][0] for call in player.writer.write.call_args_list] assert any("combat" in msg.lower() for msg in messages) assert player.pl == 50.0 # unchanged @pytest.mark.asyncio async def test_power_up_rejects_with_no_stamina(player): """Test power up is rejected when stamina is 0.""" player.pl = 50.0 player.stamina = 0.0 await cmd_power(player, "up") messages = [call[0][0] for call in player.writer.write.call_args_list] assert any("stamina" in msg.lower() for msg in messages) assert player.pl == 50.0 # unchanged @pytest.mark.asyncio async def test_power_up_sends_feedback_messages(player): """Test power up sends feedback to player.""" player.pl = 10.0 player.max_pl = 100.0 player.stamina = 100.0 await cmd_power(player, "up") messages = [call[0][0] for call in player.writer.write.call_args_list] assert any("powering up" in msg.lower() for msg in messages) @pytest.mark.asyncio async def test_power_up_broadcasts_to_nearby(player, nearby_player): """Test power up broadcasts to nearby players.""" player.pl = 10.0 player.max_pl = 100.0 player.stamina = 100.0 await cmd_power(player, "up") nearby_messages = [call[0][0] for call in nearby_player.writer.write.call_args_list] assert any("Goku" in msg and "power" in msg.lower() for msg in nearby_messages) @pytest.mark.asyncio async def test_power_up_when_already_powering_up(player): """Test power up when already powering up sends message.""" player.pl = 10.0 player.max_pl = 100.0 player.stamina = 100.0 await cmd_power(player, "up") await asyncio.sleep(0.05) # Try to power up again await cmd_power(player, "up") messages = [call[0][0] for call in player.writer.write.call_args_list] assert any("already" in msg.lower() for msg in messages) @pytest.mark.asyncio async def test_power_down_broadcasts_to_nearby(player, nearby_player): """Test power down broadcasts to nearby players.""" player.pl = 100.0 await cmd_power(player, "down") nearby_messages = [call[0][0] for call in nearby_player.writer.write.call_args_list] assert any("Goku" in msg and "power" in msg.lower() for msg in nearby_messages) @pytest.mark.asyncio async def test_power_to_number_raises_with_loop(player): """Test power starts power-up loop when target > current.""" player.pl = 10.0 player.max_pl = 100.0 player.stamina = 100.0 await cmd_power(player, "80") # Give it a moment to run await asyncio.sleep(0.15) # Should be increasing toward 80 assert player.pl > 10.0 assert player.stamina < 100.0 # deducting stamina @pytest.mark.asyncio async def test_combat_start_cancels_power_up(player, nearby_player): """Test starting combat cancels any active power-up task.""" from mudlib.combat.engine import start_encounter # Start player powering up player.pl = 10.0 player.max_pl = 100.0 player.stamina = 100.0 await cmd_power(player, "up") await asyncio.sleep(0.05) # Let power-up start # Verify power-up is active assert player._power_task is not None assert not player._power_task.done() # Start combat encounter start_encounter(player, nearby_player) # Power-up task should be cancelled assert player._power_task is None or player._power_task.cancelled() @pytest.mark.asyncio async def test_combat_start_cancels_defender_power_up(player, nearby_player): """Test starting combat cancels defender's active power-up task.""" from mudlib.combat.engine import start_encounter # Start nearby_player powering up nearby_player.pl = 10.0 nearby_player.max_pl = 100.0 nearby_player.stamina = 100.0 await cmd_power(nearby_player, "up") await asyncio.sleep(0.05) # Let power-up start # Verify power-up is active assert nearby_player._power_task is not None assert not nearby_player._power_task.done() # Start combat encounter (nearby_player is defender) start_encounter(player, nearby_player) # Power-up task should be cancelled assert nearby_player._power_task is None or nearby_player._power_task.cancelled()