"""Tests for portal enter command.""" from unittest.mock import AsyncMock, MagicMock import pytest from mudlib.commands import _registry from mudlib.player import Player from mudlib.portal import Portal from mudlib.zone import Zone from mudlib.zones import register_zone, zone_registry @pytest.fixture def test_zone(): terrain = [["." for _ in range(10)] for _ in range(10)] return Zone( name="testzone", width=10, height=10, toroidal=True, terrain=terrain, ) @pytest.fixture def target_zone(): terrain = [["." for _ in range(10)] for _ in range(10)] return Zone( name="targetzone", width=10, height=10, toroidal=True, terrain=terrain, ) @pytest.fixture def player(mock_reader, mock_writer, test_zone): p = Player( name="TestPlayer", x=5, y=5, reader=mock_reader, writer=mock_writer, location=test_zone, ) return p @pytest.fixture(autouse=True) def clear_zones(): """Clear zone registry before and after each test.""" zone_registry.clear() yield zone_registry.clear() # --- cmd_enter --- @pytest.mark.asyncio async def test_enter_moves_to_target_zone(player, test_zone, target_zone): """enter portal moves player to target zone.""" from mudlib.commands.portals import cmd_enter register_zone("targetzone", target_zone) Portal( name="shimmering doorway", aliases=["doorway"], location=test_zone, x=5, y=5, target_zone="targetzone", target_x=3, target_y=7, ) await cmd_enter(player, "doorway") assert player.location is target_zone assert player.x == 3 assert player.y == 7 assert player in target_zone.contents assert player not in test_zone.contents @pytest.mark.asyncio async def test_enter_sends_departure_message( player, test_zone, target_zone, mock_writer ): """enter portal sends departure message to old zone.""" from mudlib.commands.portals import cmd_enter register_zone("targetzone", target_zone) Portal( name="portal", location=test_zone, x=5, y=5, target_zone="targetzone", target_x=3, target_y=7, ) other_player = Player( name="OtherPlayer", x=5, y=5, reader=MagicMock(), writer=MagicMock(write=MagicMock(), drain=AsyncMock()), location=test_zone, ) await cmd_enter(player, "portal") # Check that other player received departure message other_writer = other_player.writer calls = [call[0][0] for call in other_writer.write.call_args_list] assert any("TestPlayer" in call and "enter" in call.lower() for call in calls) @pytest.mark.asyncio async def test_enter_sends_arrival_message(player, test_zone, target_zone): """enter portal sends arrival message to new zone.""" from mudlib.commands.portals import cmd_enter register_zone("targetzone", target_zone) Portal( name="portal", location=test_zone, x=5, y=5, target_zone="targetzone", target_x=3, target_y=7, ) other_player = Player( name="OtherPlayer", x=3, y=7, reader=MagicMock(), writer=MagicMock(write=MagicMock(), drain=AsyncMock()), location=target_zone, ) await cmd_enter(player, "portal") # Check that other player in target zone received arrival message other_writer = other_player.writer calls = [call[0][0] for call in other_writer.write.call_args_list] assert any("TestPlayer" in call and "arrive" in call.lower() for call in calls) @pytest.mark.asyncio async def test_enter_shows_look_in_new_zone( player, test_zone, target_zone, mock_writer ): """enter portal triggers look command in new zone.""" from mudlib.commands.portals import cmd_enter register_zone("targetzone", target_zone) Portal( name="portal", location=test_zone, x=5, y=5, target_zone="targetzone", target_x=3, target_y=7, ) await cmd_enter(player, "portal") # Check that look output was sent (viewport grid should be in output) output = "".join([call[0][0] for call in mock_writer.write.call_args_list]) # Should have the @ symbol for player position assert "@" in output @pytest.mark.asyncio async def test_enter_no_args(player, mock_writer): """enter with no arguments gives usage hint.""" from mudlib.commands.portals import cmd_enter await cmd_enter(player, "") output = mock_writer.write.call_args_list[-1][0][0] assert "enter what" in output.lower() or "usage" in output.lower() @pytest.mark.asyncio async def test_enter_portal_not_found(player, test_zone, mock_writer): """enter with portal not at position gives feedback.""" from mudlib.commands.portals import cmd_enter await cmd_enter(player, "doorway") output = mock_writer.write.call_args_list[-1][0][0] assert "don't see" in output.lower() @pytest.mark.asyncio async def test_enter_target_zone_not_found(player, test_zone, mock_writer): """enter with invalid target zone gives graceful error.""" from mudlib.commands.portals import cmd_enter Portal( name="portal", location=test_zone, x=5, y=5, target_zone="nonexistent", target_x=3, target_y=7, ) await cmd_enter(player, "portal") output = mock_writer.write.call_args_list[-1][0][0] assert "doesn't lead anywhere" in output.lower() or "nowhere" in output.lower() # Player should still be in original zone assert player.location is test_zone @pytest.mark.asyncio async def test_enter_matches_aliases(player, test_zone, target_zone): """enter matches portal aliases.""" from mudlib.commands.portals import cmd_enter register_zone("targetzone", target_zone) Portal( name="shimmering doorway", aliases=["door", "doorway"], location=test_zone, x=5, y=5, target_zone="targetzone", target_x=3, target_y=7, ) await cmd_enter(player, "door") assert player.location is target_zone @pytest.mark.asyncio async def test_enter_portal_elsewhere_in_zone(player, test_zone, mock_writer): """enter doesn't find portals not at player position.""" from mudlib.commands.portals import cmd_enter Portal( name="distant portal", location=test_zone, x=8, y=8, target_zone="targetzone", target_x=3, target_y=7, ) await cmd_enter(player, "portal") output = mock_writer.write.call_args_list[-1][0][0] assert "don't see" in output.lower() # Player should still be in original zone assert player.location is test_zone # --- command registration --- def test_enter_command_registered(): """enter command is registered.""" import mudlib.commands.portals # noqa: F401 assert "enter" in _registry