277 lines
9.8 KiB
Python
277 lines
9.8 KiB
Python
#!/usr/bin/env python3
|
|
"""Tests for the Magic: The Gathering Rules Engine"""
|
|
|
|
import sys
|
|
import os
|
|
|
|
# Add parent directory to path for importing mtg_rules_engine
|
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
|
|
from mtg_rules_engine import (
|
|
ABILITY_WORDS,
|
|
KEYWORD_ACTIONS,
|
|
KEYWORD_ABILITIES,
|
|
get_all_keywords,
|
|
get_keyword_info,
|
|
is_valid_keyword,
|
|
get_keyword_type,
|
|
KeywordValidator,
|
|
RulesEngine,
|
|
)
|
|
|
|
|
|
def test_keyword_database():
|
|
"""Test the keyword database is properly populated."""
|
|
print("=" * 60)
|
|
print("TEST: Keyword Database")
|
|
print("=" * 60)
|
|
|
|
# Test ability words
|
|
assert len(ABILITY_WORDS) > 0, "Ability words should not be empty"
|
|
assert "adamant" in ABILITY_WORDS, "adamant should be in ability words"
|
|
print(f" ✓ {len(ABILITY_WORDS)} ability words loaded")
|
|
|
|
# Test keyword actions
|
|
assert len(KEYWORD_ACTIONS) > 0, "Keyword actions should not be empty"
|
|
assert "fly" not in KEYWORD_ACTIONS and "flying" in KEYWORD_ABILITIES, "flying should be a keyword ability"
|
|
assert "cast" in KEYWORD_ACTIONS, "cast should be a keyword action"
|
|
assert "destroy" in KEYWORD_ACTIONS, "destroy should be a keyword action"
|
|
print(f" ✓ {len(KEYWORD_ACTIONS)} keyword actions loaded")
|
|
|
|
# Test keyword abilities
|
|
assert len(KEYWORD_ABILITIES) > 0, "Keyword abilities should not be empty"
|
|
assert "flying" in KEYWORD_ABILITIES, "flying should be a keyword ability"
|
|
assert "haste" in KEYWORD_ABILITIES, "haste should be a keyword ability"
|
|
assert "deathtouch" in KEYWORD_ABILITIES, "deathtouch should be a keyword ability"
|
|
assert "trample" in KEYWORD_ABILITIES, "trample should be a keyword ability"
|
|
assert "hexproof" in KEYWORD_ABILITIES, "hexproof should be a keyword ability"
|
|
assert "flying" in KEYWORD_ABILITIES, "flying should be a keyword ability"
|
|
print(f" ✓ {len(KEYWORD_ABILITIES)} keyword abilities loaded")
|
|
|
|
# Test get_all_keywords
|
|
all_keywords = get_all_keywords()
|
|
assert len(all_keywords) > 0, "get_all_keywords should return keywords"
|
|
print(f" ✓ {len(all_keywords)} total keywords loaded")
|
|
|
|
# Test get_keyword_info
|
|
info = get_keyword_info("flying")
|
|
assert info is not None, "get_keyword_info should return info"
|
|
assert "rule" in info, "Keyword info should have 'rule'"
|
|
assert "definition" in info, "Keyword info should have 'definition'"
|
|
print(f" ✓ Keyword info for 'flying': rule={info['rule'][:40]}...")
|
|
|
|
# Test get_keyword_type
|
|
type_flying = get_keyword_type("flying")
|
|
assert type_flying == "keyword_ability", f"Expected keyword_ability, got {type_flying}"
|
|
type_cast = get_keyword_type("cast")
|
|
assert type_cast == "keyword_action", f"Expected keyword_action, got {type_cast}"
|
|
print(f" ✓ Keyword type for 'flying': {type_flying}")
|
|
print(f" ✓ Keyword type for 'cast': {type_cast}")
|
|
|
|
print(" ✓ All keyword database tests passed!\n")
|
|
|
|
|
|
def test_keyword_validator():
|
|
"""Test the keyword validator."""
|
|
print("=" * 60)
|
|
print("TEST: Keyword Validator")
|
|
print("=" * 60)
|
|
|
|
validator = KeywordValidator()
|
|
|
|
# Test is_valid_keyword
|
|
assert validator.is_valid_keyword("flying"), "flying should be valid"
|
|
assert validator.is_valid_keyword("haste"), "haste should be valid"
|
|
assert not validator.is_valid_keyword("notarealkeyword"), "notarealkeyword should be invalid"
|
|
print(" ✓ is_valid_keyword works correctly")
|
|
|
|
# Test find_keywords_in_text
|
|
text = "Flying creatures can't be blocked except by flying creatures with flying abilities."
|
|
found = validator.find_keywords_in_text(text)
|
|
assert "flying" in found, "flying should be found"
|
|
assert found["flying"] == 3, f"Expected 3 'flying', got {found['flying']}"
|
|
print(f" ✓ find_keywords_in_text found {found}")
|
|
|
|
# Test validate_card_text
|
|
card_text = "Flying, trample. Haste. When this creature attacks, it deals extra damage."
|
|
errors = validator.validate_card_text(card_text)
|
|
# No errors expected for valid keywords
|
|
print(f" ✓ validate_card_text: {len(errors)} errors (expected 0)")
|
|
|
|
# Test check_for_misspelled_keywords
|
|
misspelled = validator.check_for_misspelled_keywords("I want to fly with this haste creature.")
|
|
# "fly" should suggest "flying"
|
|
print(f" ✓ check_for_misspelled_keywords found {len(misspelled)} potential misspellings")
|
|
|
|
# Test export_keywords
|
|
exported = validator.export_keywords()
|
|
assert "total_keywords" in exported, "export_keywords should have total_keywords"
|
|
assert "ability_words" in exported, "export_keywords should have ability_words"
|
|
assert "keyword_actions" in exported, "export_keywords should have keyword_actions"
|
|
assert "keyword_abilities" in exported, "export_keywords should have keyword_abilities"
|
|
print(f" ✓ export_keywords has all expected fields")
|
|
|
|
print(" ✓ All keyword validator tests passed!\n")
|
|
|
|
|
|
def test_rules_engine():
|
|
"""Test the rules engine."""
|
|
print("=" * 60)
|
|
print("TEST: Rules Engine")
|
|
print("=" * 60)
|
|
|
|
engine = RulesEngine()
|
|
|
|
# Test validate_card with a valid creature
|
|
valid_card = {
|
|
"name": "Garruk the Wreathshaper",
|
|
"text": "Flying, trample. When Garruk enters, create a 1/1 green Elf creature token.",
|
|
"type": "CREATURE",
|
|
"power_toughness": (6, 6),
|
|
"color": ["GREEN"],
|
|
"abilities": ["flying", "trample"],
|
|
}
|
|
errors = engine.validate_card(valid_card)
|
|
# Some errors expected (text analysis, etc.)
|
|
print(f" ✓ validate_card returned {len(errors)} errors")
|
|
|
|
# Test validate_action with a valid attack
|
|
valid_attack = {
|
|
"type": "attack",
|
|
"attacker": {
|
|
"name": "Garruk the Wreathshaper",
|
|
"type": "CREATURE",
|
|
"power": 6,
|
|
"toughness": 6,
|
|
"abilities": ["flying", "trample"],
|
|
},
|
|
"blocking_creatures": [
|
|
{
|
|
"name": "Garruk the Wreathshaper",
|
|
"type": "CREATURE",
|
|
"power": 6,
|
|
"toughness": 6,
|
|
"abilities": ["flying", "trample"],
|
|
}
|
|
],
|
|
}
|
|
errors = engine.validate_action(valid_attack)
|
|
# Some errors expected (flying creature can't be blocked)
|
|
print(f" ✓ validate_action returned {len(errors)} errors")
|
|
|
|
# Test get_rule_text
|
|
rule = engine.get_rule_text("702.9")
|
|
assert rule is not None, "get_rule_text should return a result"
|
|
print(f" ✓ get_rule_text returned: {rule[:80]}...")
|
|
|
|
# Test get_keyword_info_summary
|
|
summary = engine.get_keyword_info_summary("flying")
|
|
assert summary is not None, "get_keyword_info_summary should return a summary"
|
|
assert summary["keyword"] == "flying", "Summary should have correct keyword"
|
|
assert summary["rule"] is not None, "Summary should have rule"
|
|
print(f" ✓ get_keyword_info_summary works: {summary}")
|
|
|
|
# Test search_keywords
|
|
results = engine.search_keywords("flying")
|
|
assert len(results) > 0, "search_keywords should find matches"
|
|
print(f" ✓ search_keywords found {len(results)} matches for 'flying'")
|
|
|
|
# Test validate_game_state
|
|
valid_state = {
|
|
"players": [
|
|
{
|
|
"name": "Player 1",
|
|
"hand": [],
|
|
"creatures": [],
|
|
},
|
|
{
|
|
"name": "Player 2",
|
|
"hand": [],
|
|
"creatures": [],
|
|
},
|
|
],
|
|
"zones": {
|
|
"stack": [],
|
|
},
|
|
}
|
|
errors = engine.validate_game_state(valid_state)
|
|
# No errors expected for valid state
|
|
print(f" ✓ validate_game_state returned {len(errors)} errors")
|
|
|
|
print(" ✓ All rules engine tests passed!\n")
|
|
|
|
|
|
def test_integration():
|
|
"""Integration test with the full pipeline."""
|
|
print("=" * 60)
|
|
print("TEST: Integration")
|
|
print("=" * 60)
|
|
|
|
# End-to-end test: Create a card, validate it, analyze its keywords
|
|
engine = RulesEngine()
|
|
|
|
card = {
|
|
"name": "Mighty Hero",
|
|
"text": "Flying, trample, trample over planeswalkers. Haste. When this creature enters, create a 1/1 token.",
|
|
"type": "CREATURE",
|
|
"power_toughness": (4, 4),
|
|
"color": ["RED"],
|
|
"abilities": ["flying", "trample", "haste"],
|
|
}
|
|
|
|
# Validate card
|
|
errors = engine.validate_card(card)
|
|
print(f" Card validation: {len(errors)} errors")
|
|
|
|
# Find keywords in card text
|
|
keywords = engine.validator.find_keywords_in_text(card["text"])
|
|
print(f" Keywords found in card text: {list(keywords.keys())}")
|
|
|
|
# Get keyword info for each found keyword
|
|
for keyword in keywords:
|
|
info = engine.get_keyword_info_summary(keyword)
|
|
print(f" - {keyword}: {info.get('rule', 'N/A')}")
|
|
|
|
# Validate an attack
|
|
attack = {
|
|
"type": "attack",
|
|
"attacker": {
|
|
"name": "Mighty Hero",
|
|
"type": "CREATURE",
|
|
"power": 4,
|
|
"toughness": 4,
|
|
"abilities": ["flying", "trample"],
|
|
},
|
|
"blocking_creatures": [
|
|
{
|
|
"name": "Opponent's Flying Creature",
|
|
"type": "CREATURE",
|
|
"power": 3,
|
|
"toughness": 3,
|
|
"abilities": ["flying"],
|
|
}
|
|
],
|
|
}
|
|
|
|
errors = engine.validate_action(attack)
|
|
print(f" Attack validation: {len(errors)} errors")
|
|
for error in errors:
|
|
print(f" - {error['message']}")
|
|
|
|
print(" ✓ Integration test passed!\n")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
print("\n" + "=" * 60)
|
|
print("Magic: The Gathering Rules Engine - Test Suite")
|
|
print("=" * 60 + "\n")
|
|
|
|
test_keyword_database()
|
|
test_keyword_validator()
|
|
test_rules_engine()
|
|
test_integration()
|
|
|
|
print("=" * 60)
|
|
print("ALL TESTS PASSED!")
|
|
print("=" * 60)
|