Files
odysseus/tests/test_preset_atomic_save.py
T

80 lines
3.0 KiB
Python

"""Regression: PresetManager.save() must persist presets atomically.
save() used a plain open("w") + json.dump, which truncates presets.json before
writing the new content. A crash / power loss / serialization error mid-write
leaves the file truncated or empty — the user loses every saved preset. The
save now goes through core.atomic_io.atomic_write_json (tmp file + os.replace),
which the rest of the codebase already uses for JSON state files.
"""
import inspect
import json
from src.preset_manager import PresetManager
class _Unserializable:
"""json.dump cannot serialize this — stands in for a mid-write failure."""
def test_save_uses_atomic_write_json():
src = inspect.getsource(PresetManager.save)
assert "atomic_write_json" in src, "save() must persist via atomic_write_json"
assert "open(" not in src, "save() must not write presets.json with a plain open('w')"
def test_failed_save_does_not_truncate_existing_file(tmp_path):
mgr = PresetManager(str(tmp_path))
assert mgr.save({"custom": {"name": "keep"}}) is True
before = (tmp_path / "presets.json").read_text(encoding="utf-8")
# A payload that cannot be serialized must not clobber the good file.
assert mgr.save({"custom": {"obj": _Unserializable()}}) is False
after = (tmp_path / "presets.json").read_text(encoding="utf-8")
assert after == before
assert json.loads(after) == {"custom": {"name": "keep"}}
def test_save_round_trip(tmp_path):
mgr = PresetManager(str(tmp_path))
assert mgr.save({"custom": {"name": "X", "temperature": 0.5}}) is True
reloaded = PresetManager(str(tmp_path))
assert reloaded.presets["custom"]["name"] == "X"
def test_custom_persona_memory_follows_persona_lifecycle(tmp_path):
mgr = PresetManager(str(tmp_path))
assert mgr.save({
"custom": {
"name": "Old",
"character_name": "Old",
"persona_memory": "- Existing note",
"enabled": True,
},
"user_templates": [
{
"id": "p1",
"name": "Saved",
"system_prompt": "Be saved.",
"persona_memory": "- Saved note",
"persona_memory_schema": "health",
}
],
})
assert mgr.update_custom(1.0, 0, "Be old.", name="Old", enabled=True)
assert mgr.presets["custom"]["persona_memory"] == "- Existing note"
assert mgr.update_custom(1.0, 0, "Be saved.", name="Saved", enabled=True)
assert mgr.presets["custom"]["persona_memory"] == "- Saved note"
assert mgr.presets["custom"]["persona_memory_schema"] == "health"
assert mgr.update_persona_memory("Saved", "- Updated note")
assert mgr.presets["custom"]["persona_memory"] == "- Updated note"
assert mgr.presets["user_templates"][0]["persona_memory"] == "- Updated note"
assert mgr.update_custom(1.0, 0, "", name="Saved", enabled=False)
assert mgr.presets["custom"]["persona_memory"] == ""
assert mgr.presets["custom"]["persona_memory_schema"] == "general"