Files
Code_of_Conquest/api/tests/test_effects.py
2025-11-24 23:10:55 -06:00

362 lines
8.8 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Unit tests for Effect dataclass.
Tests all effect types, tick() method, stacking, and serialization.
"""
import pytest
from app.models.effects import Effect
from app.models.enums import EffectType, StatType
def test_effect_creation():
"""Test creating an Effect instance."""
effect = Effect(
effect_id="burn_1",
name="Burning",
effect_type=EffectType.DOT,
duration=3,
power=5,
)
assert effect.effect_id == "burn_1"
assert effect.name == "Burning"
assert effect.effect_type == EffectType.DOT
assert effect.duration == 3
assert effect.power == 5
assert effect.stacks == 1
assert effect.max_stacks == 5
def test_dot_effect_tick():
"""Test DOT (damage over time) effect ticking."""
effect = Effect(
effect_id="poison_1",
name="Poisoned",
effect_type=EffectType.DOT,
duration=3,
power=10,
stacks=2,
)
result = effect.tick()
assert result["effect_type"] == "dot"
assert result["value"] == 20 # 10 power × 2 stacks
assert result["expired"] == False
assert effect.duration == 2 # Reduced by 1
def test_hot_effect_tick():
"""Test HOT (heal over time) effect ticking."""
effect = Effect(
effect_id="regen_1",
name="Regeneration",
effect_type=EffectType.HOT,
duration=5,
power=8,
stacks=1,
)
result = effect.tick()
assert result["effect_type"] == "hot"
assert result["value"] == 8 # 8 power × 1 stack
assert result["expired"] == False
assert effect.duration == 4
def test_stun_effect_tick():
"""Test STUN effect ticking."""
effect = Effect(
effect_id="stun_1",
name="Stunned",
effect_type=EffectType.STUN,
duration=1,
power=0,
)
result = effect.tick()
assert result["effect_type"] == "stun"
assert result.get("stunned") == True
assert effect.duration == 0
def test_shield_effect_tick():
"""Test SHIELD effect ticking."""
effect = Effect(
effect_id="shield_1",
name="Shield",
effect_type=EffectType.SHIELD,
duration=3,
power=50,
)
result = effect.tick()
assert result["effect_type"] == "shield"
assert result["shield_remaining"] == 50
assert effect.duration == 2
def test_buff_effect_tick():
"""Test BUFF effect ticking."""
effect = Effect(
effect_id="str_buff_1",
name="Strength Boost",
effect_type=EffectType.BUFF,
duration=4,
power=5,
stat_affected=StatType.STRENGTH,
stacks=2,
)
result = effect.tick()
assert result["effect_type"] == "buff"
assert result["stat_affected"] == "strength"
assert result["stat_modifier"] == 10 # 5 power × 2 stacks
assert effect.duration == 3
def test_debuff_effect_tick():
"""Test DEBUFF effect ticking."""
effect = Effect(
effect_id="weak_1",
name="Weakened",
effect_type=EffectType.DEBUFF,
duration=2,
power=3,
stat_affected=StatType.STRENGTH,
)
result = effect.tick()
assert result["effect_type"] == "debuff"
assert result["stat_affected"] == "strength"
assert result["stat_modifier"] == 3
assert effect.duration == 1
def test_effect_expiration():
"""Test that effect expires when duration reaches 0."""
effect = Effect(
effect_id="burn_1",
name="Burning",
effect_type=EffectType.DOT,
duration=1,
power=5,
)
result = effect.tick()
assert result["expired"] == True
assert effect.duration == 0
def test_effect_stacking():
"""Test apply_stack() increases stacks up to max."""
effect = Effect(
effect_id="poison_1",
name="Poison",
effect_type=EffectType.DOT,
duration=3,
power=5,
max_stacks=5,
)
assert effect.stacks == 1
effect.apply_stack()
assert effect.stacks == 2
effect.apply_stack()
assert effect.stacks == 3
# Apply 3 more to reach max
effect.apply_stack()
effect.apply_stack()
effect.apply_stack()
assert effect.stacks == 5 # Capped at max_stacks
# Try to apply one more - should still be 5
effect.apply_stack()
assert effect.stacks == 5
def test_shield_damage_absorption():
"""Test reduce_shield() method."""
effect = Effect(
effect_id="shield_1",
name="Shield",
effect_type=EffectType.SHIELD,
duration=3,
power=50,
stacks=1,
)
# Shield absorbs 20 damage
remaining = effect.reduce_shield(20)
assert remaining == 0 # All damage absorbed
assert effect.power == 30 # Shield reduced by 20
# Shield absorbs 20 more
remaining = effect.reduce_shield(20)
assert remaining == 0
assert effect.power == 10
# Shield takes 15 damage, breaks completely
remaining = effect.reduce_shield(15)
assert remaining == 5 # 5 damage passes through
assert effect.power == 0
assert effect.duration == 0 # Effect expires
def test_shield_with_stacks():
"""Test shield absorption with multiple stacks."""
effect = Effect(
effect_id="shield_1",
name="Shield",
effect_type=EffectType.SHIELD,
duration=3,
power=20,
stacks=3,
)
# Total shield = 20 × 3 = 60
# Apply 50 damage
remaining = effect.reduce_shield(50)
assert remaining == 0 # All absorbed
# Shield reduced: 50 / 3 stacks = 16.67 per stack
assert effect.power < 20 # Power reduced
def test_effect_serialization():
"""Test to_dict() serialization."""
effect = Effect(
effect_id="burn_1",
name="Burning",
effect_type=EffectType.DOT,
duration=3,
power=5,
stacks=2,
max_stacks=5,
source="fireball",
)
data = effect.to_dict()
assert data["effect_id"] == "burn_1"
assert data["name"] == "Burning"
assert data["effect_type"] == "dot"
assert data["duration"] == 3
assert data["power"] == 5
assert data["stacks"] == 2
assert data["max_stacks"] == 5
assert data["source"] == "fireball"
def test_effect_deserialization():
"""Test from_dict() deserialization."""
data = {
"effect_id": "regen_1",
"name": "Regeneration",
"effect_type": "hot",
"duration": 5,
"power": 10,
"stat_affected": None,
"stacks": 1,
"max_stacks": 5,
"source": "potion",
}
effect = Effect.from_dict(data)
assert effect.effect_id == "regen_1"
assert effect.name == "Regeneration"
assert effect.effect_type == EffectType.HOT
assert effect.duration == 5
assert effect.power == 10
assert effect.stacks == 1
def test_effect_with_stat_deserialization():
"""Test deserializing effect with stat_affected."""
data = {
"effect_id": "buff_1",
"name": "Strength Boost",
"effect_type": "buff",
"duration": 3,
"power": 5,
"stat_affected": "strength",
"stacks": 1,
"max_stacks": 5,
"source": "spell",
}
effect = Effect.from_dict(data)
assert effect.stat_affected == StatType.STRENGTH
assert effect.effect_type == EffectType.BUFF
def test_effect_round_trip_serialization():
"""Test that serialization and deserialization preserve data."""
original = Effect(
effect_id="test_effect",
name="Test Effect",
effect_type=EffectType.DEBUFF,
duration=10,
power=15,
stat_affected=StatType.CONSTITUTION,
stacks=3,
max_stacks=5,
source="enemy_spell",
)
# Serialize then deserialize
data = original.to_dict()
restored = Effect.from_dict(data)
assert restored.effect_id == original.effect_id
assert restored.name == original.name
assert restored.effect_type == original.effect_type
assert restored.duration == original.duration
assert restored.power == original.power
assert restored.stat_affected == original.stat_affected
assert restored.stacks == original.stacks
assert restored.max_stacks == original.max_stacks
assert restored.source == original.source
def test_effect_repr():
"""Test string representation."""
effect = Effect(
effect_id="poison_1",
name="Poison",
effect_type=EffectType.DOT,
duration=3,
power=5,
stacks=2,
)
repr_str = repr(effect)
assert "Poison" in repr_str
assert "dot" in repr_str
def test_non_shield_reduce_shield():
"""Test that reduce_shield() on non-SHIELD effects returns full damage."""
effect = Effect(
effect_id="burn_1",
name="Burning",
effect_type=EffectType.DOT,
duration=3,
power=5,
)
remaining = effect.reduce_shield(50)
assert remaining == 50 # All damage passes through for non-shield effects