made test_downloader.py compatible with pytest

This commit is contained in:
cdemeyer-teachx
2025-08-14 17:12:26 +09:00
parent f55169d04c
commit 5a2fe7c976
2 changed files with 326 additions and 295 deletions

View File

@@ -1,295 +0,0 @@
#!/usr/bin/env python3
"""
Test script for the Pokemon data downloader.
This script tests the downloader with small data segments to ensure
everything works correctly before downloading larger datasets.
"""
import sys
import tempfile
import shutil
from pathlib import Path
import json
# Add the tools directory to Python path for imports
sys.path.insert(0, str(Path(__file__).parent.parent))
from data.pokemon_downloader import PokemonDownloader
from data.schemas import DataValidator
from rich.console import Console
from rich.panel import Panel
console = Console()
def test_small_pokemon_download():
"""Test downloading a small set of Pokemon (first 3)."""
console.print(Panel.fit("🧪 Testing Pokemon Download (IDs 1-3)", style="bold yellow"))
# Create temporary directory for test output
with tempfile.TemporaryDirectory() as temp_dir:
downloader = PokemonDownloader(output_dir=Path(temp_dir))
# Download first 3 Pokemon
pokemon_data = downloader.download_pokemon_batch(1, 3, max_workers=2)
if not pokemon_data:
console.print("❌ Failed to download any Pokemon")
return False
console.print(f"✅ Downloaded {len(pokemon_data)} Pokemon:")
for pokemon_id, pokemon in pokemon_data.items():
console.print(f" - #{pokemon_id}: {pokemon.name.title()} ({', '.join(pokemon.types)})")
# Save and validate
downloader.save_pokemon_data(pokemon_data, "test_pokemon.json")
# Check the saved file
saved_file = Path(temp_dir) / "test_pokemon.json"
if saved_file.exists():
with open(saved_file) as f:
saved_data = json.load(f)
console.print(f"✅ Successfully saved {len(saved_data)} Pokemon to file")
# Show first Pokemon details
first_pokemon = list(saved_data.values())[0]
console.print(f"📊 Sample data for {first_pokemon['name']}:")
console.print(f" - Types: {first_pokemon['types']}")
console.print(f" - Base HP: {first_pokemon['base_stats']['hp']}")
console.print(f" - Abilities: {first_pokemon['abilities'][:2]}...") # Show first 2
console.print(f" - Move count: {len(first_pokemon['moves'])}")
return True
def test_moves_download():
"""Test downloading a small set of moves."""
console.print(Panel.fit("🧪 Testing Moves Download (IDs 1-5)", style="bold yellow"))
with tempfile.TemporaryDirectory() as temp_dir:
downloader = PokemonDownloader(output_dir=Path(temp_dir))
# Download first 5 moves
move_ids = [1, 2, 3, 4, 5] # Pound, Karate Chop, Double Slap, Comet Punch, Mega Punch
moves_data = downloader.download_moves_batch(move_ids, max_workers=2)
if not moves_data:
console.print("❌ Failed to download any moves")
return False
console.print(f"✅ Downloaded {len(moves_data)} moves:")
for move_id, move in moves_data.items():
power_str = f"{move.power} power" if move.power else "no power"
console.print(f" - #{move_id}: {move.name.title()} ({move.type}, {power_str})")
# Save and validate
downloader.save_moves_data(moves_data, "test_moves.json")
# Check the saved file
saved_file = Path(temp_dir) / "test_moves.json"
if saved_file.exists():
with open(saved_file) as f:
saved_data = json.load(f)
console.print(f"✅ Successfully saved {len(saved_data)} moves to file")
# Show move details
for move_data in list(saved_data.values())[:2]: # Show first 2 moves
console.print(f"📊 {move_data['name'].title()}:")
console.print(f" - Type: {move_data['type']}")
console.print(f" - Power: {move_data['power']}")
console.print(f" - Accuracy: {move_data['accuracy']}")
console.print(f" - PP: {move_data['pp']}")
return True
def test_type_effectiveness():
"""Test downloading type effectiveness data."""
console.print(Panel.fit("🧪 Testing Type Effectiveness Download", style="bold yellow"))
with tempfile.TemporaryDirectory() as temp_dir:
downloader = PokemonDownloader(output_dir=Path(temp_dir))
# Download type effectiveness
effectiveness_data = downloader.download_type_effectiveness()
if not effectiveness_data:
console.print("❌ Failed to download type effectiveness data")
return False
console.print(f"✅ Downloaded {len(effectiveness_data)} type effectiveness entries")
# Show some examples
console.print("📊 Sample type effectiveness entries:")
for entry in effectiveness_data[:5]:
factor_str = {0.0: "no effect", 0.5: "not very effective", 2.0: "super effective"}
console.print(f" - {entry.attacking_type} vs {entry.defending_type}: {factor_str.get(entry.damage_factor, str(entry.damage_factor))}")
# Save and validate
downloader.save_type_effectiveness(effectiveness_data, "test_types.json")
# Check the saved file
saved_file = Path(temp_dir) / "test_types.json"
if saved_file.exists():
with open(saved_file) as f:
saved_data = json.load(f)
console.print(f"✅ Successfully saved {len(saved_data)} type effectiveness entries to file")
return True
def test_validation():
"""Test the data validation system."""
console.print(Panel.fit("🧪 Testing Data Validation", style="bold yellow"))
validator = DataValidator()
# Test valid Pokemon data
valid_pokemon = {
"1": {
"id": 1,
"name": "bulbasaur",
"types": ["grass", "poison"],
"base_stats": {
"hp": 45,
"attack": 49,
"defense": 49,
"special_attack": 65,
"special_defense": 65,
"speed": 45
},
"abilities": ["overgrow", "chlorophyll"],
"moves": [1, 2, 3, 4],
"weight": 69,
"height": 7,
"base_experience": 64
}
}
errors = validator.validate_pokemon_collection(valid_pokemon)
if errors:
console.print(f"❌ Validation failed for valid data: {errors}")
return False
else:
console.print("✅ Valid Pokemon data passed validation")
# Test invalid Pokemon data
invalid_pokemon = {
"1": {
"id": 1,
"name": "bulbasaur",
"types": ["grass", "invalid_type"], # Invalid type
"base_stats": {
"hp": 45,
"attack": 49,
"defense": 49,
"special_attack": 65,
"special_defense": 65,
"speed": 45
},
"abilities": ["overgrow"],
"moves": [1, 2, 3, 4],
"weight": 69,
"height": 7,
"base_experience": 64
}
}
errors = validator.validate_pokemon_collection(invalid_pokemon)
if errors:
console.print(f"✅ Invalid Pokemon data correctly failed validation: {len(errors)} errors")
else:
console.print("❌ Invalid data should have failed validation")
return False
return True
def test_integrated_download():
"""Test downloading Pokemon with their moves in an integrated fashion."""
console.print(Panel.fit("🧪 Testing Integrated Pokemon + Moves Download", style="bold yellow"))
with tempfile.TemporaryDirectory() as temp_dir:
downloader = PokemonDownloader(output_dir=Path(temp_dir))
# Download a single Pokemon (Pikachu)
pokemon_data = downloader.download_pokemon_batch(25, 25, max_workers=1)
if not pokemon_data:
console.print("❌ Failed to download Pikachu")
return False
pikachu = pokemon_data[25]
console.print(f"✅ Downloaded {pikachu.name.title()}")
console.print(f" - Types: {pikachu.types}")
console.print(f" - Base stats total: {sum(pikachu.base_stats.__dict__.values())}")
console.print(f" - Can learn {len(pikachu.moves)} moves")
# Download first 10 moves that Pikachu can learn
pikachu_moves = pikachu.moves[:10]
moves_data = downloader.download_moves_batch(pikachu_moves, max_workers=3)
if moves_data:
console.print(f"✅ Downloaded {len(moves_data)} of Pikachu's moves:")
for move_id, move in list(moves_data.items())[:5]:
console.print(f" - {move.name.title()} ({move.type} type)")
# Save both datasets
downloader.save_pokemon_data(pokemon_data, "pikachu.json")
downloader.save_moves_data(moves_data, "pikachu_moves.json")
return True
def run_all_tests():
"""Run all tests."""
console.print(Panel.fit(
"🚀 Pokemon Data Downloader Test Suite",
style="bold green"
))
tests = [
("Pokemon Download", test_small_pokemon_download),
("Moves Download", test_moves_download),
("Type Effectiveness", test_type_effectiveness),
("Data Validation", test_validation),
("Integrated Download", test_integrated_download),
]
results = []
for test_name, test_func in tests:
console.print(f"\n{'='*50}")
try:
result = test_func()
results.append((test_name, result))
status = "✅ PASSED" if result else "❌ FAILED"
console.print(f"{test_name}: {status}")
except Exception as e:
results.append((test_name, False))
console.print(f"{test_name}: ❌ ERROR - {e}")
# Summary
console.print(f"\n{'='*50}")
console.print("TEST SUMMARY:")
passed = sum(1 for _, result in results if result)
total = len(results)
for test_name, result in results:
status = "" if result else ""
console.print(f" {status} {test_name}")
console.print(f"\nOverall: {passed}/{total} tests passed")
if passed == total:
console.print(Panel.fit("🎉 All tests passed! The downloader is ready to use.", style="bold green"))
else:
console.print(Panel.fit("⚠️ Some tests failed. Please check the errors above.", style="bold red"))
return passed == total
if __name__ == "__main__":
success = run_all_tests()
sys.exit(0 if success else 1)

