import json from pathlib import Path from unittest.mock import MagicMock import pytest from ttfrog.db import schema from ttfrog.db.manager import db as _db from ttfrog.db.schema.constants import DamageType, Defenses FIXTURE_PATH = Path(__file__).parent / "fixtures" def load_fixture(db, fixture_name): with db.transaction(): data = json.loads((FIXTURE_PATH / f"{fixture_name}.json").read_text()) for schema_name in data: for record in data[schema_name]: print(f"Loading {schema_name} {record = }") obj = getattr(schema, schema_name)(**record) db.session.add(obj) @pytest.fixture(autouse=True) def db(monkeypatch): monkeypatch.setattr("ttfrog.db.manager.database", MagicMock(return_value="")) monkeypatch.setenv("DATABASE_URL", "sqlite:///:memory:") monkeypatch.setenv("DEBUG", "1") _db.init() yield _db _db.metadata.drop_all(bind=_db.engine) @pytest.fixture def bootstrap(db): with db.transaction(): # ancestries human = schema.Ancestry("human") tiefling = schema.Ancestry("tiefling") tiefling.add_modifier( schema.Modifier("Ability Score Increase", target="intelligence", stacks=True, relative_value=1) ) tiefling.add_modifier( schema.Modifier("Ability Score Increase", target="charisma", stacks=True, relative_value=2) ) # ancestry traits darkvision = schema.AncestryTrait("Darkvision") darkvision.add_modifier(schema.Modifier("Darkvision", target="vision_in_darkness", absolute_value=120)) tiefling.add_trait(darkvision) # resistant to fire infernal_origin = schema.AncestryTrait("Infernal Origin") infernal_origin.add_modifier( schema.Modifier("Infernal Origin", target=DamageType.fire, new_value=Defenses.resistant) ) tiefling.add_trait(infernal_origin) dragonborn = schema.Ancestry("dragonborn") dragonborn.add_trait(darkvision) db.add_or_update([human, dragonborn, tiefling]) # skills skills = { name: schema.Skill(name=name) for name in ("strength", "dexterity", "constitution", "intelligence", "wisdom", "charisma") } db.add_or_update(list(skills.values())) acrobatics = schema.Skill(name="Acrobatics", base_id=skills["dexterity"].id) athletics = schema.Skill(name="Athletics", base_id=skills["strength"].id) db.add_or_update([acrobatics, athletics]) # classes fighting_style = schema.ClassFeature("Fighting Style") fighting_style.add_option(name="Archery") fighting_style.add_option(name="Defense") db.add_or_update(fighting_style) fighter = schema.CharacterClass( "fighter", hit_die_name="1d10", hit_die_stat_name="_constitution", starting_skills=2 ) db.add_or_update(fighter) # add skills fighter.add_skill(acrobatics) fighter.add_skill(athletics) fighter.add_feature(fighting_style, level=2) db.add_or_update(fighter) assert acrobatics in fighter.skills assert athletics in fighter.skills wizard = schema.CharacterClass( "wizard", hit_die_name="1d6", hit_die_stat_name="_intelligence", ) db.add_or_update(wizard) wizard.spell_slots = [ schema.ClassSpellSlotMap(wizard.id, class_level=1, spell_level=1), schema.ClassSpellSlotMap(wizard.id, class_level=1, spell_level=1), schema.ClassSpellSlotMap(wizard.id, class_level=2, spell_level=1), schema.ClassSpellSlotMap(wizard.id, class_level=3, spell_level=1), schema.ClassSpellSlotMap(wizard.id, class_level=3, spell_level=2), schema.ClassSpellSlotMap(wizard.id, class_level=3, spell_level=2), schema.ClassSpellSlotMap(wizard.id, class_level=4, spell_level=2), schema.ClassSpellSlotMap(wizard.id, class_level=5, spell_level=3), schema.ClassSpellSlotMap(wizard.id, class_level=5, spell_level=3), schema.ClassSpellSlotMap(wizard.id, class_level=6, spell_level=3), schema.ClassSpellSlotMap(wizard.id, class_level=7, spell_level=4), ] rogue = schema.CharacterClass("rogue", hit_die_name="1d8", hit_die_stat_name="_dexterity") db.add_or_update([rogue, fighter, wizard]) # create a character: Carl the Wizard carl = schema.Character("Carl", ancestry=tiefling, _intelligence=14) carl.add_class(wizard) db.add_or_update(carl) @pytest.fixture def carl(db, bootstrap): return db.Character.filter_by(name="Carl").one() @pytest.fixture def wizard(db, bootstrap): return db.CharacterClass.filter_by(name="wizard").one() @pytest.fixture def tiefling(db, bootstrap): return db.Ancestry.filter_by(name="tiefling").one() @pytest.fixture def human(db, bootstrap): return db.Ancestry.filter_by(name="human").one()