"""Batch 1 (V6a): the 32 PC-sheet spells added to data/spells/. Given: the registry directory with the 11 original spells plus batch 1 When: load_spell_registry / load_spell is called Then: every new spell loads with the expected level/school/range/save, the registry grew by exactly 32, and every effect validates against the schema (parsing raises on any unknown field value or condition). """ from __future__ import annotations from pathlib import Path import pytest from pf1e_simulator.conditions import CONDITIONS_BY_NAME from pf1e_simulator.spells import ( AreaEffect, BuffEffect, ConditionEffect, DamageEffect, HealEffect, SpellEffect, SpellSpec, SummonEffect, load_spell, load_spell_registry, ) _SPELLS_DIR = Path(__file__).resolve().parent.parent / "data" / "spells" _ORIGINAL_COUNT = 11 # name -> (file stem, level, school, range, save) _BATCH1: dict[str, tuple[str, int, str, str, str]] = { "Abundant Ammunition": ("abundant_ammunition", 0, "transmutation", "close", "none"), "Acid Splash": ("acid_splash", 0, "conjuration", "close", "none"), "Aid": ("aid", 1, "enchantment", "long", "none"), "Air Bubble": ("air_bubble", 1, "conjuration", "personal", "none"), "Alarm": ("alarm", 1, "abjuration", "medium", "none"), "Arcane Mark": ("arcane_mark", 0, "divination", "touch", "none"), "Bane": ("bane", 1, "enchantment", "medium", "will"), "Bear's Endurance": ("bears_endurance", 2, "transmutation", "touch", "fort"), "Bladed Dash": ("bladed_dash", 1, "transmutation", "close", "none"), "Bleed": ("bleed", 0, "necromancy", "close", "will"), "Bless": ("bless", 1, "enchantment", "medium", "none"), "Blindness/Deafness": ("blindness_deafness", 2, "necromancy", "medium", "will"), "Charm Person": ("charm_person", 1, "enchantment", "medium", "will"), "Color Spray": ("color_spray", 1, "enchantment", "close", "will"), "Comprehend Languages": ("comprehend_languages", 0, "divination", "personal", "none"), "Create Alcool": ("create_alcool", 0, "conjuration", "close", "none"), "Create Water": ("create_water", 1, "conjuration", "medium", "none"), "Dancing Lights": ("dancing_lights", 0, "conjuration", "close", "none"), "Darkness": ("darkness", 2, "evocation", "touch", "none"), "Daze": ("daze", 0, "enchantment", "close", "will"), "Deja Vu": ("deja_vu", 0, "divination", "personal", "none"), "Delay Disease": ("delay_disease", 3, "abjuration", "touch", "fort"), "Delay Poison": ("delay_poison", 2, "abjuration", "touch", "fort"), "Detect Fiendish Presence": ("detect_fiendish_presence", 0, "divination", "close", "none"), "Detect Magic": ("detect_magic", 0, "divination", "close", "none"), "Detect Poison": ("detect_poison", 0, "divination", "close", "none"), "Disrupt Undead": ("disrupt_undead", 0, "necromancy", "close", "none"), "Divine Favor": ("divine_favor", 1, "evocation", "personal", "none"), "Doom": ("doom", 1, "necromancy", "close", "will"), "Drunkard's Breath": ("drunkards_breath", 1, "abjuration", "personal", "none"), "Endure Elements": ("endure_elements", 1, "abjuration", "personal", "none"), "Enhance Water": ("enhance_water", 2, "abjuration", "personal", "none"), } _EFFECT_TYPES = ( DamageEffect, ConditionEffect, HealEffect, BuffEffect, AreaEffect, SummonEffect, ) def _walk_effects(effects: tuple[SpellEffect, ...]) -> list[SpellEffect]: flat: list[SpellEffect] = [] for effect in effects: flat.append(effect) if isinstance(effect, AreaEffect): flat.extend(_walk_effects(effect.effects)) return flat class TestBatch1Loading: """Each batch-1 spell loads with the expected metadata.""" @pytest.mark.parametrize( ("name", "stem", "level", "school", "range_", "save"), [ (name, *fields) for name, fields in sorted(_BATCH1.items()) ], ) def test_spell_fields( self, name: str, stem: str, level: int, school: str, range_: str, save: str ) -> None: spell = load_spell(_SPELLS_DIR / f"{stem}.json") assert spell.name == name assert spell.level == level assert spell.school == school assert spell.range == range_ assert spell.save == save assert spell.base_dc == 10 assert spell.description def test_registry_grew_by_exactly_32(self) -> None: reg = load_spell_registry(_SPELLS_DIR) # exact total is pinned by test_spell_registry_batch2 assert len(reg) >= _ORIGINAL_COUNT + 32 assert set(_BATCH1) <= set(reg) def test_every_effect_validates(self) -> None: reg = load_spell_registry(_SPELLS_DIR) for name in _BATCH1: spell: SpellSpec = reg[name] for effect in _walk_effects(spell.effects): assert isinstance(effect, _EFFECT_TYPES) if isinstance(effect, ConditionEffect): assert effect.condition_name in CONDITIONS_BY_NAME if isinstance(effect, BuffEffect): assert effect.modifiers