mtg_python_deckbuilder/code/tests/test_migration_compatibility.py

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