121 lines
3.7 KiB
Python
121 lines
3.7 KiB
Python
"""Tests for IF mode integration with player model and server shell."""
|
|
|
|
from unittest.mock import AsyncMock, MagicMock
|
|
|
|
import pytest
|
|
|
|
from mudlib.if_session import IFResponse, IFSession
|
|
from mudlib.player import Player
|
|
|
|
|
|
@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 player(mock_reader, mock_writer):
|
|
return Player(name="TestPlayer", x=5, y=5, reader=mock_reader, writer=mock_writer)
|
|
|
|
|
|
def test_player_has_if_session_attribute(player):
|
|
"""Player has if_session attribute that defaults to None."""
|
|
assert hasattr(player, "if_session")
|
|
assert player.if_session is None
|
|
|
|
|
|
def test_player_if_session_can_be_set(player):
|
|
"""Player.if_session can be set to an IFSession instance."""
|
|
session = IFSession(player, "/path/to/story.z5", "story")
|
|
player.if_session = session
|
|
assert player.if_session == session
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_if_mode_routes_input_to_if_session(player):
|
|
"""When mode is 'if' and if_session is set, input routes to if_session.handle_input."""
|
|
# Create a mock IF session
|
|
mock_session = MagicMock()
|
|
mock_session.handle_input = AsyncMock(
|
|
return_value=IFResponse(output="You see a room.\r\n", done=False)
|
|
)
|
|
|
|
player.if_session = mock_session
|
|
player.mode_stack.append("if")
|
|
|
|
# Simulate input routing (what shell loop should do)
|
|
if player.mode == "if" and player.if_session:
|
|
response = await player.if_session.handle_input("look")
|
|
assert response.output == "You see a room.\r\n"
|
|
assert response.done is False
|
|
|
|
mock_session.handle_input.assert_called_once_with("look")
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_if_mode_done_clears_session_and_pops_mode(player):
|
|
"""When handle_input returns done=True, mode pops and if_session is cleared."""
|
|
# Create a mock IF session that returns done=True
|
|
mock_session = MagicMock()
|
|
mock_session.handle_input = AsyncMock(
|
|
return_value=IFResponse(output="Goodbye.\r\n", done=True)
|
|
)
|
|
mock_session.stop = AsyncMock()
|
|
|
|
player.if_session = mock_session
|
|
player.mode_stack.append("if")
|
|
|
|
# Simulate shell loop handling done response
|
|
if player.mode == "if" and player.if_session:
|
|
response = await player.if_session.handle_input("::quit")
|
|
if response.done:
|
|
await player.if_session.stop()
|
|
player.if_session = None
|
|
player.mode_stack.pop()
|
|
|
|
assert player.mode == "normal"
|
|
assert player.if_session is None
|
|
mock_session.stop.assert_called_once()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_mode_stack_push_and_pop_for_if(player):
|
|
"""Test mode stack mechanics for IF mode."""
|
|
assert player.mode_stack == ["normal"]
|
|
assert player.mode == "normal"
|
|
|
|
# Enter IF mode
|
|
player.mode_stack.append("if")
|
|
assert player.mode == "if"
|
|
assert player.mode_stack == ["normal", "if"]
|
|
|
|
# Exit IF mode
|
|
player.mode_stack.pop()
|
|
assert player.mode == "normal"
|
|
assert player.mode_stack == ["normal"]
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_empty_input_allowed_in_if_mode(player):
|
|
"""Test that empty input is allowed in IF mode (some IF games accept blank input)."""
|
|
# Create a mock IF session
|
|
mock_session = MagicMock()
|
|
mock_session.handle_input = AsyncMock(
|
|
return_value=IFResponse(output="Time passes.\r\n", done=False)
|
|
)
|
|
|
|
player.if_session = mock_session
|
|
player.mode_stack.append("if")
|
|
|
|
# Empty input should still be passed to IF session
|
|
response = await player.if_session.handle_input("")
|
|
assert response.output == "Time passes.\r\n"
|
|
mock_session.handle_input.assert_called_once_with("")
|