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.
This commit is contained in:
parent
2ac2335b18
commit
c3f8c8cf12
1 changed files with 195 additions and 0 deletions
195
tests/test_if_spectator.py
Normal file
195
tests/test_if_spectator.py
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
"""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
|
||||
Loading…
Reference in a new issue