196 lines
6.5 KiB
Python
196 lines
6.5 KiB
Python
import sqlite3
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from engine.character_creator import CharacterCreator
|
|
from engine.character_library import CharacterLibrary
|
|
from engine.defs import DefRegistry
|
|
|
|
DEFS_DIR = Path(__file__).resolve().parent.parent / "resources" / "defs"
|
|
|
|
|
|
@pytest.fixture()
|
|
def registry():
|
|
return DefRegistry.load(DEFS_DIR)
|
|
|
|
|
|
@pytest.fixture()
|
|
def library():
|
|
return CharacterLibrary(sqlite3.connect(":memory:"))
|
|
|
|
|
|
# --- species / gender selection -----------------------------------------------------------
|
|
|
|
|
|
def test_set_species_defaults_gender_to_the_first_valid_option(registry):
|
|
creator = CharacterCreator(registry)
|
|
assert creator.set_species("human") is True
|
|
assert creator.species_id == "human"
|
|
assert creator.gender == "male"
|
|
|
|
|
|
def test_set_species_rejects_unknown_species(registry):
|
|
creator = CharacterCreator(registry)
|
|
assert creator.set_species("dragon") is False
|
|
assert creator.species_id is None
|
|
|
|
|
|
def test_set_gender_accepts_a_valid_option(registry):
|
|
creator = CharacterCreator(registry)
|
|
creator.set_species("human")
|
|
assert creator.set_gender("nonbinary") is True
|
|
assert creator.gender == "nonbinary"
|
|
|
|
|
|
def test_set_gender_rejects_option_not_valid_for_species(registry):
|
|
creator = CharacterCreator(registry)
|
|
creator.set_species("alien_tri") # genders: alpha/beta/gamma
|
|
assert creator.set_gender("male") is False
|
|
assert creator.gender == "alpha" # unchanged, still the auto-picked default
|
|
|
|
|
|
def test_set_gender_before_species_is_a_no_op(registry):
|
|
creator = CharacterCreator(registry)
|
|
assert creator.set_gender("male") is False
|
|
assert creator.gender is None
|
|
|
|
|
|
def test_switching_species_resets_gender_if_no_longer_valid(registry):
|
|
creator = CharacterCreator(registry)
|
|
creator.set_species("human")
|
|
creator.set_gender("nonbinary")
|
|
creator.set_species("bot") # monogender, "none" - "nonbinary" isn't valid here
|
|
assert creator.gender == "none"
|
|
|
|
|
|
def test_switching_species_keeps_gender_if_still_valid(registry):
|
|
creator = CharacterCreator(registry)
|
|
creator.set_species("human")
|
|
creator.set_gender("female")
|
|
creator.set_species("dog") # dog also has "male"/"female"
|
|
assert creator.gender == "female"
|
|
|
|
|
|
# --- skill point budget ---------------------------------------------------------------------
|
|
|
|
|
|
def test_starts_with_the_full_budget_unspent(registry):
|
|
creator = CharacterCreator(registry)
|
|
assert creator.spent_skill_points() == 0
|
|
assert creator.remaining_skill_points() == creator.skill_points_budget
|
|
|
|
|
|
def test_increase_skill_spends_a_point(registry):
|
|
creator = CharacterCreator(registry)
|
|
assert creator.increase_skill("stealth") is True
|
|
assert creator.bio["stealth"] == 1
|
|
assert creator.remaining_skill_points() == creator.skill_points_budget - 1
|
|
|
|
|
|
def test_increase_skill_fails_once_budget_exhausted(registry):
|
|
creator = CharacterCreator(registry, skill_points_budget=2)
|
|
creator.increase_skill("stealth")
|
|
creator.increase_skill("stealth")
|
|
assert creator.increase_skill("athletics") is False
|
|
assert creator.bio["athletics"] == 0
|
|
|
|
|
|
def test_increase_skill_fails_past_max_level(registry):
|
|
creator = CharacterCreator(registry, skill_points_budget=99, max_skill_level=2)
|
|
creator.increase_skill("stealth")
|
|
creator.increase_skill("stealth")
|
|
assert creator.increase_skill("stealth") is False
|
|
assert creator.bio["stealth"] == 2
|
|
|
|
|
|
def test_increase_skill_rejects_unknown_skill(registry):
|
|
creator = CharacterCreator(registry)
|
|
assert creator.increase_skill("lockpicking") is False
|
|
|
|
|
|
def test_decrease_skill_refunds_a_point(registry):
|
|
creator = CharacterCreator(registry)
|
|
creator.increase_skill("stealth")
|
|
assert creator.decrease_skill("stealth") is True
|
|
assert creator.bio["stealth"] == 0
|
|
assert creator.remaining_skill_points() == creator.skill_points_budget
|
|
|
|
|
|
def test_decrease_skill_below_zero_is_a_no_op(registry):
|
|
creator = CharacterCreator(registry)
|
|
assert creator.decrease_skill("stealth") is False
|
|
|
|
|
|
# --- body part preview ------------------------------------------------------------------------
|
|
|
|
|
|
def test_preview_body_parts_empty_before_species_chosen(registry):
|
|
creator = CharacterCreator(registry)
|
|
assert creator.preview_body_parts() == []
|
|
|
|
|
|
def test_preview_body_parts_matches_species_defaults(registry):
|
|
creator = CharacterCreator(registry)
|
|
creator.set_species("dog")
|
|
slots = {p.slot for p in creator.preview_body_parts()}
|
|
assert slots == {"head", "torso", "front_left_leg", "front_right_leg", "back_left_leg", "back_right_leg"}
|
|
|
|
|
|
# --- readiness / build_character ----------------------------------------------------------------
|
|
|
|
|
|
def test_not_ready_without_name_species_and_gender(registry):
|
|
creator = CharacterCreator(registry)
|
|
assert creator.is_ready() is False
|
|
creator.set_name("Vex")
|
|
assert creator.is_ready() is False
|
|
creator.set_species("human")
|
|
assert creator.is_ready() is True # species auto-fills a valid gender
|
|
|
|
|
|
def test_build_character_returns_none_when_not_ready(registry):
|
|
creator = CharacterCreator(registry)
|
|
creator.set_name("Vex")
|
|
assert creator.build_character() is None
|
|
|
|
|
|
def test_build_character_produces_a_character_with_chosen_traits(registry):
|
|
creator = CharacterCreator(registry)
|
|
creator.set_name(" Vex ")
|
|
creator.set_species("reptilian_quad")
|
|
creator.increase_skill("athletics")
|
|
|
|
character = creator.build_character()
|
|
assert character is not None
|
|
assert character.name == "Vex" # whitespace trimmed
|
|
assert character.species_id == "reptilian_quad"
|
|
assert character.gender == "none"
|
|
assert character.bio["athletics"] == 1
|
|
assert {p.slot for p in character.default_body_parts} == {
|
|
"head", "torso", "left_arm", "right_arm", "left_arm_2", "right_arm_2", "left_leg", "right_leg",
|
|
}
|
|
|
|
|
|
# --- confirm: saves into the library -------------------------------------------------------------
|
|
|
|
|
|
def test_confirm_saves_into_the_library(registry, library):
|
|
creator = CharacterCreator(registry)
|
|
creator.set_name("Vex")
|
|
creator.set_species("human")
|
|
|
|
character = creator.confirm(library, "acct-1", "char-1")
|
|
assert character is not None
|
|
|
|
loaded = library.load_character("acct-1", "char-1", registry)
|
|
assert loaded is not None
|
|
assert loaded.name == "Vex"
|
|
assert loaded.species_id == "human"
|
|
|
|
|
|
def test_confirm_does_nothing_when_not_ready(registry, library):
|
|
creator = CharacterCreator(registry)
|
|
assert creator.confirm(library, "acct-1", "char-1") is None
|
|
assert library.load_character("acct-1", "char-1", registry) is None
|