207 lines
5.3 KiB
Python
207 lines
5.3 KiB
Python
"""Tests for look command target resolution."""
|
|
|
|
import pytest
|
|
|
|
from mudlib.commands.look import cmd_look
|
|
from mudlib.entity import Mob
|
|
from mudlib.player import Player
|
|
from mudlib.thing import Thing
|
|
from mudlib.zone import Zone
|
|
|
|
|
|
@pytest.fixture
|
|
def zone():
|
|
"""Create a test zone."""
|
|
terrain = [["." for _ in range(100)] for _ in range(100)]
|
|
return Zone(
|
|
name="test",
|
|
description="Test Zone",
|
|
width=100,
|
|
height=100,
|
|
toroidal=False,
|
|
terrain=terrain,
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_writer():
|
|
"""Create a mock writer that captures output."""
|
|
from unittest.mock import MagicMock
|
|
|
|
class MockWriter:
|
|
def __init__(self):
|
|
self.output = []
|
|
# Mock telnet options
|
|
self.local_option = MagicMock()
|
|
self.remote_option = MagicMock()
|
|
self.local_option.enabled = MagicMock(return_value=False)
|
|
self.remote_option.enabled = MagicMock(return_value=False)
|
|
|
|
def write(self, data):
|
|
self.output.append(data)
|
|
|
|
async def drain(self):
|
|
pass
|
|
|
|
def get_output(self):
|
|
return "".join(self.output)
|
|
|
|
return MockWriter()
|
|
|
|
|
|
@pytest.fixture
|
|
def player(zone, mock_writer):
|
|
"""Create a test player."""
|
|
p = Player(
|
|
name="TestPlayer",
|
|
location=zone,
|
|
x=50,
|
|
y=50,
|
|
writer=mock_writer,
|
|
)
|
|
return p
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_look_finds_mob_by_exact_name(player, zone, mock_writer):
|
|
"""look goblin finds mob with exact name."""
|
|
_mob = Mob(name="goblin", location=zone, x=50, y=50)
|
|
|
|
await cmd_look(player, "goblin")
|
|
|
|
output = mock_writer.get_output()
|
|
assert "goblin" in output.lower()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_look_finds_mob_by_prefix(player, zone, mock_writer):
|
|
"""look gob prefix matches goblin."""
|
|
_mob = Mob(name="goblin", location=zone, x=50, y=50)
|
|
|
|
await cmd_look(player, "gob")
|
|
|
|
output = mock_writer.get_output()
|
|
assert "goblin" in output.lower()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_look_finds_second_mob_with_ordinal(player, zone, mock_writer):
|
|
"""look 2.goblin finds second goblin."""
|
|
_mob1 = Mob(name="goblin", location=zone, x=50, y=50)
|
|
_mob2 = Mob(name="goblin", location=zone, x=50, y=50)
|
|
|
|
await cmd_look(player, "2.goblin")
|
|
|
|
output = mock_writer.get_output()
|
|
# Should find the second goblin
|
|
assert "goblin" in output.lower()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_look_finds_thing_on_ground(player, zone, mock_writer):
|
|
"""look sword finds thing on ground and shows description."""
|
|
_sword = Thing(
|
|
name="sword",
|
|
description="A sharp blade.",
|
|
location=zone,
|
|
x=50,
|
|
y=50,
|
|
)
|
|
|
|
await cmd_look(player, "sword")
|
|
|
|
output = mock_writer.get_output()
|
|
assert "A sharp blade." in output
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_look_shows_error_for_nonexistent(player, zone, mock_writer):
|
|
"""look nonexistent shows 'You don't see that here.'"""
|
|
await cmd_look(player, "nonexistent")
|
|
|
|
output = mock_writer.get_output()
|
|
assert "You don't see that here." in output
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_look_prioritizes_entity_over_thing(player, zone, mock_writer):
|
|
"""look target finds entity before thing with same name."""
|
|
_mob = Mob(name="target", location=zone, x=50, y=50)
|
|
_thing = Thing(
|
|
name="target",
|
|
description="A thing.",
|
|
location=zone,
|
|
x=50,
|
|
y=50,
|
|
)
|
|
|
|
await cmd_look(player, "target")
|
|
|
|
output = mock_writer.get_output()
|
|
# Should show entity info (name/posture), not thing description
|
|
assert "target" in output.lower()
|
|
# Should not show thing description
|
|
assert "A thing." not in output
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_look_skips_dead_mobs(player, zone, mock_writer):
|
|
"""look goblin skips dead mobs."""
|
|
_mob = Mob(name="goblin", location=zone, x=50, y=50)
|
|
_mob.alive = False
|
|
|
|
await cmd_look(player, "goblin")
|
|
|
|
output = mock_writer.get_output()
|
|
assert "You don't see that here." in output
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_look_skips_self(player, zone, mock_writer):
|
|
"""look TestPlayer doesn't target the player themselves."""
|
|
await cmd_look(player, "TestPlayer")
|
|
|
|
output = mock_writer.get_output()
|
|
assert "You don't see that here." in output
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_look_finds_thing_in_inventory(player, zone, mock_writer):
|
|
"""look sword finds thing in inventory when not on ground."""
|
|
_sword = Thing(
|
|
name="sword",
|
|
description="A sharp blade.",
|
|
location=player,
|
|
)
|
|
|
|
await cmd_look(player, "sword")
|
|
|
|
output = mock_writer.get_output()
|
|
assert "A sharp blade." in output
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_look_finds_second_thing_with_ordinal(player, zone, mock_writer):
|
|
"""look 2.sword finds second sword on ground."""
|
|
_sword1 = Thing(
|
|
name="sword",
|
|
description="A rusty blade.",
|
|
location=zone,
|
|
x=50,
|
|
y=50,
|
|
)
|
|
_sword2 = Thing(
|
|
name="sword",
|
|
description="A sharp blade.",
|
|
location=zone,
|
|
x=50,
|
|
y=50,
|
|
)
|
|
|
|
await cmd_look(player, "2.sword")
|
|
|
|
output = mock_writer.get_output()
|
|
# Should show the second sword's description
|
|
assert "A sharp blade." in output
|
|
# Should not show the first sword's description
|
|
assert "A rusty blade." not in output
|