Implements TDD feature for readable text on Things: - Added readable_text field to Thing dataclass - Extended ThingTemplate to parse readable_text from TOML - Created read command that finds objects by name/alias in inventory or on ground - Handles edge cases: no target, not found, not readable
286 lines
6.7 KiB
Python
286 lines
6.7 KiB
Python
"""Tests for readable objects."""
|
|
|
|
from unittest.mock import AsyncMock, MagicMock
|
|
|
|
import pytest
|
|
|
|
from mudlib.player import Player, players
|
|
from mudlib.thing import Thing
|
|
from mudlib.zone import Zone
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def clear_state():
|
|
players.clear()
|
|
yield
|
|
players.clear()
|
|
|
|
|
|
@pytest.fixture
|
|
def zone():
|
|
terrain = [["." for _ in range(10)] for _ in range(10)]
|
|
return Zone(
|
|
name="library",
|
|
width=10,
|
|
height=10,
|
|
terrain=terrain,
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_writer():
|
|
writer = MagicMock()
|
|
writer.write = MagicMock()
|
|
writer.drain = AsyncMock()
|
|
return writer
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_reader():
|
|
return MagicMock()
|
|
|
|
|
|
# --- Thing model ---
|
|
|
|
|
|
def test_thing_readable_text_default():
|
|
"""Thing has empty readable_text by default."""
|
|
t = Thing(name="rock")
|
|
assert t.readable_text == ""
|
|
|
|
|
|
def test_thing_readable_text_set():
|
|
"""Thing can have readable_text."""
|
|
t = Thing(name="sign", readable_text="welcome to town")
|
|
assert t.readable_text == "welcome to town"
|
|
|
|
|
|
# --- Template loading ---
|
|
|
|
|
|
def test_thing_template_readable_text():
|
|
"""ThingTemplate parses readable_text from TOML data."""
|
|
import pathlib
|
|
import tempfile
|
|
|
|
from mudlib.things import load_thing_template
|
|
|
|
with tempfile.NamedTemporaryFile(mode="w", suffix=".toml", delete=False) as f:
|
|
f.write("""
|
|
name = "old sign"
|
|
description = "a weathered wooden sign"
|
|
portable = false
|
|
readable_text = "beware of the goblin king"
|
|
""")
|
|
temp_path = pathlib.Path(f.name)
|
|
|
|
try:
|
|
template = load_thing_template(temp_path)
|
|
assert template.readable_text == "beware of the goblin king"
|
|
finally:
|
|
temp_path.unlink()
|
|
|
|
|
|
def test_thing_template_readable_text_default():
|
|
"""ThingTemplate defaults readable_text to empty string."""
|
|
import pathlib
|
|
import tempfile
|
|
|
|
from mudlib.things import load_thing_template
|
|
|
|
with tempfile.NamedTemporaryFile(mode="w", suffix=".toml", delete=False) as f:
|
|
f.write("""
|
|
name = "rock"
|
|
description = "a plain rock"
|
|
""")
|
|
temp_path = pathlib.Path(f.name)
|
|
|
|
try:
|
|
template = load_thing_template(temp_path)
|
|
assert template.readable_text == ""
|
|
finally:
|
|
temp_path.unlink()
|
|
|
|
|
|
def test_spawn_thing_readable_text():
|
|
"""spawn_thing passes readable_text to Thing instance."""
|
|
from mudlib.things import ThingTemplate, spawn_thing
|
|
|
|
template = ThingTemplate(
|
|
name="notice",
|
|
description="a posted notice",
|
|
readable_text="town meeting at noon",
|
|
)
|
|
thing = spawn_thing(template, location=None)
|
|
assert thing.readable_text == "town meeting at noon"
|
|
|
|
|
|
# --- Read command ---
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_read_thing_on_ground(zone, mock_writer, mock_reader):
|
|
"""Reading a thing on the ground shows its text."""
|
|
from mudlib.commands.read import cmd_read
|
|
|
|
player = Player(
|
|
name="reader",
|
|
x=5,
|
|
y=5,
|
|
writer=mock_writer,
|
|
reader=mock_reader,
|
|
location=zone,
|
|
)
|
|
zone._contents.append(player)
|
|
players["reader"] = player
|
|
|
|
Thing(
|
|
name="sign",
|
|
description="a wooden sign",
|
|
readable_text="welcome to the forest",
|
|
location=zone,
|
|
x=5,
|
|
y=5,
|
|
)
|
|
|
|
await cmd_read(player, "sign")
|
|
|
|
mock_writer.write.assert_called()
|
|
written = mock_writer.write.call_args_list[-1][0][0]
|
|
assert "welcome to the forest" in written
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_read_thing_in_inventory(zone, mock_writer, mock_reader):
|
|
"""Reading a thing in inventory shows its text."""
|
|
from mudlib.commands.read import cmd_read
|
|
|
|
player = Player(
|
|
name="reader",
|
|
x=5,
|
|
y=5,
|
|
writer=mock_writer,
|
|
reader=mock_reader,
|
|
location=zone,
|
|
)
|
|
zone._contents.append(player)
|
|
players["reader"] = player
|
|
|
|
Thing(
|
|
name="scroll",
|
|
description="an ancient scroll",
|
|
readable_text="the ancient prophecy speaks of...",
|
|
location=player,
|
|
)
|
|
|
|
await cmd_read(player, "scroll")
|
|
|
|
mock_writer.write.assert_called()
|
|
written = mock_writer.write.call_args_list[-1][0][0]
|
|
assert "the ancient prophecy speaks of" in written
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_read_no_target(zone, mock_writer, mock_reader):
|
|
"""Reading without a target shows error."""
|
|
from mudlib.commands.read import cmd_read
|
|
|
|
player = Player(
|
|
name="reader",
|
|
x=5,
|
|
y=5,
|
|
writer=mock_writer,
|
|
reader=mock_reader,
|
|
location=zone,
|
|
)
|
|
zone._contents.append(player)
|
|
|
|
await cmd_read(player, "")
|
|
|
|
mock_writer.write.assert_called()
|
|
written = mock_writer.write.call_args_list[0][0][0]
|
|
assert "read what" in written.lower()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_read_not_found(zone, mock_writer, mock_reader):
|
|
"""Reading something not present shows error."""
|
|
from mudlib.commands.read import cmd_read
|
|
|
|
player = Player(
|
|
name="reader",
|
|
x=5,
|
|
y=5,
|
|
writer=mock_writer,
|
|
reader=mock_reader,
|
|
location=zone,
|
|
)
|
|
zone._contents.append(player)
|
|
|
|
await cmd_read(player, "scroll")
|
|
|
|
mock_writer.write.assert_called()
|
|
written = mock_writer.write.call_args_list[0][0][0]
|
|
assert "don't see" in written.lower() or "nothing" in written.lower()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_read_not_readable(zone, mock_writer, mock_reader):
|
|
"""Reading a thing with no readable_text shows error."""
|
|
from mudlib.commands.read import cmd_read
|
|
|
|
player = Player(
|
|
name="reader",
|
|
x=5,
|
|
y=5,
|
|
writer=mock_writer,
|
|
reader=mock_reader,
|
|
location=zone,
|
|
)
|
|
zone._contents.append(player)
|
|
|
|
Thing(
|
|
name="rock",
|
|
description="a plain rock",
|
|
location=zone,
|
|
x=5,
|
|
y=5,
|
|
)
|
|
|
|
await cmd_read(player, "rock")
|
|
|
|
mock_writer.write.assert_called()
|
|
written = mock_writer.write.call_args_list[0][0][0]
|
|
assert "nothing to read" in written.lower()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_read_by_alias(zone, mock_writer, mock_reader):
|
|
"""Can read a thing by its alias."""
|
|
from mudlib.commands.read import cmd_read
|
|
|
|
player = Player(
|
|
name="reader",
|
|
x=5,
|
|
y=5,
|
|
writer=mock_writer,
|
|
reader=mock_reader,
|
|
location=zone,
|
|
)
|
|
zone._contents.append(player)
|
|
|
|
Thing(
|
|
name="leather-bound book",
|
|
aliases=["book", "tome"],
|
|
description="a leather-bound book",
|
|
readable_text="once upon a time...",
|
|
location=zone,
|
|
x=5,
|
|
y=5,
|
|
)
|
|
|
|
await cmd_read(player, "tome")
|
|
|
|
mock_writer.write.assert_called()
|
|
written = mock_writer.write.call_args_list[-1][0][0]
|
|
assert "once upon a time" in written
|