"""Tests for the Portal class.""" import pytest from mudlib.object import Object from mudlib.player import Player from mudlib.portal import Portal from mudlib.thing import Thing from mudlib.zone import Zone from mudlib.zones import register_zone, zone_registry # --- fixtures --- @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 zone_a(): terrain = [["." for _ in range(10)] for _ in range(10)] return Zone( name="zone_a", width=10, height=10, toroidal=True, terrain=terrain, ) @pytest.fixture def zone_b(): terrain = [["." for _ in range(10)] for _ in range(10)] return Zone( name="zone_b", 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() # --- construction --- def test_portal_forced_non_portable(): """Portal forces portable=False even if explicitly set True.""" # Even if we try to make it portable, it should be forced to False p = Portal(name="portal", portable=True) assert p.portable is False def test_portal_in_zone(): """Portal can exist in a zone with coordinates.""" terrain = [["." for _ in range(10)] for _ in range(10)] zone = Zone(name="test", width=10, height=10, terrain=terrain) p = Portal( name="gateway", location=zone, x=3, y=7, target_zone="dungeon", target_x=5, target_y=5, ) assert p.location is zone assert p.x == 3 assert p.y == 7 assert p in zone.contents def test_portal_rejects_contents(): """Portal cannot accept other objects (uses Thing's default behavior).""" p = Portal(name="portal") obj = Object(name="rock") assert p.can_accept(obj) is False def test_portal_rejects_things(): """Portal cannot accept things.""" p = Portal(name="portal") thing = Thing(name="sword") assert p.can_accept(thing) is False # --- portal display --- @pytest.mark.asyncio async def test_look_shows_portal_at_position(player, test_zone, mock_writer): """look command shows portals at player position.""" from mudlib.commands.look import cmd_look Portal( name="shimmering doorway", location=test_zone, x=5, y=5, target_zone="elsewhere", target_x=0, target_y=0, ) await cmd_look(player, "") output = "".join([call[0][0] for call in mock_writer.write.call_args_list]) # New format: "You see {portal.name}." assert "you see shimmering doorway." in output.lower() @pytest.mark.asyncio async def test_look_shows_multiple_portals(player, test_zone, mock_writer): """look command shows multiple portals at player position.""" from mudlib.commands.look import cmd_look Portal( name="red portal", location=test_zone, x=5, y=5, target_zone="redzone", target_x=0, target_y=0, ) Portal( name="blue portal", location=test_zone, x=5, y=5, target_zone="bluezone", target_x=0, target_y=0, ) await cmd_look(player, "") output = "".join([call[0][0] for call in mock_writer.write.call_args_list]) assert "red portal" in output.lower() assert "blue portal" in output.lower() @pytest.mark.asyncio async def test_look_no_portals_at_position(player, test_zone, mock_writer): """look command doesn't show portals when none at position.""" from mudlib.commands.look import cmd_look Portal( name="distant portal", location=test_zone, x=8, y=8, target_zone="elsewhere", target_x=0, target_y=0, ) await cmd_look(player, "") output = "".join([call[0][0] for call in mock_writer.write.call_args_list]) # Should not mention portals when none are at player position assert "portal" not in output.lower() or "distant portal" not in output.lower() # --- two-way portal transitions --- @pytest.mark.asyncio async def test_two_way_portal_transitions(mock_reader, mock_writer, zone_a, zone_b): """Portals work bidirectionally between zones.""" from mudlib.commands.portals import cmd_enter # Create player in zone_a at (2, 2) player = Player( name="TestPlayer", x=2, y=2, reader=mock_reader, writer=mock_writer, location=zone_a, ) # Register zones register_zone("zone_a", zone_a) register_zone("zone_b", zone_b) # Create portal in zone A pointing to zone B Portal( name="doorway to B", location=zone_a, x=2, y=2, target_zone="zone_b", target_x=7, target_y=7, ) # Create portal in zone B pointing to zone A Portal( name="doorway to A", location=zone_b, x=7, y=7, target_zone="zone_a", target_x=2, target_y=2, ) # Player starts in zone A at (2, 2) assert player.location is zone_a assert player.x == 2 assert player.y == 2 # Enter portal to zone B await cmd_enter(player, "doorway to B") assert player.location is zone_b assert player.x == 7 assert player.y == 7 # Enter portal back to zone A await cmd_enter(player, "doorway to A") assert player.location is zone_a assert player.x == 2 assert player.y == 2