215 lines
7.1 KiB
Python
215 lines
7.1 KiB
Python
"""Tests for commander_ai_lab.sim.models."""
|
|
import pytest
|
|
from commander_ai_lab.sim.models import (
|
|
Card, Player, PlayerStats, PlayerResult, GameResult, SimState
|
|
)
|
|
|
|
|
|
# ── Card ────────────────────────────────────────────────────
|
|
|
|
class TestCardGetPower:
|
|
def test_from_power_field(self):
|
|
c = Card(name="Test", power="3", toughness="2", pt="3/2")
|
|
assert c.get_power() == 3
|
|
|
|
def test_from_pt_field(self):
|
|
c = Card(name="Test", pt="4/5")
|
|
assert c.get_power() == 4
|
|
|
|
def test_no_pt_returns_zero(self):
|
|
c = Card(name="Test")
|
|
assert c.get_power() == 0
|
|
|
|
def test_non_numeric_returns_zero(self):
|
|
c = Card(name="Test", power="*", pt="*/2")
|
|
assert c.get_power() == 0
|
|
|
|
|
|
class TestCardGetToughness:
|
|
def test_from_toughness_field(self):
|
|
c = Card(name="Test", power="2", toughness="5", pt="2/5")
|
|
assert c.get_toughness() == 5
|
|
|
|
def test_from_pt_field(self):
|
|
c = Card(name="Test", pt="2/7")
|
|
assert c.get_toughness() == 7
|
|
|
|
def test_no_pt_returns_zero(self):
|
|
c = Card(name="Test")
|
|
assert c.get_toughness() == 0
|
|
|
|
|
|
class TestCardHasKeyword:
|
|
def test_in_keywords_list(self):
|
|
c = Card(name="Test", keywords=["Flying", "Haste"])
|
|
assert c.has_keyword("flying") is True
|
|
assert c.has_keyword("haste") is True
|
|
|
|
def test_in_oracle_text(self):
|
|
c = Card(name="Test", oracle_text="This creature has flying and deathtouch.")
|
|
assert c.has_keyword("flying") is True
|
|
assert c.has_keyword("deathtouch") is True
|
|
|
|
def test_missing_keyword(self):
|
|
c = Card(name="Test", oracle_text="Draw a card.", keywords=[])
|
|
assert c.has_keyword("flying") is False
|
|
|
|
def test_case_insensitive(self):
|
|
c = Card(name="Test", keywords=["FLYING"])
|
|
assert c.has_keyword("Flying") is True
|
|
|
|
|
|
class TestCardIsLand:
|
|
def test_basic_land(self):
|
|
c = Card(name="Forest", type_line="Basic Land — Forest")
|
|
assert c.is_land() is True
|
|
|
|
def test_nonbasic_land(self):
|
|
c = Card(name="Command Tower", type_line="Land")
|
|
assert c.is_land() is True
|
|
|
|
def test_creature_not_land(self):
|
|
c = Card(name="Grizzly Bears", type_line="Creature — Bear")
|
|
assert c.is_land() is False
|
|
|
|
def test_no_type_line(self):
|
|
c = Card(name="Unknown")
|
|
assert c.is_land() is False
|
|
|
|
|
|
class TestCardIsCreature:
|
|
def test_creature(self):
|
|
c = Card(name="Grizzly Bears", type_line="Creature — Bear")
|
|
assert c.is_creature() is True
|
|
|
|
def test_legendary_creature(self):
|
|
c = Card(name="Korvold", type_line="Legendary Creature — Dragon Noble")
|
|
assert c.is_creature() is True
|
|
|
|
def test_instant_not_creature(self):
|
|
c = Card(name="Murder", type_line="Instant")
|
|
assert c.is_creature() is False
|
|
|
|
def test_land_not_creature(self):
|
|
c = Card(name="Forest", type_line="Basic Land — Forest")
|
|
assert c.is_creature() is False
|
|
|
|
|
|
class TestCardClone:
|
|
def test_clone_is_equal(self, basic_creature):
|
|
clone = basic_creature.clone()
|
|
assert clone.name == basic_creature.name
|
|
assert clone.cmc == basic_creature.cmc
|
|
|
|
def test_clone_is_independent(self, basic_creature):
|
|
clone = basic_creature.clone()
|
|
clone.name = "Changed"
|
|
assert basic_creature.name == "Grizzly Bears"
|
|
|
|
|
|
# ── PlayerStats ─────────────────────────────────────────────
|
|
|
|
class TestPlayerStats:
|
|
def test_to_dict_keys(self):
|
|
stats = PlayerStats(cards_drawn=7, damage_dealt=10)
|
|
d = stats.to_dict()
|
|
assert "cardsDrawn" in d
|
|
assert "damageDealt" in d
|
|
assert d["cardsDrawn"] == 7
|
|
assert d["damageDealt"] == 10
|
|
|
|
def test_default_values(self):
|
|
stats = PlayerStats()
|
|
assert stats.cards_drawn == 7
|
|
assert stats.lands_played == 0
|
|
|
|
|
|
# ── PlayerResult & GameResult ────────────────────────────────
|
|
|
|
class TestPlayerResult:
|
|
def test_to_dict_winner(self):
|
|
pr = PlayerResult(seat_index=0, name="Alice", life=40, finish_position=1)
|
|
d = pr.to_dict()
|
|
assert d["isWinner"] is True
|
|
assert d["seatIndex"] == 0
|
|
assert d["name"] == "Alice"
|
|
|
|
def test_to_dict_loser(self):
|
|
pr = PlayerResult(seat_index=1, name="Bob", life=0, eliminated=True, finish_position=2)
|
|
d = pr.to_dict()
|
|
assert d["isWinner"] is False
|
|
assert d["eliminated"] is True
|
|
|
|
|
|
class TestGameResult:
|
|
def _make_result(self):
|
|
p0 = PlayerResult(seat_index=0, name="Alice", life=35, finish_position=1, stats=PlayerStats(damage_dealt=20))
|
|
p1 = PlayerResult(seat_index=1, name="Bob", life=0, eliminated=True, finish_position=2, stats=PlayerStats())
|
|
return GameResult(winner_seat=0, turns=12, players=[p0, p1])
|
|
|
|
def test_winner_property(self):
|
|
gr = self._make_result()
|
|
assert gr.winner == 0
|
|
|
|
def test_player_a_name(self):
|
|
gr = self._make_result()
|
|
assert gr.player_a_name == "Alice"
|
|
|
|
def test_player_b_name(self):
|
|
gr = self._make_result()
|
|
assert gr.player_b_name == "Bob"
|
|
|
|
def test_player_a_eliminated(self):
|
|
gr = self._make_result()
|
|
assert gr.player_a_eliminated is False
|
|
|
|
def test_player_b_eliminated(self):
|
|
gr = self._make_result()
|
|
assert gr.player_b_eliminated is True
|
|
|
|
def test_to_dict_has_legacy_keys(self):
|
|
gr = self._make_result()
|
|
d = gr.to_dict()
|
|
assert "winner" in d
|
|
assert "turns" in d
|
|
assert "playerResults" in d
|
|
assert len(d["playerResults"]) == 2
|
|
|
|
|
|
# ── SimState ─────────────────────────────────────────────────
|
|
|
|
class TestSimState:
|
|
def test_init_battlefields(self):
|
|
state = SimState()
|
|
state.init_battlefields(4)
|
|
assert len(state.battlefields) == 4
|
|
|
|
def test_add_and_get_battlefield(self):
|
|
state = SimState()
|
|
c = Card(name="Test", id=1)
|
|
state.add_to_battlefield(0, c)
|
|
bf = state.get_battlefield(0)
|
|
assert len(bf) == 1
|
|
assert bf[0].name == "Test"
|
|
|
|
def test_remove_from_battlefield(self):
|
|
state = SimState()
|
|
c = Card(name="Test", id=42)
|
|
state.add_to_battlefield(0, c)
|
|
removed = state.remove_from_battlefield(42)
|
|
assert removed is not None
|
|
assert removed.name == "Test"
|
|
assert len(state.get_battlefield(0)) == 0
|
|
|
|
def test_remove_nonexistent_returns_none(self):
|
|
state = SimState()
|
|
assert state.remove_from_battlefield(999) is None
|
|
|
|
def test_all_battlefield_cards(self):
|
|
state = SimState()
|
|
state.add_to_battlefield(0, Card(name="A", id=1))
|
|
state.add_to_battlefield(1, Card(name="B", id=2))
|
|
state.add_to_battlefield(1, Card(name="C", id=3))
|
|
all_cards = state.all_battlefield_cards()
|
|
assert len(all_cards) == 3
|