mud/tests/test_if_spectator.py
Jared Miller c3f8c8cf12
Add spectator broadcasting tests for IF mode
Tests verify:
- Spectators at same location see IF output with player name header
- Spectators at different locations see nothing
- Game start intro broadcasts to spectators
- broadcast_to_spectators skips the playing player
- Multiple spectators all receive messages

Tests currently fail as broadcast_to_spectators not yet implemented.
2026-02-09 16:24:07 -05:00

195 lines
5.9 KiB
Python

"""Tests for spectator broadcasting in IF mode."""
from unittest.mock import AsyncMock, MagicMock
import pytest
from mudlib.if_session import IFResponse, IFSession
from mudlib.player import Player, players
@pytest.fixture
def mock_writer():
writer = MagicMock()
writer.write = MagicMock()
writer.drain = AsyncMock()
return writer
@pytest.fixture
def mock_reader():
return MagicMock()
@pytest.fixture(autouse=True)
def clear_players():
"""Clear players registry before and after each test."""
players.clear()
yield
players.clear()
@pytest.fixture
def player_a(mock_reader, mock_writer):
"""Player A at (5, 5) who will be playing IF."""
return Player(
name="PlayerA", x=5, y=5, reader=mock_reader, writer=mock_writer
)
@pytest.fixture
def player_b(mock_reader, mock_writer):
"""Player B at (5, 5) who will be spectating."""
return Player(
name="PlayerB", x=5, y=5, reader=mock_reader, writer=mock_writer
)
@pytest.fixture
def player_c(mock_reader, mock_writer):
"""Player C at different coords (10, 10)."""
return Player(
name="PlayerC", x=10, y=10, reader=mock_reader, writer=mock_writer
)
@pytest.mark.asyncio
async def test_spectator_sees_if_output(player_a, player_b):
"""Spectator at same location sees IF output with header and input."""
# Register both players
players[player_a.name] = player_a
players[player_b.name] = player_b
# Create mock IF session for player A
mock_session = MagicMock()
mock_session.handle_input = AsyncMock(
return_value=IFResponse(
output="Opening the small mailbox reveals a leaflet.", done=False
)
)
player_a.if_session = mock_session
player_a.mode_stack.append("if")
# Import the broadcast function (will be created in implementation)
from mudlib.if_session import broadcast_to_spectators
# Player A sends input
command = "open mailbox"
response = await player_a.if_session.handle_input(command)
await player_a.send(response.output)
# Broadcast to spectators
spectator_msg = (
f"[{player_a.name}'s terminal]\r\n"
f"> {command}\r\n"
f"{response.output}"
)
await broadcast_to_spectators(player_a, spectator_msg)
# Player B should have received the message
assert player_b.writer.write.called
calls = player_b.writer.write.call_args_list
sent_text = "".join(call[0][0] for call in calls)
assert "[PlayerA's terminal]" in sent_text
assert "> open mailbox" in sent_text
assert "Opening the small mailbox reveals a leaflet." in sent_text
@pytest.mark.asyncio
async def test_spectator_not_on_same_tile_sees_nothing(player_a, player_c):
"""Spectator at different location does not see IF output."""
# Register both players (player_c is at different coords)
players[player_a.name] = player_a
players[player_c.name] = player_c
# Create mock IF session for player A
mock_session = MagicMock()
mock_session.handle_input = AsyncMock(
return_value=IFResponse(output="You open the door.", done=False)
)
player_a.if_session = mock_session
player_a.mode_stack.append("if")
from mudlib.if_session import broadcast_to_spectators
# Player A sends input
command = "open door"
response = await player_a.if_session.handle_input(command)
await player_a.send(response.output)
# Broadcast to spectators
spectator_msg = (
f"[{player_a.name}'s terminal]\r\n"
f"> {command}\r\n"
f"{response.output}"
)
await broadcast_to_spectators(player_a, spectator_msg)
# Player C should NOT have received anything
assert not player_c.writer.write.called
@pytest.mark.asyncio
async def test_spectator_sees_game_start(player_a, player_b):
"""Spectator sees formatted intro text when player starts game."""
# Register both players
players[player_a.name] = player_a
players[player_b.name] = player_b
from mudlib.if_session import broadcast_to_spectators
# Simulate game start with intro text
intro = "ZORK I: The Great Underground Empire\nCopyright (c) 1981"
spectator_msg = f"[{player_a.name}'s terminal]\r\n{intro}\r\n"
await broadcast_to_spectators(player_a, spectator_msg)
# Player B should see the intro
assert player_b.writer.write.called
calls = player_b.writer.write.call_args_list
sent_text = "".join(call[0][0] for call in calls)
assert "[PlayerA's terminal]" in sent_text
assert "ZORK I: The Great Underground Empire" in sent_text
@pytest.mark.asyncio
async def test_broadcast_to_spectators_skips_self(player_a, player_b):
"""broadcast_to_spectators does not send to the playing player."""
# Register both players
players[player_a.name] = player_a
players[player_b.name] = player_b
from mudlib.if_session import broadcast_to_spectators
message = "[PlayerA's terminal]\r\n> look\r\nYou see a room.\r\n"
await broadcast_to_spectators(player_a, message)
# Player A should NOT have received the message
assert not player_a.writer.write.called
# Player B should have received it
assert player_b.writer.write.called
@pytest.mark.asyncio
async def test_multiple_spectators(player_a, player_b, mock_reader, mock_writer):
"""Multiple spectators at same location all see IF output."""
# Create a third player at same location
player_d = Player(
name="PlayerD", x=5, y=5, reader=mock_reader, writer=mock_writer
)
# Register all players
players[player_a.name] = player_a
players[player_b.name] = player_b
players[player_d.name] = player_d
from mudlib.if_session import broadcast_to_spectators
message = "[PlayerA's terminal]\r\n> inventory\r\nYou are empty-handed.\r\n"
await broadcast_to_spectators(player_a, message)
# Both spectators should see the message
assert player_b.writer.write.called
assert player_d.writer.write.called
# Player A should not
assert not player_a.writer.write.called