mirror of
https://github.com/mwisnowski/mtg_python_deckbuilder.git
synced 2025-12-16 07:30:13 +01:00
280 lines
9.5 KiB
Python
280 lines
9.5 KiB
Python
"""
|
|
Migration Compatibility Tests
|
|
|
|
Ensures backward compatibility during migration from individual CSV files
|
|
to consolidated all_cards.parquet. Tests verify that legacy adapter functions
|
|
produce identical results to direct AllCardsLoader calls.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import tempfile
|
|
|
|
import pandas as pd
|
|
import pytest
|
|
|
|
from code.services.all_cards_loader import AllCardsLoader
|
|
from code.services.legacy_loader_adapter import (
|
|
load_all_cards,
|
|
load_cards_by_color_identity,
|
|
load_cards_by_name,
|
|
load_cards_by_names,
|
|
load_cards_by_type,
|
|
load_cards_with_tag,
|
|
load_cards_with_tags,
|
|
search_cards,
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def sample_cards_df():
|
|
"""Create a sample DataFrame for testing."""
|
|
return pd.DataFrame(
|
|
{
|
|
"name": [
|
|
"Sol Ring",
|
|
"Lightning Bolt",
|
|
"Counterspell",
|
|
"Giant Growth",
|
|
"Goblin Token Maker",
|
|
],
|
|
"colorIdentity": ["Colorless", "R", "U", "G", "R"],
|
|
"type": ["Artifact", "Instant", "Instant", "Instant", "Creature — Goblin"],
|
|
"text": [
|
|
"Add two mana",
|
|
"Deal 3 damage",
|
|
"Counter target spell",
|
|
"Target creature gets +3/+3",
|
|
"When this enters, create two 1/1 red Goblin creature tokens",
|
|
],
|
|
"themeTags": ["", "burn,damage", "control,counterspells", "combat,pump", "tokens,goblins"],
|
|
}
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def temp_parquet_file(sample_cards_df):
|
|
"""Create a temporary Parquet file for testing."""
|
|
with tempfile.NamedTemporaryFile(delete=False, suffix=".parquet") as tmp:
|
|
sample_cards_df.to_parquet(tmp.name, engine="pyarrow")
|
|
yield tmp.name
|
|
os.unlink(tmp.name)
|
|
|
|
|
|
def test_load_all_cards_adapter(temp_parquet_file):
|
|
"""Test load_all_cards() legacy function."""
|
|
# Direct loader call
|
|
loader = AllCardsLoader(file_path=temp_parquet_file)
|
|
direct_result = loader.load()
|
|
|
|
# Legacy adapter call
|
|
# Note: We need to temporarily override the loader's file path
|
|
from code.services import legacy_loader_adapter
|
|
legacy_loader_adapter._shared_loader = AllCardsLoader(file_path=temp_parquet_file)
|
|
|
|
with pytest.warns(DeprecationWarning):
|
|
adapter_result = load_all_cards()
|
|
|
|
# Results should be identical
|
|
pd.testing.assert_frame_equal(direct_result, adapter_result)
|
|
|
|
|
|
def test_load_cards_by_name_adapter(temp_parquet_file):
|
|
"""Test load_cards_by_name() legacy function."""
|
|
loader = AllCardsLoader(file_path=temp_parquet_file)
|
|
direct_result = loader.get_by_name("Sol Ring")
|
|
|
|
# Setup adapter with test file
|
|
from code.services import legacy_loader_adapter
|
|
legacy_loader_adapter._shared_loader = AllCardsLoader(file_path=temp_parquet_file)
|
|
|
|
with pytest.warns(DeprecationWarning):
|
|
adapter_result = load_cards_by_name("Sol Ring")
|
|
|
|
# Results should be identical
|
|
assert adapter_result is not None
|
|
pd.testing.assert_series_equal(direct_result, adapter_result)
|
|
|
|
|
|
def test_load_cards_by_names_adapter(temp_parquet_file):
|
|
"""Test load_cards_by_names() legacy function."""
|
|
loader = AllCardsLoader(file_path=temp_parquet_file)
|
|
names = ["Sol Ring", "Lightning Bolt"]
|
|
direct_result = loader.get_by_names(names)
|
|
|
|
from code.services import legacy_loader_adapter
|
|
legacy_loader_adapter._shared_loader = AllCardsLoader(file_path=temp_parquet_file)
|
|
|
|
with pytest.warns(DeprecationWarning):
|
|
adapter_result = load_cards_by_names(names)
|
|
|
|
pd.testing.assert_frame_equal(direct_result, adapter_result)
|
|
|
|
|
|
def test_load_cards_by_type_adapter(temp_parquet_file):
|
|
"""Test load_cards_by_type() legacy function."""
|
|
loader = AllCardsLoader(file_path=temp_parquet_file)
|
|
direct_result = loader.filter_by_type("Instant")
|
|
|
|
from code.services import legacy_loader_adapter
|
|
legacy_loader_adapter._shared_loader = AllCardsLoader(file_path=temp_parquet_file)
|
|
|
|
with pytest.warns(DeprecationWarning):
|
|
adapter_result = load_cards_by_type("Instant")
|
|
|
|
pd.testing.assert_frame_equal(direct_result, adapter_result)
|
|
|
|
|
|
def test_load_cards_with_tag_adapter(temp_parquet_file):
|
|
"""Test load_cards_with_tag() legacy function."""
|
|
loader = AllCardsLoader(file_path=temp_parquet_file)
|
|
direct_result = loader.filter_by_themes(["tokens"], mode="any")
|
|
|
|
from code.services import legacy_loader_adapter
|
|
legacy_loader_adapter._shared_loader = AllCardsLoader(file_path=temp_parquet_file)
|
|
|
|
with pytest.warns(DeprecationWarning):
|
|
adapter_result = load_cards_with_tag("tokens")
|
|
|
|
pd.testing.assert_frame_equal(direct_result, adapter_result)
|
|
|
|
|
|
def test_load_cards_with_tags_any_mode(temp_parquet_file):
|
|
"""Test load_cards_with_tags() with mode='any'."""
|
|
loader = AllCardsLoader(file_path=temp_parquet_file)
|
|
direct_result = loader.filter_by_themes(["burn", "tokens"], mode="any")
|
|
|
|
from code.services import legacy_loader_adapter
|
|
legacy_loader_adapter._shared_loader = AllCardsLoader(file_path=temp_parquet_file)
|
|
|
|
with pytest.warns(DeprecationWarning):
|
|
adapter_result = load_cards_with_tags(["burn", "tokens"], require_all=False)
|
|
|
|
pd.testing.assert_frame_equal(direct_result, adapter_result)
|
|
|
|
|
|
def test_load_cards_with_tags_all_mode(temp_parquet_file):
|
|
"""Test load_cards_with_tags() with mode='all'."""
|
|
loader = AllCardsLoader(file_path=temp_parquet_file)
|
|
direct_result = loader.filter_by_themes(["tokens", "goblins"], mode="all")
|
|
|
|
from code.services import legacy_loader_adapter
|
|
legacy_loader_adapter._shared_loader = AllCardsLoader(file_path=temp_parquet_file)
|
|
|
|
with pytest.warns(DeprecationWarning):
|
|
adapter_result = load_cards_with_tags(["tokens", "goblins"], require_all=True)
|
|
|
|
pd.testing.assert_frame_equal(direct_result, adapter_result)
|
|
|
|
|
|
def test_load_cards_by_color_identity_adapter(temp_parquet_file):
|
|
"""Test load_cards_by_color_identity() legacy function."""
|
|
loader = AllCardsLoader(file_path=temp_parquet_file)
|
|
direct_result = loader.filter_by_color_identity(["R"])
|
|
|
|
from code.services import legacy_loader_adapter
|
|
legacy_loader_adapter._shared_loader = AllCardsLoader(file_path=temp_parquet_file)
|
|
|
|
with pytest.warns(DeprecationWarning):
|
|
adapter_result = load_cards_by_color_identity(["R"])
|
|
|
|
pd.testing.assert_frame_equal(direct_result, adapter_result)
|
|
|
|
|
|
def test_search_cards_adapter(temp_parquet_file):
|
|
"""Test search_cards() legacy function."""
|
|
loader = AllCardsLoader(file_path=temp_parquet_file)
|
|
direct_result = loader.search("token", limit=100)
|
|
|
|
from code.services import legacy_loader_adapter
|
|
legacy_loader_adapter._shared_loader = AllCardsLoader(file_path=temp_parquet_file)
|
|
|
|
with pytest.warns(DeprecationWarning):
|
|
adapter_result = search_cards("token", limit=100)
|
|
|
|
pd.testing.assert_frame_equal(direct_result, adapter_result)
|
|
|
|
|
|
def test_deprecation_warnings_logged(temp_parquet_file, caplog):
|
|
"""Test that deprecation warnings are properly logged."""
|
|
from code.services import legacy_loader_adapter
|
|
legacy_loader_adapter._shared_loader = AllCardsLoader(file_path=temp_parquet_file)
|
|
|
|
with pytest.warns(DeprecationWarning):
|
|
load_cards_by_name("Sol Ring")
|
|
|
|
# Check that warning was logged
|
|
assert any("DEPRECATION" in record.message for record in caplog.records)
|
|
|
|
|
|
def test_feature_flag_disabled(temp_parquet_file, monkeypatch):
|
|
"""Test behavior when USE_ALL_CARDS_FILE is disabled."""
|
|
# Disable feature flag
|
|
monkeypatch.setattr("code.settings.USE_ALL_CARDS_FILE", False)
|
|
|
|
# Reimport to pick up new setting
|
|
import importlib
|
|
from code.services import legacy_loader_adapter
|
|
importlib.reload(legacy_loader_adapter)
|
|
|
|
legacy_loader_adapter._shared_loader = AllCardsLoader(file_path=temp_parquet_file)
|
|
|
|
with pytest.warns(DeprecationWarning):
|
|
result = load_all_cards()
|
|
|
|
# Should return empty DataFrame when disabled
|
|
assert result.empty
|
|
|
|
|
|
def test_adapter_uses_shared_loader(temp_parquet_file):
|
|
"""Test that adapter reuses shared loader instance for performance."""
|
|
from code.services import legacy_loader_adapter
|
|
|
|
# Clear any existing loader
|
|
legacy_loader_adapter._shared_loader = None
|
|
legacy_loader_adapter._shared_loader = AllCardsLoader(file_path=temp_parquet_file)
|
|
|
|
with pytest.warns(DeprecationWarning):
|
|
load_all_cards()
|
|
|
|
loader1 = legacy_loader_adapter._shared_loader
|
|
|
|
with pytest.warns(DeprecationWarning):
|
|
load_cards_by_name("Sol Ring")
|
|
|
|
loader2 = legacy_loader_adapter._shared_loader
|
|
|
|
# Should be the same instance
|
|
assert loader1 is loader2
|
|
|
|
|
|
def test_multiple_calls_use_cache(temp_parquet_file, monkeypatch):
|
|
"""Test that multiple adapter calls benefit from caching."""
|
|
import time
|
|
from code.services import legacy_loader_adapter
|
|
|
|
# Ensure feature flag is enabled
|
|
monkeypatch.setattr("code.settings.USE_ALL_CARDS_FILE", True)
|
|
|
|
# Reimport to pick up setting
|
|
import importlib
|
|
importlib.reload(legacy_loader_adapter)
|
|
|
|
legacy_loader_adapter._shared_loader = AllCardsLoader(file_path=temp_parquet_file)
|
|
|
|
# First call (loads from disk)
|
|
start = time.time()
|
|
with pytest.warns(DeprecationWarning):
|
|
load_all_cards()
|
|
first_time = time.time() - start
|
|
|
|
# Second call (should use cache)
|
|
start = time.time()
|
|
with pytest.warns(DeprecationWarning):
|
|
load_all_cards()
|
|
second_time = time.time() - start
|
|
|
|
# Cache should make second call faster (or at least not slower)
|
|
# Use a more lenient check since file is very small
|
|
assert second_time <= first_time * 2 # Allow some variance
|