Files
ai-rpg/tests/unit/test_state_validator.py
2026-06-20 19:13:05 +03:00

167 lines
5.4 KiB
Python

"""Tests for `app.core.state_validator`."""
from __future__ import annotations
from app.core.state_validator import apply_patch, validate_state, validate_world
def _character_schema():
return [
{
"name": "player",
"type": "object",
"required": True,
"properties": [
{"name": "name", "type": "string", "required": True},
{
"name": "stats",
"type": "object",
"required": True,
"properties": [
{"name": "health", "type": "integer", "required": True, "min": 0, "max": 100},
{"name": "mana", "type": "integer", "required": False, "min": 0, "max": 100},
],
},
{"name": "inventory", "type": "array", "required": False},
],
},
{"name": "current_location", "type": "string", "required": True},
{
"name": "plot_rails",
"type": "object",
"required": True,
"properties": [
{"name": "hooks", "type": "array", "required": True},
{"name": "current_goals", "type": "array", "required": True},
],
},
]
def test_validate_state_ok():
state = {
"player": {
"name": "Eric",
"stats": {"health": 100, "mana": 10},
"inventory": [],
},
"current_location": "Tavern",
"plot_rails": {"hooks": [], "current_goals": ["survive"]},
}
ok, errors = validate_state(state, _character_schema())
assert ok, errors
def test_validate_state_missing_required():
state = {"player": {}, "current_location": "Tavern"}
ok, errors = validate_state(state, _character_schema())
assert not ok
assert any("player" in e for e in errors) or any("plot_rails" in e for e in errors)
def test_validate_state_wrong_type():
state = {
"player": {"name": "Eric", "stats": {"health": "not a number"}},
"current_location": "Tavern",
"plot_rails": {"hooks": [], "current_goals": []},
}
ok, errors = validate_state(state, _character_schema())
assert not ok
assert any("health" in e for e in errors)
def test_validate_state_range_violation():
state = {
"player": {"name": "Eric", "stats": {"health": 200, "mana": 10}},
"current_location": "Tavern",
"plot_rails": {"hooks": [], "current_goals": []},
}
ok, errors = validate_state(state, _character_schema())
assert not ok
assert any("health" in e for e in errors)
def test_apply_patch_set_value():
state = {"player": {"name": "Eric"}}
new, errors = apply_patch(state, {"player.name": "Erik"})
assert not errors
assert new["player"]["name"] == "Erik"
def test_apply_patch_inc():
state = {"player": {"stats": {"health": 50}}}
new, errors = apply_patch(state, {"player.stats.health": {"op": "inc", "by": -10}})
assert not errors
assert new["player"]["stats"]["health"] == 40
def test_apply_patch_dec():
state = {"player": {"stats": {"mana": 30}}}
new, errors = apply_patch(state, {"player.stats.mana": {"op": "dec", "by": 5}})
assert not errors
assert new["player"]["stats"]["mana"] == 25
def test_apply_patch_append_to_list():
state = {"player": {"inventory": []}}
new, errors = apply_patch(
state,
{"player.inventory": {"op": "append", "value": {"item_id": "sword_01", "qty": 1}}},
)
assert not errors
assert new["player"]["inventory"] == [{"item_id": "sword_01", "qty": 1}]
def test_apply_patch_remove_key():
state = {"player": {"name": "Eric", "backstory": "mysterious"}}
new, errors = apply_patch(state, {"player.backstory": {"op": "remove"}})
assert not errors
assert "backstory" not in new["player"]
def test_apply_patch_creates_nested_path():
state = {"player": {}}
new, errors = apply_patch(state, {"player.stats": {"health": 100, "mana": 10}})
assert not errors
assert new["player"]["stats"]["health"] == 100
def test_apply_patch_does_not_mutate_input():
state = {"player": {"stats": {"health": 100}}}
_ = apply_patch(state, {"player.stats.health": {"op": "inc", "by": -10}})
# Original state unchanged
assert state["player"]["stats"]["health"] == 100
def test_validate_world_ok():
world = {
"name": "Test world",
"language": "en",
"schemas": [{"type": "character", "properties": []}],
"environment_schema": _character_schema(),
"environment": {
"player": {"name": "Eric", "stats": {"health": 100, "mana": 10}, "inventory": []},
"current_location": "Tavern",
"plot_rails": {"hooks": [], "current_goals": [], "completed_goals": []},
},
"plot_rails": {"hooks": [], "current_goals": [], "completed_goals": []},
"current_time": "day_1_hour_8",
}
ok, errors = validate_world(world)
assert ok, errors
def test_validate_world_invalid_time():
world = {
"name": "X",
"language": "en",
"schemas": [],
"environment_schema": [],
"environment": {},
"plot_rails": {"hooks": [], "current_goals": [], "completed_goals": []},
"current_time": "invalid_time",
}
ok, errors = validate_world(world)
assert not ok
assert any("time" in e.lower() for e in errors)