257 lines
9.2 KiB
Python
257 lines
9.2 KiB
Python
"""
|
||
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"])
|