mud/tests/test_portal.py

249 lines
5.8 KiB
Python

"""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