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 rogue = schema.CharacterClass("rogue", hit_die_name="1d8", hit_die_stat_name="_dexterity") db.add_or_update([rogue, fighter]) # characters foo = schema.Character("Foo", ancestry=tiefling, _intelligence=14) db.add_or_update(foo) foo.add_class(fighter, level=2) foo.add_class(rogue, level=3) bar = schema.Character("Bar", ancestry=human) # persist all the records we've created db.add_or_update([foo, bar]) @pytest.fixture def carl(db, bootstrap): tiefling = db.Ancestry.filter_by(name="tiefling").one() carl = schema.Character(name="Carl", ancestry=tiefling) return carl