"""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)