2024-03-24 16:56:13 -07:00
|
|
|
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
|
2024-06-30 16:09:20 -07:00
|
|
|
from ttfrog.db.schema.constants import DamageType, Defenses
|
2024-03-24 16:56:13 -07:00
|
|
|
|
2024-03-26 00:53:21 -07:00
|
|
|
FIXTURE_PATH = Path(__file__).parent / "fixtures"
|
2024-03-24 16:56:13 -07:00
|
|
|
|
|
|
|
|
|
|
|
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):
|
2024-03-26 00:53:21 -07:00
|
|
|
monkeypatch.setattr("ttfrog.db.manager.database", MagicMock(return_value=""))
|
|
|
|
monkeypatch.setenv("DATABASE_URL", "sqlite:///:memory:")
|
|
|
|
monkeypatch.setenv("DEBUG", "1")
|
2024-03-24 16:56:13 -07:00
|
|
|
_db.init()
|
2024-04-20 23:27:47 -07:00
|
|
|
yield _db
|
|
|
|
_db.metadata.drop_all(bind=_db.engine)
|
2024-03-24 16:56:13 -07:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
2024-05-04 13:15:54 -07:00
|
|
|
def bootstrap(db):
|
|
|
|
with db.transaction():
|
|
|
|
# ancestries
|
|
|
|
human = schema.Ancestry("human")
|
2024-04-20 20:35:24 -07:00
|
|
|
|
2024-05-04 13:15:54 -07:00
|
|
|
tiefling = schema.Ancestry("tiefling")
|
2024-05-08 01:40:19 -07:00
|
|
|
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)
|
|
|
|
)
|
2024-04-20 20:35:24 -07:00
|
|
|
|
2024-05-04 13:15:54 -07:00
|
|
|
# ancestry traits
|
|
|
|
darkvision = schema.AncestryTrait("Darkvision")
|
|
|
|
darkvision.add_modifier(schema.Modifier("Darkvision", target="vision_in_darkness", absolute_value=120))
|
|
|
|
tiefling.add_trait(darkvision)
|
2024-03-24 16:56:13 -07:00
|
|
|
|
2024-06-30 16:09:20 -07:00
|
|
|
# resistant to fire
|
2024-05-14 20:15:42 -07:00
|
|
|
infernal_origin = schema.AncestryTrait("Infernal Origin")
|
|
|
|
infernal_origin.add_modifier(
|
2024-06-30 16:09:20 -07:00
|
|
|
schema.Modifier("Infernal Origin", target=DamageType.fire, new_value=Defenses.resistant)
|
2024-05-14 20:15:42 -07:00
|
|
|
)
|
|
|
|
tiefling.add_trait(infernal_origin)
|
|
|
|
|
2024-05-04 13:15:54 -07:00
|
|
|
dragonborn = schema.Ancestry("dragonborn")
|
|
|
|
dragonborn.add_trait(darkvision)
|
2024-03-24 16:56:13 -07:00
|
|
|
|
2024-05-04 13:15:54 -07:00
|
|
|
db.add_or_update([human, dragonborn, tiefling])
|
|
|
|
|
2024-05-06 00:13:52 -07:00
|
|
|
# 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])
|
|
|
|
|
2024-05-04 13:15:54 -07:00
|
|
|
# classes
|
2024-07-05 17:45:27 -07:00
|
|
|
fighting_style = schema.ClassFeature("Fighting Style")
|
2024-05-04 13:15:54 -07:00
|
|
|
fighting_style.add_option(name="Archery")
|
|
|
|
fighting_style.add_option(name="Defense")
|
|
|
|
db.add_or_update(fighting_style)
|
|
|
|
|
2024-05-14 20:15:42 -07:00
|
|
|
fighter = schema.CharacterClass(
|
|
|
|
"fighter", hit_die_name="1d10", hit_die_stat_name="_constitution", starting_skills=2
|
|
|
|
)
|
2024-05-06 00:13:52 -07:00
|
|
|
db.add_or_update(fighter)
|
|
|
|
|
|
|
|
# add skills
|
|
|
|
fighter.add_skill(acrobatics)
|
|
|
|
fighter.add_skill(athletics)
|
2024-07-13 12:30:43 -07:00
|
|
|
fighter.add_feature(fighting_style, level=2)
|
2024-05-06 00:13:52 -07:00
|
|
|
db.add_or_update(fighter)
|
|
|
|
assert acrobatics in fighter.skills
|
|
|
|
assert athletics in fighter.skills
|
2024-05-04 13:15:54 -07:00
|
|
|
|
2024-07-30 23:17:20 -07:00
|
|
|
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),
|
|
|
|
]
|
|
|
|
|
2024-05-14 20:15:42 -07:00
|
|
|
rogue = schema.CharacterClass("rogue", hit_die_name="1d8", hit_die_stat_name="_dexterity")
|
2024-07-30 23:17:20 -07:00
|
|
|
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)
|
2024-05-04 13:15:54 -07:00
|
|
|
|
2024-04-20 20:35:24 -07:00
|
|
|
|
2024-07-30 23:17:20 -07:00
|
|
|
@pytest.fixture
|
|
|
|
def carl(db, bootstrap):
|
|
|
|
return db.Character.filter_by(name="Carl").one()
|
2024-04-20 20:35:24 -07:00
|
|
|
|
2024-07-28 13:55:19 -07:00
|
|
|
|
|
|
|
@pytest.fixture
|
2024-07-30 23:17:20 -07:00
|
|
|
def wizard(db, bootstrap):
|
|
|
|
return db.CharacterClass.filter_by(name="wizard").one()
|
|
|
|
|
2024-07-28 20:47:42 -07:00
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
def tiefling(db, bootstrap):
|
|
|
|
return db.Ancestry.filter_by(name="tiefling").one()
|
|
|
|
|
2024-07-30 23:17:20 -07:00
|
|
|
|
2024-07-28 20:47:42 -07:00
|
|
|
@pytest.fixture
|
|
|
|
def human(db, bootstrap):
|
|
|
|
return db.Ancestry.filter_by(name="human").one()
|