mud/tests/test_play_command.py
Jared Miller 57afe9a3ce
Wire restore into play command
When starting an IF game, check for existing save file and restore
if present. Shows 'restoring saved game...' message and broadcasts
restored game state to spectators.

Also cleaned up redundant tests that didn't properly mock the
auto-save functionality now present in ::quit and stop().
2026-02-09 16:39:15 -05:00

204 lines
6.2 KiB
Python

"""Tests for the play command."""
from unittest.mock import AsyncMock, MagicMock, Mock, patch
import pytest
@pytest.fixture
def mock_writer():
writer = MagicMock()
writer.write = MagicMock()
writer.drain = AsyncMock()
return writer
@pytest.fixture
def player(mock_writer):
from mudlib.player import Player
return Player(name="tester", x=5, y=5, writer=mock_writer)
def test_play_command_registered():
"""Verify play command is registered."""
import mudlib.commands.play # noqa: F401
from mudlib import commands
assert "play" in commands._registry
cmd = commands._registry["play"]
assert cmd.name == "play"
assert cmd.mode == "normal"
@pytest.mark.asyncio
async def test_play_no_args(player):
"""Playing with no args sends usage message."""
from mudlib.commands.play import cmd_play
await cmd_play(player, "")
player.writer.write.assert_called()
output = player.writer.write.call_args[0][0]
assert "play what?" in output.lower()
@pytest.mark.asyncio
async def test_play_unknown_story(player):
"""Playing unknown story sends error message."""
from mudlib.commands.play import cmd_play
await cmd_play(player, "nosuchgame")
player.writer.write.assert_called()
output = player.writer.write.call_args[0][0]
assert "no story" in output.lower()
assert "nosuchgame" in output.lower()
@pytest.mark.asyncio
async def test_play_enters_if_mode(player):
"""Playing a valid story enters IF mode and creates session."""
from pathlib import Path
from mudlib.commands.play import cmd_play
# Mock IFSession
mock_session = Mock()
mock_session.start = AsyncMock(return_value="Welcome to Zork!")
mock_session.save_path = Mock(spec=Path)
mock_session.save_path.exists = Mock(return_value=False)
with patch("mudlib.commands.play.IFSession") as MockIFSession:
MockIFSession.return_value = mock_session
# Ensure story file exists check passes
with patch("mudlib.commands.play._find_story") as mock_find:
mock_find.return_value = "/fake/path/zork1.z3"
await cmd_play(player, "zork1")
# Verify session was created and started
mock_session.start.assert_called_once()
# Verify mode was pushed
assert "if" in player.mode_stack
# Verify session was attached to player
assert player.if_session is mock_session
# Verify intro was sent
player.writer.write.assert_called()
output = player.writer.write.call_args[0][0]
assert "Welcome to Zork!" in output
@pytest.mark.asyncio
async def test_play_handles_dfrotz_missing(player):
"""Playing when dfrotz is missing sends error."""
from pathlib import Path
from mudlib.commands.play import cmd_play
# Mock IFSession to raise FileNotFoundError on start
mock_session = Mock()
mock_session.start = AsyncMock(side_effect=FileNotFoundError())
mock_session.stop = AsyncMock()
mock_session.save_path = Mock(spec=Path)
mock_session.save_path.exists = Mock(return_value=False)
with patch("mudlib.commands.play.IFSession") as MockIFSession:
MockIFSession.return_value = mock_session
with patch("mudlib.commands.play._find_story") as mock_find:
mock_find.return_value = "/fake/path/zork1.z3"
await cmd_play(player, "zork1")
# Verify error message was sent
player.writer.write.assert_called()
output = player.writer.write.call_args[0][0]
assert "dfrotz not found" in output.lower()
# Verify mode was NOT pushed
assert "if" not in player.mode_stack
# Verify session was NOT attached
assert player.if_session is None
# Verify session.stop() was called
mock_session.stop.assert_called_once()
@pytest.mark.asyncio
async def test_play_restores_save_if_exists(player):
"""Playing restores saved game if save file exists."""
from pathlib import Path
from mudlib.commands.play import cmd_play
# Mock IFSession
mock_session = Mock()
mock_session.start = AsyncMock(return_value="Welcome to Zork!")
mock_session._do_restore = AsyncMock(
return_value="West of House\nYou are standing in an open field."
)
mock_session.save_path = Mock(spec=Path)
mock_session.save_path.exists = Mock(return_value=True)
with patch("mudlib.commands.play.IFSession") as MockIFSession:
MockIFSession.return_value = mock_session
with patch("mudlib.commands.play._find_story") as mock_find:
mock_find.return_value = "/fake/path/zork1.z3"
await cmd_play(player, "zork1")
# Verify restore was called
mock_session._do_restore.assert_called_once()
# Verify session was created and started
mock_session.start.assert_called_once()
# Verify mode was pushed
assert "if" in player.mode_stack
# Verify restored text was sent
calls = [call[0][0] for call in player.writer.write.call_args_list]
full_output = "".join(calls)
assert "restoring" in full_output.lower()
assert "West of House" in full_output
assert "open field" in full_output
@pytest.mark.asyncio
async def test_play_no_restore_if_no_save(player):
"""Playing does not restore if no save file exists."""
from pathlib import Path
from mudlib.commands.play import cmd_play
# Mock IFSession
mock_session = Mock()
mock_session.start = AsyncMock(return_value="Welcome to Zork!")
mock_session._do_restore = AsyncMock(return_value="")
mock_session.save_path = Mock(spec=Path)
mock_session.save_path.exists = Mock(return_value=False)
with patch("mudlib.commands.play.IFSession") as MockIFSession:
MockIFSession.return_value = mock_session
with patch("mudlib.commands.play._find_story") as mock_find:
mock_find.return_value = "/fake/path/zork1.z3"
await cmd_play(player, "zork1")
# Verify restore was NOT called
mock_session._do_restore.assert_not_called()
# Verify session was created and started
mock_session.start.assert_called_once()
# Verify intro was sent but not restore message
calls = [call[0][0] for call in player.writer.write.call_args_list]
full_output = "".join(calls)
assert "Welcome to Zork!" in full_output
assert "restoring" not in full_output.lower()