- Add Where: header with zone description
- Add Location: line with quadrant and coordinates
- Add Nearby: line showing entities in viewport (not on player's tile)
- Add Exits: line showing available cardinal directions
- Replace 'Here:' with individual entity lines showing posture
- Replace 'Portals:' with individual 'You see {name}.' lines
- Add look <thing> routing to examine command
- Add comprehensive tests for new structured output
- Update existing tests to match new output format
205 lines
5.7 KiB
Python
205 lines
5.7 KiB
Python
"""Tests for the look command with structured room display."""
|
|
|
|
from unittest.mock import AsyncMock, MagicMock
|
|
|
|
import pytest
|
|
|
|
from mudlib.commands import look # noqa: F401
|
|
from mudlib.commands.look import cmd_look
|
|
from mudlib.entity import Mob
|
|
from mudlib.player import Player
|
|
from mudlib.portal import Portal
|
|
from mudlib.thing import Thing
|
|
from mudlib.zone import Zone
|
|
|
|
|
|
@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 test_zone():
|
|
"""Create a test zone with simple terrain."""
|
|
# 50x50 zone with grass everywhere
|
|
terrain = [["." for _ in range(50)] for _ in range(50)]
|
|
# Add some mountains
|
|
terrain[10][10] = "^"
|
|
terrain[10][11] = "^"
|
|
return Zone(
|
|
name="test_zone",
|
|
description="The Test Zone",
|
|
width=50,
|
|
height=50,
|
|
terrain=terrain,
|
|
impassable={"^", "~"},
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def player(mock_reader, mock_writer, test_zone):
|
|
"""Create a test player in the test zone."""
|
|
p = Player(
|
|
name="TestPlayer",
|
|
x=25,
|
|
y=25,
|
|
reader=mock_reader,
|
|
writer=mock_writer,
|
|
)
|
|
p.location = test_zone
|
|
test_zone._contents.append(p)
|
|
return p
|
|
|
|
|
|
def get_output(player):
|
|
"""Get all output written to player's writer."""
|
|
return "".join([call[0][0] for call in player.writer.write.call_args_list])
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_look_includes_where_header(player):
|
|
"""Look output should include 'Where: {zone description}' line."""
|
|
await cmd_look(player, "")
|
|
output = get_output(player)
|
|
assert "Where: The Test Zone" in output
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_look_includes_location_line(player):
|
|
"""Look output should include 'Location:' line with quadrant and coords."""
|
|
await cmd_look(player, "")
|
|
output = get_output(player)
|
|
assert "Location: center 25, 25" in output
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_look_includes_exits_line(player):
|
|
"""Look output should include 'Exits:' line."""
|
|
await cmd_look(player, "")
|
|
output = get_output(player)
|
|
assert "Exits: north south east west" in output
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_look_shows_nearby_entities(player, test_zone):
|
|
"""Look should show nearby entities (not on player's tile) in viewport."""
|
|
# Add some mobs in viewport range (location param auto-adds to zone)
|
|
Mob(name="Goku", x=26, y=25, location=test_zone)
|
|
Mob(name="Vegeta", x=27, y=26, location=test_zone)
|
|
|
|
await cmd_look(player, "")
|
|
output = get_output(player)
|
|
|
|
# Should see nearby line with count
|
|
assert "Nearby: (2)" in output
|
|
assert "Goku" in output
|
|
assert "Vegeta" in output
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_look_shows_entities_here(player, test_zone):
|
|
"""Look should show entities on player's tile with posture."""
|
|
# Add entities on player's tile (location param auto-adds to zone)
|
|
Mob(name="Krillin", x=25, y=25, resting=True, location=test_zone)
|
|
Mob(name="Piccolo", x=25, y=25, location=test_zone)
|
|
|
|
await cmd_look(player, "")
|
|
output = get_output(player)
|
|
|
|
# Should see individual lines with postures
|
|
assert "Krillin is resting here." in output
|
|
assert "Piccolo is standing here." in output
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_look_shows_ground_items(player, test_zone):
|
|
"""Look should show items on the ground."""
|
|
# Add an item on player's tile (location param auto-adds to zone)
|
|
Thing(name="rusty sword", x=25, y=25, location=test_zone)
|
|
|
|
await cmd_look(player, "")
|
|
output = get_output(player)
|
|
|
|
# Should see ground items
|
|
assert "rusty sword" in output
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_look_shows_portals(player, test_zone):
|
|
"""Look should show portals on player's tile."""
|
|
# Add a portal on player's tile (location param auto-adds to zone)
|
|
Portal(
|
|
name="wide dirt path",
|
|
x=25,
|
|
y=25,
|
|
location=test_zone,
|
|
target_zone="other",
|
|
target_x=0,
|
|
target_y=0,
|
|
)
|
|
|
|
await cmd_look(player, "")
|
|
output = get_output(player)
|
|
|
|
# Should see portal with new format
|
|
assert "You see wide dirt path." in output
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_look_with_args_routes_to_examine(player, test_zone):
|
|
"""look <thing> should route to examine command logic."""
|
|
# Add an item to examine (location param auto-adds to zone)
|
|
Thing(name="sword", x=25, y=25, description="A sharp blade.", location=test_zone)
|
|
|
|
await cmd_look(player, "sword")
|
|
output = get_output(player)
|
|
|
|
# Should see the item's description (examine behavior)
|
|
assert "A sharp blade." in output
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_look_structure_order(player, test_zone):
|
|
"""Look output should have sections in correct order."""
|
|
# Add entities and items (location param auto-adds to zone)
|
|
Mob(name="Goku", x=25, y=25, location=test_zone)
|
|
|
|
await cmd_look(player, "")
|
|
output = get_output(player)
|
|
|
|
# Find positions of key sections
|
|
where_pos = output.find("Where:")
|
|
location_pos = output.find("Location:")
|
|
exits_pos = output.find("Exits:")
|
|
|
|
# Verify order: Where comes first, then viewport (with terrain),
|
|
# then Location, then Exits
|
|
assert where_pos < location_pos
|
|
assert location_pos < exits_pos
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_look_nowhere(mock_reader, mock_writer):
|
|
"""Look should show 'You are nowhere.' when player has no location."""
|
|
# Create player without a location
|
|
player = Player(
|
|
name="NowherePlayer",
|
|
x=0,
|
|
y=0,
|
|
reader=mock_reader,
|
|
writer=mock_writer,
|
|
)
|
|
player.location = None
|
|
|
|
await cmd_look(player, "")
|
|
output = get_output(player)
|
|
|
|
assert "You are nowhere." in output
|