Files
commander-ai-lab/tests/deck_builder/test_adapter.py
T

105 lines
4.0 KiB
Python

"""Tests for DeckBuilderAdapter — the bridge between Ollama pipeline and legacy API."""
import pytest
from unittest.mock import patch, MagicMock
from types import SimpleNamespace
from src.commander_ai_lab.deck_builder.adapter import DeckBuilderAdapter
from src.commander_ai_lab.deck_builder.core.models import (
BuildResult,
CardEntry,
CommanderDeck,
DeckRatios,
)
def _fake_build_result():
"""Return a minimal BuildResult for mocking build_deck."""
cmdr = CardEntry(name="Atraxa", color_identity=["W", "U", "B", "G"])
basics = ["Plains", "Island", "Swamp", "Mountain", "Forest"]
cards = [
CardEntry(name=basics[i % 5], quantity=1, category="lands", color_identity={"W", "U", "B", "G"})
for i in range(99)
]
deck = CommanderDeck(commander=cmdr, cards=cards)
return BuildResult(
deck=deck,
warnings=["test warning"],
sources_consulted=["scryfall", "edhrec"],
build_time_seconds=1.23,
)
class TestDeckBuilderAdapter:
def test_init_defaults(self):
adapter = DeckBuilderAdapter()
assert adapter.model == "gpt-oss:20b"
assert adapter.db_conn_factory is None
@patch("src.commander_ai_lab.deck_builder.adapter.build_deck")
def test_generate_deck_returns_expected_shape(self, mock_build):
mock_build.return_value = _fake_build_result()
adapter = DeckBuilderAdapter()
result = adapter.generate_deck("Atraxa", strategy="superfriends")
assert "commander" in result
assert result["commander"]["name"] == "Atraxa"
assert "cards" in result
assert "stats" in result
assert result["model"] == "gpt-oss:20b"
assert result["build_time_seconds"] == 1.23
assert "scryfall" in result["reasoning"]["sources"]
@patch("src.commander_ai_lab.deck_builder.adapter.build_deck")
def test_generate_deck_fills_to_100(self, mock_build):
mock_build.return_value = _fake_build_result()
adapter = DeckBuilderAdapter()
result = adapter.generate_deck("Atraxa")
total = sum(c.get("count", 1) for c in result["cards"])
assert total == 100 # 99 cards + commander fills to 100
@patch("src.commander_ai_lab.deck_builder.adapter.build_deck")
def test_ownership_without_db(self, mock_build):
mock_build.return_value = _fake_build_result()
adapter = DeckBuilderAdapter(db_conn_factory=None)
result = adapter.generate_deck("Atraxa")
for card in result["cards"]:
assert card["owned"] is False
assert card["from_collection"] is False
class TestRunSubstitution:
def test_no_op_returns_namespace(self):
adapter = DeckBuilderAdapter()
cards = [
{"name": "Sol Ring", "owned": True},
{"name": "Mana Crypt", "owned": False},
]
result = adapter.run_substitution(cards, commander="Atraxa")
assert isinstance(result, SimpleNamespace)
assert result.owned_count == 1
assert result.substituted_count == 0
assert result.missing_count == 1
class TestHelpers:
def test_fill_basic_lands_adds_missing(self):
cards = [{"name": "Sol Ring", "count": 1, "category": "ramp"}]
filled = DeckBuilderAdapter._fill_basic_lands(cards, ["W", "U"], target_total=5)
total = sum(c.get("count", 1) for c in filled)
assert total == 5
def test_fill_basic_lands_no_excess(self):
cards = [{"name": f"Card{i}", "count": 1} for i in range(100)]
filled = DeckBuilderAdapter._fill_basic_lands(cards, ["W"], target_total=100)
assert len(filled) == 100
def test_compute_stats(self):
cards = [
{"name": "Sol Ring", "count": 1, "category": "ramp", "cmc": 1},
{"name": "Plains", "count": 36, "category": "lands", "cmc": 0},
]
stats = DeckBuilderAdapter._compute_stats(cards)
assert stats["total_cards"] == 37
assert stats["land_count"] == 36
assert stats["average_cmc"] == 1.0