View File

@@ -0,0 +1,326 @@
#!/usr/bin/env python3
"""
Pytest test suite for the Pokemon data downloader.
This module contains pytest-compatible tests for the downloader with small data segments
to ensure everything works correctly before downloading larger datasets.
"""
import sys
import tempfile
import json
from pathlib import Path
import pytest
# Add the tools directory to Python path for imports
sys.path.insert(0, str(Path(__file__).parent.parent))
from data.pokemon_downloader import PokemonDownloader
from data.schemas import DataValidator
from rich.console import Console
from rich.panel import Panel
console = Console()
@pytest.fixture
def temp_output_dir():
"""Fixture providing a temporary directory for test outputs."""
with tempfile.TemporaryDirectory() as temp_dir:
yield Path(temp_dir)
@pytest.fixture
def downloader(temp_output_dir):
"""Fixture providing a PokemonDownloader instance with temporary output directory."""
return PokemonDownloader(output_dir=temp_output_dir)
@pytest.fixture
def data_validator():
"""Fixture providing a DataValidator instance."""
return DataValidator()
def test_small_pokemon_download(downloader, temp_output_dir):
"""Test downloading a small set of Pokemon (first 3)."""
console.print(Panel.fit("🧪 Testing Pokemon Download (IDs 1-3)", style="bold yellow"))
# Download first 3 Pokemon
pokemon_data = downloader.download_pokemon_batch(1, 3, max_workers=2)
# Assertions using pytest
assert pokemon_data is not None, "Failed to download any Pokemon"
assert len(pokemon_data) > 0, "Pokemon data should not be empty"
assert len(pokemon_data) <= 3, "Should not download more than 3 Pokemon"
console.print(f"✅ Downloaded {len(pokemon_data)} Pokemon:")
for pokemon_id, pokemon in pokemon_data.items():
console.print(f" - #{pokemon_id}: {pokemon.name.title()} ({', '.join(pokemon.types)})")
# Assert each pokemon has required attributes
assert hasattr(pokemon, 'name'), f"Pokemon {pokemon_id} missing name"
assert hasattr(pokemon, 'types'), f"Pokemon {pokemon_id} missing types"
assert len(pokemon.types) > 0, f"Pokemon {pokemon_id} should have at least one type"
# Save and validate
downloader.save_pokemon_data(pokemon_data, "test_pokemon.json")
# Check the saved file
saved_file = temp_output_dir / "test_pokemon.json"
assert saved_file.exists(), "Saved Pokemon file should exist"
with open(saved_file) as f:
saved_data = json.load(f)
assert len(saved_data) == len(pokemon_data), "Saved data should match downloaded data length"
console.print(f"✅ Successfully saved {len(saved_data)} Pokemon to file")
# Show first Pokemon details
first_pokemon = list(saved_data.values())[0]
console.print(f"📊 Sample data for {first_pokemon['name']}:")
console.print(f" - Types: {first_pokemon['types']}")
console.print(f" - Base HP: {first_pokemon['base_stats']['hp']}")
console.print(f" - Abilities: {first_pokemon['abilities'][:2]}...") # Show first 2
console.print(f" - Move count: {len(first_pokemon['moves'])}")
# Assert the structure of saved data
assert 'name' in first_pokemon, "Pokemon should have name"
assert 'types' in first_pokemon, "Pokemon should have types"
assert 'base_stats' in first_pokemon, "Pokemon should have base_stats"
assert 'hp' in first_pokemon['base_stats'], "Pokemon should have HP stat"
def test_moves_download(downloader, temp_output_dir):
"""Test downloading a small set of moves."""
console.print(Panel.fit("🧪 Testing Moves Download (IDs 1-5)", style="bold yellow"))
# Download first 5 moves
move_ids = [1, 2, 3, 4, 5] # Pound, Karate Chop, Double Slap, Comet Punch, Mega Punch
moves_data = downloader.download_moves_batch(move_ids, max_workers=2)
# Assertions using pytest
assert moves_data is not None, "Failed to download any moves"
assert len(moves_data) > 0, "Moves data should not be empty"
assert len(moves_data) <= 5, "Should not download more than 5 moves"
console.print(f"✅ Downloaded {len(moves_data)} moves:")
for move_id, move in moves_data.items():
power_str = f"{move.power} power" if move.power else "no power"
console.print(f" - #{move_id}: {move.name.title()} ({move.type}, {power_str})")
# Assert each move has required attributes
assert hasattr(move, 'name'), f"Move {move_id} missing name"
assert hasattr(move, 'type'), f"Move {move_id} missing type"
assert move.name, f"Move {move_id} should have a non-empty name"
# Save and validate
downloader.save_moves_data(moves_data, "test_moves.json")
# Check the saved file
saved_file = temp_output_dir / "test_moves.json"
assert saved_file.exists(), "Saved moves file should exist"
with open(saved_file) as f:
saved_data = json.load(f)
assert len(saved_data) == len(moves_data), "Saved data should match downloaded data length"
console.print(f"✅ Successfully saved {len(saved_data)} moves to file")
# Show move details and assert structure
for move_data in list(saved_data.values())[:2]: # Show first 2 moves
console.print(f"📊 {move_data['name'].title()}:")
console.print(f" - Type: {move_data['type']}")
console.print(f" - Power: {move_data['power']}")
console.print(f" - Accuracy: {move_data['accuracy']}")
console.print(f" - PP: {move_data['pp']}")
# Assert the structure of saved move data
assert 'name' in move_data, "Move should have name"
assert 'type' in move_data, "Move should have type"
assert 'power' in move_data, "Move should have power (can be None)"
assert 'accuracy' in move_data, "Move should have accuracy"
assert 'pp' in move_data, "Move should have PP"
def test_type_effectiveness(downloader, temp_output_dir):
"""Test downloading type effectiveness data."""
console.print(Panel.fit("🧪 Testing Type Effectiveness Download", style="bold yellow"))
# Download type effectiveness
effectiveness_data = downloader.download_type_effectiveness()
# Assertions using pytest
assert effectiveness_data is not None, "Failed to download type effectiveness data"
assert len(effectiveness_data) > 0, "Type effectiveness data should not be empty"
console.print(f"✅ Downloaded {len(effectiveness_data)} type effectiveness entries")
# Show some examples and validate structure
console.print("📊 Sample type effectiveness entries:")
for entry in effectiveness_data[:5]:
factor_str = {0.0: "no effect", 0.5: "not very effective", 2.0: "super effective"}
console.print(f" - {entry.attacking_type} vs {entry.defending_type}: {factor_str.get(entry.damage_factor, str(entry.damage_factor))}")
# Assert each entry has required attributes
assert hasattr(entry, 'attacking_type'), "Entry missing attacking_type"
assert hasattr(entry, 'defending_type'), "Entry missing defending_type"
assert hasattr(entry, 'damage_factor'), "Entry missing damage_factor"
assert isinstance(entry.damage_factor, (int, float)), "Damage factor should be numeric"
# Save and validate
downloader.save_type_effectiveness(effectiveness_data, "test_types.json")
# Check the saved file
saved_file = temp_output_dir / "test_types.json"
assert saved_file.exists(), "Saved type effectiveness file should exist"
with open(saved_file) as f:
saved_data = json.load(f)
assert len(saved_data) == len(effectiveness_data), "Saved data should match downloaded data length"
console.print(f"✅ Successfully saved {len(saved_data)} type effectiveness entries to file")
def test_validation(data_validator):
"""Test the data validation system."""
console.print(Panel.fit("🧪 Testing Data Validation", style="bold yellow"))
# Test valid Pokemon data
valid_pokemon = {
"1": {
"id": 1,
"name": "bulbasaur",
"types": ["grass", "poison"],
"base_stats": {
"hp": 45,
"attack": 49,
"defense": 49,
"special_attack": 65,
"special_defense": 65,
"speed": 45
},
"abilities": ["overgrow", "chlorophyll"],
"moves": [1, 2, 3, 4],
"weight": 69,
"height": 7,
"base_experience": 64
}
}
errors = data_validator.validate_pokemon_collection(valid_pokemon)
assert not errors, f"Validation should pass for valid data, but got errors: {errors}"
console.print("✅ Valid Pokemon data passed validation")
# Test invalid Pokemon data
invalid_pokemon = {
"1": {
"id": 1,
"name": "bulbasaur",
"types": ["grass", "invalid_type"], # Invalid type
"base_stats": {
"hp": 45,
"attack": 49,
"defense": 49,
"special_attack": 65,
"special_defense": 65,
"speed": 45
},
"abilities": ["overgrow"],
"moves": [1, 2, 3, 4],
"weight": 69,
"height": 7,
"base_experience": 64
}
}
errors = data_validator.validate_pokemon_collection(invalid_pokemon)
assert errors, "Invalid Pokemon data should fail validation"
assert len(errors) > 0, "Should have validation errors for invalid data"
console.print(f"✅ Invalid Pokemon data correctly failed validation: {len(errors)} errors")
def test_integrated_download(downloader, temp_output_dir):
"""Test downloading Pokemon with their moves in an integrated fashion."""
console.print(Panel.fit("🧪 Testing Integrated Pokemon + Moves Download", style="bold yellow"))
# Download a single Pokemon (Pikachu)
pokemon_data = downloader.download_pokemon_batch(25, 25, max_workers=1)
assert pokemon_data is not None, "Failed to download Pikachu"
assert 25 in pokemon_data, "Pikachu (ID 25) should be in the downloaded data"
pikachu = pokemon_data[25]
assert pikachu.name.lower() == "pikachu", "Downloaded Pokemon should be Pikachu"
assert len(pikachu.types) > 0, "Pikachu should have at least one type"
assert len(pikachu.moves) > 0, "Pikachu should have moves"
console.print(f"✅ Downloaded {pikachu.name.title()}")
console.print(f" - Types: {pikachu.types}")
console.print(f" - Base stats total: {sum(pikachu.base_stats.__dict__.values())}")
console.print(f" - Can learn {len(pikachu.moves)} moves")
# Download first 10 moves that Pikachu can learn
pikachu_moves = pikachu.moves[:10]
moves_data = downloader.download_moves_batch(pikachu_moves, max_workers=3)
assert moves_data is not None, "Should successfully download Pikachu's moves"
assert len(moves_data) > 0, "Should download at least some of Pikachu's moves"
console.print(f"✅ Downloaded {len(moves_data)} of Pikachu's moves:")
for move_id, move in list(moves_data.items())[:5]:
console.print(f" - {move.name.title()} ({move.type} type)")
assert hasattr(move, 'name'), f"Move {move_id} should have a name"
assert hasattr(move, 'type'), f"Move {move_id} should have a type"
# Save both datasets
downloader.save_pokemon_data(pokemon_data, "pikachu.json")
downloader.save_moves_data(moves_data, "pikachu_moves.json")
# Verify files were saved
assert (temp_output_dir / "pikachu.json").exists(), "Pikachu data file should be saved"
assert (temp_output_dir / "pikachu_moves.json").exists(), "Pikachu moves data file should be saved"
# Parametrized tests for better pytest organization
@pytest.mark.parametrize("pokemon_range", [(1, 3), (4, 6)])
def test_pokemon_download_ranges(downloader, temp_output_dir, pokemon_range):
"""Test downloading different ranges of Pokemon."""
start_id, end_id = pokemon_range
pokemon_data = downloader.download_pokemon_batch(start_id, end_id, max_workers=2)
assert pokemon_data is not None, f"Failed to download Pokemon {start_id}-{end_id}"
assert len(pokemon_data) > 0, "Pokemon data should not be empty"
for pokemon_id, pokemon in pokemon_data.items():
assert start_id <= pokemon_id <= end_id, f"Pokemon ID {pokemon_id} should be in range {start_id}-{end_id}"
assert hasattr(pokemon, 'name'), f"Pokemon {pokemon_id} missing name"
assert hasattr(pokemon, 'types'), f"Pokemon {pokemon_id} missing types"
@pytest.mark.parametrize("move_ids", [[1, 2, 3], [10, 11, 12, 13]])
def test_moves_download_batches(downloader, temp_output_dir, move_ids):
"""Test downloading different batches of moves."""
moves_data = downloader.download_moves_batch(move_ids, max_workers=2)
assert moves_data is not None, f"Failed to download moves {move_ids}"
assert len(moves_data) > 0, "Moves data should not be empty"
for move_id, move in moves_data.items():
assert move_id in move_ids, f"Move ID {move_id} should be in requested list"
assert hasattr(move, 'name'), f"Move {move_id} missing name"
assert hasattr(move, 'type'), f"Move {move_id} missing type"
# Test that can be run directly with python for debugging
if __name__ == "__main__":
# This allows the file to be run directly for debugging, but pytest is preferred
console.print(Panel.fit(
"🚀 Pokemon Data Downloader Tests\n\nFor full test suite, please use: pytest tools/data/test_downloader.py",
style="bold yellow"
))
console.print("\nRunning basic validation test...")
# Just run a quick validation test when run directly
validator = DataValidator()
test_validation(validator)
console.print("\n✅ Basic test passed! Use pytest for full test suite.")