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.
195 lines
5.9 KiB
Python
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
|