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().
204 lines
6.2 KiB
Python
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()
|