mud/tests/test_help_unlock.py
Jared Miller edbad4666f
Rework combat state machine
PENDING phase, defense active/recovery windows
2026-02-16 12:17:34 -05:00

152 lines
4.8 KiB
Python

"""Tests for help command showing unlock status for combat moves."""
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from mudlib.combat.moves import CombatMove, UnlockCondition
from mudlib.commands import dispatch, help # noqa: F401
from mudlib.player import Player, players
@pytest.fixture(autouse=True)
def clear_state():
"""Clear players before and after each test."""
players.clear()
yield
players.clear()
@pytest.fixture
def mock_writer():
writer = MagicMock()
writer.write = MagicMock()
writer.drain = AsyncMock()
return writer
@pytest.fixture
def player(mock_writer):
return Player(name="Test", writer=mock_writer)
@pytest.fixture
def mock_move_kill_count():
"""Mock move with kill_count unlock condition."""
return CombatMove(
name="roundhouse",
move_type="attack",
stamina_cost=30.0,
hit_time_ms=850,
aliases=["rh"],
description="A powerful spinning kick",
damage_pct=0.35,
unlock_condition=UnlockCondition(type="kill_count", threshold=5),
)
@pytest.fixture
def mock_move_mob_kills():
"""Mock move with mob_kills unlock condition."""
return CombatMove(
name="goblin slayer",
move_type="attack",
stamina_cost=25.0,
hit_time_ms=800,
description="Specialized technique against goblins",
damage_pct=0.40,
unlock_condition=UnlockCondition(
type="mob_kills", threshold=3, mob_name="goblin"
),
)
@pytest.fixture
def mock_move_no_unlock():
"""Mock move without unlock condition."""
return CombatMove(
name="jab",
move_type="attack",
stamina_cost=10.0,
hit_time_ms=600,
description="A quick straight punch",
damage_pct=0.15,
)
@pytest.mark.asyncio
async def test_help_locked_move_shows_kill_count_requirement(
player, mock_move_kill_count
):
"""Help for locked move shows kill count requirement."""
player.unlocked_moves = set()
moves = {"roundhouse": mock_move_kill_count, "rh": mock_move_kill_count}
with patch("mudlib.combat.commands.combat_moves", moves):
await dispatch(player, "help roundhouse")
output = "".join([call[0][0] for call in player.writer.write.call_args_list])
assert "LOCKED" in output or "Locked" in output
assert "5" in output
assert "enemies" in output.lower() or "kills" in output.lower()
@pytest.mark.asyncio
async def test_help_locked_move_shows_mob_kills_requirement(
player, mock_move_mob_kills
):
"""Help for locked move shows mob-specific kill requirement."""
player.unlocked_moves = set()
moves = {"goblin slayer": mock_move_mob_kills}
with patch("mudlib.combat.commands.combat_moves", moves):
await dispatch(player, "help goblin slayer")
output = "".join([call[0][0] for call in player.writer.write.call_args_list])
assert "LOCKED" in output or "Locked" in output
assert "3" in output
assert "goblin" in output.lower()
@pytest.mark.asyncio
async def test_help_unlocked_move_no_lock_notice(player, mock_move_kill_count):
"""Help for unlocked move shows normal help without lock notice."""
# Add move to unlocked_moves
player.unlocked_moves = {"roundhouse"}
moves = {"roundhouse": mock_move_kill_count, "rh": mock_move_kill_count}
with patch("mudlib.combat.commands.combat_moves", moves):
await dispatch(player, "help roundhouse")
output = "".join([call[0][0] for call in player.writer.write.call_args_list])
assert "LOCKED" not in output and "Locked" not in output
assert "roundhouse" in output
assert "A powerful spinning kick" in output
@pytest.mark.asyncio
async def test_help_move_without_unlock_condition(player, mock_move_no_unlock):
"""Help for move without unlock condition shows normal help."""
player.unlocked_moves = set()
with patch("mudlib.combat.commands.combat_moves", {"jab": mock_move_no_unlock}):
await dispatch(player, "help jab")
output = "".join([call[0][0] for call in player.writer.write.call_args_list])
assert "LOCKED" not in output and "Locked" not in output
assert "jab" in output
assert "A quick straight punch" in output
@pytest.mark.asyncio
async def test_help_locked_move_via_alias(player, mock_move_kill_count):
"""Help via alias for locked move shows unlock requirement."""
player.unlocked_moves = set()
moves = {"roundhouse": mock_move_kill_count, "rh": mock_move_kill_count}
with patch("mudlib.combat.commands.combat_moves", moves):
await dispatch(player, "help rh")
output = "".join([call[0][0] for call in player.writer.write.call_args_list])
assert "LOCKED" in output or "Locked" in output
assert "5" in output