216 lines
6.2 KiB
Python
216 lines
6.2 KiB
Python
"""Tests for sleep command."""
|
|
|
|
from unittest.mock import AsyncMock, MagicMock
|
|
|
|
import pytest
|
|
|
|
from mudlib.commands.sleep import cmd_sleep
|
|
from mudlib.player import Player, players
|
|
from mudlib.resting import STAMINA_PER_TICK, process_resting
|
|
from mudlib.zone import Zone
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def clear_state():
|
|
"""Clear players before and after each test."""
|
|
players.clear()
|
|
yield
|
|
players.clear()
|
|
|
|
|
|
@pytest.fixture
|
|
def test_zone():
|
|
"""Create a test zone for players."""
|
|
terrain = [["." for _ in range(256)] for _ in range(256)]
|
|
zone = Zone(
|
|
name="testzone",
|
|
width=256,
|
|
height=256,
|
|
toroidal=True,
|
|
terrain=terrain,
|
|
impassable=set(),
|
|
)
|
|
return zone
|
|
|
|
|
|
@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, test_zone):
|
|
p = Player(name="Goku", x=0, y=0, reader=mock_reader, writer=mock_writer)
|
|
p.location = test_zone
|
|
test_zone._contents.append(p)
|
|
players[p.name] = p
|
|
return p
|
|
|
|
|
|
@pytest.fixture
|
|
def nearby_player(mock_reader, mock_writer, test_zone):
|
|
p = Player(name="Vegeta", x=0, y=0, reader=mock_reader, writer=mock_writer)
|
|
p.location = test_zone
|
|
test_zone._contents.append(p)
|
|
players[p.name] = p
|
|
return p
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_sleep_when_full_sends_not_tired_message(player):
|
|
"""Test sleeping when at full stamina sends 'not tired' message."""
|
|
player.stamina = 100.0
|
|
player.max_stamina = 100.0
|
|
|
|
await cmd_sleep(player, "")
|
|
|
|
player.writer.write.assert_called_once_with("You're not tired.\r\n")
|
|
assert player.stamina == 100.0
|
|
assert not player.sleeping
|
|
assert not player.resting
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_sleep_when_not_sleeping_starts_sleeping(player):
|
|
"""Test sleep command when not sleeping starts the sleeping state."""
|
|
player.stamina = 50.0
|
|
player.max_stamina = 100.0
|
|
player.sleeping = False
|
|
player.resting = False
|
|
|
|
await cmd_sleep(player, "")
|
|
|
|
assert player.sleeping
|
|
assert player.resting # sleeping implies resting
|
|
messages = [call[0][0] for call in player.writer.write.call_args_list]
|
|
assert any("fall asleep" in msg or "go to sleep" in msg for msg in messages)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_sleep_when_already_sleeping_wakes_up(player):
|
|
"""Test sleep command when already sleeping wakes the player."""
|
|
player.stamina = 50.0
|
|
player.max_stamina = 100.0
|
|
player.sleeping = True
|
|
player.resting = True
|
|
|
|
await cmd_sleep(player, "")
|
|
|
|
assert not player.sleeping
|
|
assert not player.resting
|
|
messages = [call[0][0] for call in player.writer.write.call_args_list]
|
|
assert any("wake up" in msg or "wake" in msg for msg in messages)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_sleep_blocked_during_combat(player):
|
|
"""Test sleep is blocked when player is in combat mode."""
|
|
player.stamina = 50.0
|
|
player.mode_stack.append("combat") # Push combat mode onto the stack
|
|
|
|
await cmd_sleep(player, "")
|
|
|
|
assert not player.sleeping
|
|
messages = [call[0][0] for call in player.writer.write.call_args_list]
|
|
assert any(
|
|
"can't sleep" in msg.lower() or "combat" in msg.lower() for msg in messages
|
|
)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_sleep_broadcasts_to_nearby_players(player, nearby_player):
|
|
"""Test sleeping broadcasts message to nearby players."""
|
|
player.stamina = 50.0
|
|
player.sleeping = False
|
|
|
|
await cmd_sleep(player, "")
|
|
|
|
nearby_messages = [call[0][0] for call in nearby_player.writer.write.call_args_list]
|
|
assert any(
|
|
"Goku" in msg and ("asleep" in msg or "sleep" in msg) for msg in nearby_messages
|
|
)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_wake_broadcasts_to_nearby_players(player, nearby_player):
|
|
"""Test waking up broadcasts message to nearby players."""
|
|
player.stamina = 50.0
|
|
player.sleeping = True
|
|
player.resting = True
|
|
|
|
await cmd_sleep(player, "")
|
|
|
|
nearby_messages = [call[0][0] for call in nearby_player.writer.write.call_args_list]
|
|
assert any("Goku" in msg and "wake" in msg for msg in nearby_messages)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_sleep_stamina_recovery_faster_than_rest(player):
|
|
"""Test sleeping provides faster stamina recovery than resting."""
|
|
player.stamina = 50.0
|
|
player.max_stamina = 100.0
|
|
player.sleeping = True
|
|
player.resting = True
|
|
|
|
await process_resting()
|
|
|
|
# Sleep should give 3x the base rate (0.2 * 3 = 0.6)
|
|
expected = 50.0 + (STAMINA_PER_TICK * 3)
|
|
assert player.stamina == expected
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_sleep_auto_stops_when_full(player):
|
|
"""Test sleep auto-stops when stamina reaches max."""
|
|
player.stamina = 99.5
|
|
player.max_stamina = 100.0
|
|
player.sleeping = True
|
|
player.resting = True
|
|
|
|
await process_resting()
|
|
|
|
assert player.stamina == 100.0
|
|
assert not player.sleeping
|
|
assert not player.resting
|
|
messages = [call[0][0] for call in player.writer.write.call_args_list]
|
|
assert any("fully rested" in msg or "wake" in msg for msg in messages)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_sleeping_player_does_not_see_nearby_messages(player, nearby_player):
|
|
"""Test sleeping players don't receive nearby messages."""
|
|
from mudlib.commands.movement import send_nearby_message
|
|
|
|
player.sleeping = True
|
|
|
|
# Nearby player does something that would normally broadcast
|
|
await send_nearby_message(
|
|
nearby_player, nearby_player.x, nearby_player.y, "Test message.\r\n"
|
|
)
|
|
|
|
# Sleeping player should not have received it
|
|
messages = [call[0][0] for call in player.writer.write.call_args_list]
|
|
assert len(messages) == 0
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_awake_player_sees_nearby_messages(player, nearby_player):
|
|
"""Test awake players receive nearby messages normally."""
|
|
from mudlib.commands.movement import send_nearby_message
|
|
|
|
player.sleeping = False
|
|
|
|
await send_nearby_message(
|
|
nearby_player, nearby_player.x, nearby_player.y, "Test message.\r\n"
|
|
)
|
|
|
|
messages = [call[0][0] for call in player.writer.write.call_args_list]
|
|
assert any("Test message" in msg for msg in messages)
|