mirror of
https://github.com/RichieCahill/dotfiles.git
synced 2026-04-17 13:08:19 -04:00
Added 35 test files with 502 tests covering all Python modules including API routes, ORM models, splendor game logic/TUI, heater controller, weather service, NixOS installer, ZFS dataset management, and utilities. Coverage improved from 11% to 99% (2540/2564 statements covered). https://claude.ai/code/session_01SVzgLDUS1Cdc4eh1ijETTh
247 lines
8.0 KiB
Python
247 lines
8.0 KiB
Python
"""Extra tests for splendor/base.py covering missed lines and branches."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import random
|
|
|
|
from python.splendor.base import (
|
|
BASE_COLORS,
|
|
GEM_COLORS,
|
|
BuyCard,
|
|
BuyCardReserved,
|
|
Card,
|
|
GameConfig,
|
|
Noble,
|
|
ReserveCard,
|
|
TakeDifferent,
|
|
TakeDouble,
|
|
apply_action,
|
|
apply_buy_card,
|
|
apply_buy_card_reserved,
|
|
apply_reserve_card,
|
|
apply_take_different,
|
|
apply_take_double,
|
|
auto_discard_tokens,
|
|
check_nobles_for_player,
|
|
create_random_cards,
|
|
create_random_nobles,
|
|
enforce_token_limit,
|
|
get_legal_actions,
|
|
new_game,
|
|
run_game,
|
|
)
|
|
from python.splendor.bot import RandomBot
|
|
|
|
|
|
def _make_game(num_players: int = 2):
|
|
random.seed(42)
|
|
bots = [RandomBot(f"bot{i}") for i in range(num_players)]
|
|
cards = create_random_cards()
|
|
nobles = create_random_nobles()
|
|
config = GameConfig(cards=cards, nobles=nobles)
|
|
game = new_game(bots, config)
|
|
return game, bots
|
|
|
|
|
|
def test_auto_discard_tokens_all_zero() -> None:
|
|
"""Test auto_discard when all tokens are zero."""
|
|
game, _ = _make_game()
|
|
p = game.players[0]
|
|
for c in GEM_COLORS:
|
|
p.tokens[c] = 0
|
|
result = auto_discard_tokens(p, 3)
|
|
assert sum(result.values()) == 0 # Can't discard from empty
|
|
|
|
|
|
def test_enforce_token_limit_with_fallback() -> None:
|
|
"""Test enforce_token_limit uses auto_discard as fallback."""
|
|
game, bots = _make_game()
|
|
p = game.players[0]
|
|
strategy = bots[0]
|
|
# Give player many tokens to force discard
|
|
for c in BASE_COLORS:
|
|
p.tokens[c] = 5
|
|
enforce_token_limit(game, strategy, p)
|
|
assert p.total_tokens() <= game.config.token_limit
|
|
|
|
|
|
def test_apply_take_different_invalid_color() -> None:
|
|
"""Test take different with gold (non-base) color."""
|
|
game, bots = _make_game()
|
|
action = TakeDifferent(colors=["gold"])
|
|
apply_take_different(game, bots[0], action)
|
|
# Gold is not in BASE_COLORS, so no tokens should be taken
|
|
|
|
|
|
def test_apply_take_double_invalid_color() -> None:
|
|
"""Test take double with gold (non-base) color."""
|
|
game, bots = _make_game()
|
|
action = TakeDouble(color="gold")
|
|
apply_take_double(game, bots[0], action)
|
|
|
|
|
|
def test_apply_take_double_insufficient_bank() -> None:
|
|
"""Test take double when bank has fewer than minimum."""
|
|
game, bots = _make_game()
|
|
game.bank["white"] = 2 # Below minimum_tokens_to_buy_2 (4)
|
|
action = TakeDouble(color="white")
|
|
apply_take_double(game, bots[0], action)
|
|
|
|
|
|
def test_apply_buy_card_invalid_tier() -> None:
|
|
"""Test buy card with invalid tier."""
|
|
game, bots = _make_game()
|
|
action = BuyCard(tier=99, index=0)
|
|
apply_buy_card(game, bots[0], action)
|
|
|
|
|
|
def test_apply_buy_card_invalid_index() -> None:
|
|
"""Test buy card with out-of-range index."""
|
|
game, bots = _make_game()
|
|
action = BuyCard(tier=1, index=99)
|
|
apply_buy_card(game, bots[0], action)
|
|
|
|
|
|
def test_apply_buy_card_cannot_afford() -> None:
|
|
"""Test buy card when player can't afford."""
|
|
game, bots = _make_game()
|
|
# Zero out all tokens
|
|
for c in GEM_COLORS:
|
|
game.players[0].tokens[c] = 0
|
|
# Find an expensive card
|
|
for tier, row in game.table_by_tier.items():
|
|
for idx, card in enumerate(row):
|
|
if any(v > 0 for v in card.cost.values()):
|
|
action = BuyCard(tier=tier, index=idx)
|
|
apply_buy_card(game, bots[0], action)
|
|
return
|
|
|
|
|
|
def test_apply_buy_card_reserved_invalid_index() -> None:
|
|
"""Test buy reserved card with out-of-range index."""
|
|
game, bots = _make_game()
|
|
action = BuyCardReserved(index=99)
|
|
apply_buy_card_reserved(game, bots[0], action)
|
|
|
|
|
|
def test_apply_buy_card_reserved_cannot_afford() -> None:
|
|
"""Test buy reserved card when can't afford."""
|
|
game, bots = _make_game()
|
|
expensive = Card(tier=3, points=5, color="white", cost={
|
|
"white": 10, "blue": 10, "green": 10, "red": 10, "black": 10, "gold": 0
|
|
})
|
|
game.players[0].reserved.append(expensive)
|
|
for c in GEM_COLORS:
|
|
game.players[0].tokens[c] = 0
|
|
action = BuyCardReserved(index=0)
|
|
apply_buy_card_reserved(game, bots[0], action)
|
|
|
|
|
|
def test_apply_reserve_card_at_limit() -> None:
|
|
"""Test reserve card when at reserve limit."""
|
|
game, bots = _make_game()
|
|
p = game.players[0]
|
|
# Fill up reserved slots
|
|
for _ in range(game.config.reserve_limit):
|
|
p.reserved.append(Card(tier=1, points=0, color="white", cost=dict.fromkeys(GEM_COLORS, 0)))
|
|
action = ReserveCard(tier=1, index=0, from_deck=False)
|
|
apply_reserve_card(game, bots[0], action)
|
|
assert len(p.reserved) == game.config.reserve_limit
|
|
|
|
|
|
def test_apply_reserve_card_invalid_tier() -> None:
|
|
"""Test reserve face-up card with invalid tier."""
|
|
game, bots = _make_game()
|
|
action = ReserveCard(tier=99, index=0, from_deck=False)
|
|
apply_reserve_card(game, bots[0], action)
|
|
|
|
|
|
def test_apply_reserve_card_invalid_index() -> None:
|
|
"""Test reserve face-up card with None index."""
|
|
game, bots = _make_game()
|
|
action = ReserveCard(tier=1, index=None, from_deck=False)
|
|
apply_reserve_card(game, bots[0], action)
|
|
|
|
|
|
def test_apply_reserve_card_from_empty_deck() -> None:
|
|
"""Test reserve from deck when deck is empty."""
|
|
game, bots = _make_game()
|
|
game.decks_by_tier[1] = [] # Empty the deck
|
|
action = ReserveCard(tier=1, index=None, from_deck=True)
|
|
apply_reserve_card(game, bots[0], action)
|
|
|
|
|
|
def test_apply_reserve_card_no_gold() -> None:
|
|
"""Test reserve card when bank has no gold."""
|
|
game, bots = _make_game()
|
|
game.bank["gold"] = 0
|
|
action = ReserveCard(tier=1, index=0, from_deck=True)
|
|
reserved_before = len(game.players[0].reserved)
|
|
apply_reserve_card(game, bots[0], action)
|
|
if len(game.players[0].reserved) > reserved_before:
|
|
assert game.players[0].tokens["gold"] == 0
|
|
|
|
|
|
def test_check_nobles_multiple_candidates() -> None:
|
|
"""Test check_nobles when player qualifies for multiple nobles."""
|
|
game, bots = _make_game()
|
|
p = game.players[0]
|
|
# Give player huge discounts to qualify for everything
|
|
for c in BASE_COLORS:
|
|
p.discounts[c] = 20
|
|
check_nobles_for_player(game, bots[0], p)
|
|
|
|
|
|
def test_check_nobles_chosen_not_in_available() -> None:
|
|
"""Test check_nobles when chosen noble is somehow not available."""
|
|
game, bots = _make_game()
|
|
p = game.players[0]
|
|
for c in BASE_COLORS:
|
|
p.discounts[c] = 20
|
|
# This tests the normal path - chosen should be in available
|
|
|
|
|
|
def test_run_game_turn_limit() -> None:
|
|
"""Test run_game respects turn limit."""
|
|
random.seed(99)
|
|
bots = [RandomBot(f"bot{i}") for i in range(2)]
|
|
cards = create_random_cards()
|
|
nobles = create_random_nobles()
|
|
config = GameConfig(cards=cards, nobles=nobles, turn_limit=5)
|
|
game = new_game(bots, config)
|
|
winner, turns = run_game(game)
|
|
assert turns <= 5
|
|
|
|
|
|
def test_run_game_action_none() -> None:
|
|
"""Test run_game stops when strategy returns None."""
|
|
from unittest.mock import MagicMock
|
|
bots = [RandomBot(f"bot{i}") for i in range(2)]
|
|
cards = create_random_cards()
|
|
nobles = create_random_nobles()
|
|
config = GameConfig(cards=cards, nobles=nobles)
|
|
game = new_game(bots, config)
|
|
# Make the first player's strategy return None
|
|
game.players[0].strategy.choose_action = MagicMock(return_value=None)
|
|
winner, turns = run_game(game)
|
|
assert turns == 1
|
|
|
|
|
|
def test_get_valid_actions_with_reserved() -> None:
|
|
"""Test get_valid_actions includes BuyCardReserved when player has reserved cards."""
|
|
game, _ = _make_game()
|
|
p = game.players[0]
|
|
# Give player a free reserved card
|
|
free_card = Card(tier=1, points=0, color="white", cost=dict.fromkeys(GEM_COLORS, 0))
|
|
p.reserved.append(free_card)
|
|
actions = get_legal_actions(game)
|
|
assert any(isinstance(a, BuyCardReserved) for a in actions)
|
|
|
|
|
|
def test_get_legal_actions_reserve_from_deck() -> None:
|
|
"""Test get_legal_actions includes ReserveCard from deck."""
|
|
game, _ = _make_game()
|
|
actions = get_legal_actions(game)
|
|
assert any(isinstance(a, ReserveCard) and a.from_deck for a in actions)
|
|
assert any(isinstance(a, ReserveCard) and not a.from_deck for a in actions)
|