""" Unit tests for ClassLoader service. Tests loading player class definitions from YAML files. """ import pytest from pathlib import Path from app.services.class_loader import ClassLoader from app.models.skills import PlayerClass, SkillTree, SkillNode from app.models.stats import Stats class TestClassLoader: """Test ClassLoader functionality.""" @pytest.fixture def loader(self): """Create a ClassLoader instance for testing.""" return ClassLoader() def test_load_vanguard_class(self, loader): """Test loading the Vanguard class.""" vanguard = loader.load_class("vanguard") # Verify class loaded successfully assert vanguard is not None assert isinstance(vanguard, PlayerClass) # Check basic properties assert vanguard.class_id == "vanguard" assert vanguard.name == "Vanguard" assert "melee combat" in vanguard.description.lower() def test_vanguard_base_stats(self, loader): """Test Vanguard base stats are correct.""" vanguard = loader.load_class("vanguard") assert isinstance(vanguard.base_stats, Stats) assert vanguard.base_stats.strength == 14 assert vanguard.base_stats.dexterity == 10 assert vanguard.base_stats.constitution == 14 assert vanguard.base_stats.intelligence == 8 assert vanguard.base_stats.wisdom == 10 assert vanguard.base_stats.charisma == 9 def test_vanguard_skill_trees(self, loader): """Test Vanguard has 2 skill trees.""" vanguard = loader.load_class("vanguard") # Should have exactly 2 skill trees assert len(vanguard.skill_trees) == 2 # Get trees by ID shield_bearer = vanguard.get_skill_tree("shield_bearer") weapon_master = vanguard.get_skill_tree("weapon_master") assert shield_bearer is not None assert weapon_master is not None # Check tree names assert shield_bearer.name == "Shield Bearer" assert weapon_master.name == "Weapon Master" def test_shield_bearer_tree_structure(self, loader): """Test Shield Bearer tree has correct structure.""" vanguard = loader.load_class("vanguard") shield_bearer = vanguard.get_skill_tree("shield_bearer") # Should have 10 nodes (5 tiers × 2 nodes) assert len(shield_bearer.nodes) == 10 # Check tier distribution tier_counts = {} for node in shield_bearer.nodes: tier_counts[node.tier] = tier_counts.get(node.tier, 0) + 1 # Should have 2 nodes per tier for tiers 1-5 assert tier_counts == {1: 2, 2: 2, 3: 2, 4: 2, 5: 2} def test_weapon_master_tree_structure(self, loader): """Test Weapon Master tree has correct structure.""" vanguard = loader.load_class("vanguard") weapon_master = vanguard.get_skill_tree("weapon_master") # Should have 10 nodes (5 tiers × 2 nodes) assert len(weapon_master.nodes) == 10 # Check tier distribution tier_counts = {} for node in weapon_master.nodes: tier_counts[node.tier] = tier_counts.get(node.tier, 0) + 1 # Should have 2 nodes per tier for tiers 1-5 assert tier_counts == {1: 2, 2: 2, 3: 2, 4: 2, 5: 2} def test_skill_node_prerequisites(self, loader): """Test skill nodes have correct prerequisites.""" vanguard = loader.load_class("vanguard") shield_bearer = vanguard.get_skill_tree("shield_bearer") # Find tier 1 and tier 2 nodes tier1_nodes = [n for n in shield_bearer.nodes if n.tier == 1] tier2_nodes = [n for n in shield_bearer.nodes if n.tier == 2] # Tier 1 nodes should have no prerequisites for node in tier1_nodes: assert len(node.prerequisites) == 0 # Tier 2 nodes should have prerequisites for node in tier2_nodes: assert len(node.prerequisites) > 0 # Prerequisites should reference tier 1 skills for prereq_id in node.prerequisites: prereq_found = any(n.skill_id == prereq_id for n in tier1_nodes) assert prereq_found, f"Prerequisite {prereq_id} not found in tier 1" def test_skill_node_effects(self, loader): """Test skill nodes have proper effects defined.""" vanguard = loader.load_class("vanguard") shield_bearer = vanguard.get_skill_tree("shield_bearer") # Find the "fortify" skill (passive defense bonus) fortify = next((n for n in shield_bearer.nodes if n.skill_id == "fortify"), None) assert fortify is not None # Should have stat bonuses in effects assert "stat_bonuses" in fortify.effects assert "defense" in fortify.effects["stat_bonuses"] assert fortify.effects["stat_bonuses"]["defense"] == 5 def test_skill_node_abilities(self, loader): """Test skill nodes with ability unlocks.""" vanguard = loader.load_class("vanguard") shield_bearer = vanguard.get_skill_tree("shield_bearer") # Find the "shield_bash" skill (active ability) shield_bash = next((n for n in shield_bearer.nodes if n.skill_id == "shield_bash"), None) assert shield_bash is not None # Should have abilities in effects assert "abilities" in shield_bash.effects assert "shield_bash" in shield_bash.effects["abilities"] def test_starting_equipment(self, loader): """Test Vanguard starting equipment.""" vanguard = loader.load_class("vanguard") # Should have starting equipment assert len(vanguard.starting_equipment) > 0 assert "rusty_sword" in vanguard.starting_equipment assert "cloth_armor" in vanguard.starting_equipment assert "rusty_knife" in vanguard.starting_equipment def test_starting_abilities(self, loader): """Test Vanguard starting abilities.""" vanguard = loader.load_class("vanguard") # Should have basic_attack assert len(vanguard.starting_abilities) > 0 assert "basic_attack" in vanguard.starting_abilities def test_cache_functionality(self, loader): """Test that classes are cached after first load.""" # Load class twice vanguard1 = loader.load_class("vanguard") vanguard2 = loader.load_class("vanguard") # Should be the same object (cached) assert vanguard1 is vanguard2 def test_reload_class(self, loader): """Test reload_class forces a fresh load.""" # Load class vanguard1 = loader.load_class("vanguard") # Reload class vanguard2 = loader.reload_class("vanguard") # Should still be equal but different objects assert vanguard1.class_id == vanguard2.class_id # Note: May or may not be same object depending on implementation def test_load_nonexistent_class(self, loader): """Test loading a class that doesn't exist.""" result = loader.load_class("nonexistent_class") assert result is None def test_get_all_class_ids(self, loader): """Test getting list of all class IDs.""" class_ids = loader.get_all_class_ids() # Should include vanguard assert "vanguard" in class_ids # Should be a list of strings assert all(isinstance(cid, str) for cid in class_ids) def test_load_all_classes(self, loader): """Test loading all classes at once.""" classes = loader.load_all_classes() # Should have at least 1 class (vanguard) assert len(classes) >= 1 # All should be PlayerClass instances assert all(isinstance(c, PlayerClass) for c in classes) # Vanguard should be in the list vanguard_found = any(c.class_id == "vanguard" for c in classes) assert vanguard_found def test_get_all_skills_method(self, loader): """Test PlayerClass.get_all_skills() method.""" vanguard = loader.load_class("vanguard") all_skills = vanguard.get_all_skills() # Should have 20 skills (2 trees × 10 nodes) assert len(all_skills) == 20 # All should be SkillNode instances assert all(isinstance(skill, SkillNode) for skill in all_skills) # Should include skills from both trees skill_ids = [s.skill_id for s in all_skills] assert "shield_bash" in skill_ids # Shield Bearer tree assert "power_strike" in skill_ids # Weapon Master tree def test_tier_5_ultimate_skills(self, loader): """Test that tier 5 skills exist and have powerful effects.""" vanguard = loader.load_class("vanguard") all_skills = vanguard.get_all_skills() # Get tier 5 skills tier5_skills = [s for s in all_skills if s.tier == 5] # Should have 4 tier 5 skills (2 per tree) assert len(tier5_skills) == 4 # Each tier 5 skill should have prerequisites for skill in tier5_skills: assert len(skill.prerequisites) > 0 # At least one tier 5 skill should have significant stat bonuses has_major_bonuses = any( "stat_bonuses" in s.effects and any(v >= 10 for v in s.effects.get("stat_bonuses", {}).values()) for s in tier5_skills ) assert has_major_bonuses if __name__ == "__main__": pytest.main([__file__, "-v"])