"""Tests for rest command.""" from unittest.mock import AsyncMock, MagicMock import pytest import mudlib.commands.movement as movement_mod from mudlib.commands.rest import cmd_rest from mudlib.player import Player, players from mudlib.resting import process_resting @pytest.fixture(autouse=True) def clear_state(): """Clear players before and after each test.""" players.clear() yield players.clear() @pytest.fixture(autouse=True) def mock_world(): """Inject a mock world for send_nearby_message.""" fake_world = MagicMock() fake_world.width = 256 fake_world.height = 256 old = movement_mod.world movement_mod.world = fake_world yield fake_world movement_mod.world = old @pytest.fixture def mock_writer(): writer = MagicMock() writer.write = MagicMock() writer.drain = AsyncMock() return writer @pytest.fixture def mock_reader(): return MagicMock() @pytest.fixture def player(mock_reader, mock_writer): p = Player(name="Goku", x=0, y=0, reader=mock_reader, writer=mock_writer) players[p.name] = p return p @pytest.fixture def nearby_player(mock_reader, mock_writer): p = Player(name="Vegeta", x=0, y=0, reader=mock_reader, writer=mock_writer) players[p.name] = p return p @pytest.mark.asyncio async def test_rest_when_full_sends_not_tired_message(player): """Test resting when at full stamina sends 'not tired' message.""" player.stamina = 100.0 player.max_stamina = 100.0 await cmd_rest(player, "") player.writer.write.assert_called_once_with("You're not tired.\r\n") assert player.stamina == 100.0 assert not player.resting @pytest.mark.asyncio async def test_rest_when_not_resting_starts_resting(player): """Test rest command when not resting starts the resting state.""" player.stamina = 50.0 player.max_stamina = 100.0 player.resting = False await cmd_rest(player, "") assert player.resting messages = [call[0][0] for call in player.writer.write.call_args_list] assert any("begin to rest" in msg for msg in messages) @pytest.mark.asyncio async def test_rest_when_already_resting_stops_resting(player): """Test rest command when already resting stops the resting state.""" player.stamina = 50.0 player.max_stamina = 100.0 player.resting = True await cmd_rest(player, "") assert not player.resting messages = [call[0][0] for call in player.writer.write.call_args_list] assert any("stop resting" in msg for msg in messages) @pytest.mark.asyncio async def test_rest_broadcasts_begin_to_nearby_players(player, nearby_player): """Test resting broadcasts begin message to nearby players.""" player.stamina = 50.0 player.resting = False await cmd_rest(player, "") nearby_messages = [call[0][0] for call in nearby_player.writer.write.call_args_list] assert any("Goku begins to rest" in msg for msg in nearby_messages) @pytest.mark.asyncio async def test_rest_broadcasts_stop_to_nearby_players(player, nearby_player): """Test stopping rest broadcasts stop message to nearby players.""" player.stamina = 50.0 player.resting = True await cmd_rest(player, "") nearby_messages = [call[0][0] for call in nearby_player.writer.write.call_args_list] assert any("Goku stops resting" in msg for msg in nearby_messages) @pytest.mark.asyncio async def test_process_resting_ticks_up_stamina(player): """Test process_resting increases stamina for resting players.""" player.stamina = 50.0 player.max_stamina = 100.0 player.resting = True await process_resting() assert player.stamina == 50.2 # 50 + 0.2 per tick @pytest.mark.asyncio async def test_process_resting_auto_stops_when_full(player): """Test process_resting auto-stops resting when stamina reaches max.""" player.stamina = 99.9 player.max_stamina = 100.0 player.resting = True await process_resting() assert player.stamina == 100.0 assert not player.resting messages = [call[0][0] for call in player.writer.write.call_args_list] assert any("fully rested" in msg for msg in messages) @pytest.mark.asyncio async def test_process_resting_broadcasts_when_auto_stopping(player, nearby_player): """Test process_resting broadcasts when auto-stopping rest.""" player.stamina = 99.9 player.max_stamina = 100.0 player.resting = True await process_resting() nearby_messages = [call[0][0] for call in nearby_player.writer.write.call_args_list] assert any("Goku stops resting" in msg for msg in nearby_messages) @pytest.mark.asyncio async def test_process_resting_ignores_non_resting_players(player): """Test process_resting doesn't modify stamina for non-resting players.""" player.stamina = 50.0 player.max_stamina = 100.0 player.resting = False await process_resting() assert player.stamina == 50.0 # unchanged @pytest.mark.asyncio async def test_stamina_doesnt_exceed_max(player): """Test stamina doesn't exceed max_stamina during resting.""" player.stamina = 99.95 player.max_stamina = 100.0 player.resting = True await process_resting() assert player.stamina == 100.0 # capped at max