mud/tests/test_use.py
Jared Miller d2de6bdc16
Add use command for verb-based interaction
Implements a TDD-built 'use' command that lets players invoke
object verbs with optional targets:
- use X - calls X's use verb
- use X on Y - calls X's use verb with Y as args
- Proper error messages for missing objects/verbs
- Tests cover all edge cases including inventory/ground search

Also fixes type checking issue in verb dispatch where get_verb
could return None.
2026-02-11 21:47:33 -05:00

180 lines
4.8 KiB
Python

from unittest.mock import AsyncMock, MagicMock
import pytest
from mudlib.commands.use import cmd_use
from mudlib.player import Player
from mudlib.thing import Thing
from mudlib.verbs import verb
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():
terrain = [["." for _ in range(10)] for _ in range(10)]
return Zone(name="testzone", width=10, height=10, terrain=terrain)
@pytest.fixture
def player(mock_reader, mock_writer, test_zone):
return Player(
name="TestPlayer",
x=5,
y=5,
reader=mock_reader,
writer=mock_writer,
location=test_zone,
)
def get_output(mock_writer):
"""Extract all text written to the mock writer."""
return "".join(call[0][0] for call in mock_writer.write.call_args_list)
@pytest.mark.asyncio
async def test_use_no_args(player, mock_writer):
"""use with no args should prompt for what to use"""
await cmd_use(player, "")
output = get_output(mock_writer)
assert "Use what?" in output
@pytest.mark.asyncio
async def test_use_object_with_verb(player, mock_writer, test_zone):
"""use rock should find rock with use verb and call it"""
class Rock(Thing):
@verb("use")
async def use_verb(self, player, args):
await player.send("You use the rock.\r\n")
_rock = Rock(
name="rock", description="a smooth rock", portable=True, location=test_zone
)
_rock.x = 5
_rock.y = 5
await cmd_use(player, "rock")
output = get_output(mock_writer)
assert "You use the rock." in output
@pytest.mark.asyncio
async def test_use_object_without_verb(player, mock_writer, test_zone):
"""use rock when rock has no use verb should give error"""
_rock = Thing(
name="rock", description="a smooth rock", portable=True, location=test_zone
)
_rock.x = 5
_rock.y = 5
await cmd_use(player, "rock")
output = get_output(mock_writer)
assert "You can't use that." in output
@pytest.mark.asyncio
async def test_use_object_not_found(player, mock_writer):
"""use flurb when nothing matches should give error"""
await cmd_use(player, "flurb")
output = get_output(mock_writer)
assert "You don't see that here." in output
@pytest.mark.asyncio
async def test_use_object_on_target(player, mock_writer, test_zone):
"""use key on chest should pass chest as args to verb"""
class Key(Thing):
@verb("use")
async def use_verb(self, player, args):
await player.send(f"You use the key on {args}.\r\n")
_key = Key(name="key", description="a brass key", portable=True, location=test_zone)
_key.x = 5
_key.y = 5
await cmd_use(player, "key on chest")
output = get_output(mock_writer)
assert "You use the key on chest." in output
@pytest.mark.asyncio
async def test_use_object_on_nonexistent_target(player, mock_writer, test_zone):
"""use key on flurb should still work, passing flurb to verb handler"""
class Key(Thing):
@verb("use")
async def use_verb(self, player, args):
await player.send(f"You use the key on {args}.\r\n")
_key = Key(name="key", description="a brass key", portable=True, location=test_zone)
_key.x = 5
_key.y = 5
await cmd_use(player, "key on flurb")
output = get_output(mock_writer)
assert "You use the key on flurb." in output
@pytest.mark.asyncio
async def test_use_object_in_inventory(player, mock_writer):
"""use should find objects in inventory"""
class Potion(Thing):
@verb("use")
async def use_verb(self, player, args):
await player.send("You drink the potion.\r\n")
_potion = Potion(
name="potion", description="a healing potion", portable=True, location=player
)
await cmd_use(player, "potion")
output = get_output(mock_writer)
assert "You drink the potion." in output
@pytest.mark.asyncio
async def test_use_passes_correct_args(player, mock_writer, test_zone):
"""verify verb handler receives correct args string"""
received_args = None
class Tool(Thing):
@verb("use")
async def use_verb(self, player, args):
nonlocal received_args
received_args = args
await player.send(f"Used with args: '{args}'\r\n")
_tool = Tool(
name="tool", description="a multi-tool", portable=True, location=test_zone
)
_tool.x = 5
_tool.y = 5
# Test with no target
await cmd_use(player, "tool")
assert received_args == ""
# Reset writer
mock_writer.write.reset_mock()
received_args = None
# Test with target
await cmd_use(player, "tool on something")
assert received_args == "something"