main
py 90 lines 2.98 KB
Raw
1 from __future__ import annotations
2
3 import json
4 from pathlib import Path
5 from types import SimpleNamespace
6
7 import pytest
8
9
10 def test_deserialize_log_preserves_item_id() -> None:
11 from helpers.log import Log
12 from helpers.persist_chat import _deserialize_log, _serialize_log
13
14 log = Log()
15 log.log(type="user", heading="User message", content="hello", id="msg-123")
16 log.log(type="assistant", heading="Assistant", content="hi")
17
18 serialized = _serialize_log(log)
19 restored = _deserialize_log(serialized)
20
21 assert restored.logs[0].type == "user"
22 assert restored.logs[0].id == "msg-123"
23 assert restored.logs[1].type == "assistant"
24 assert restored.logs[1].id is None
25
26
27 def test_load_tmp_chats_skips_directories_without_chat_json(monkeypatch, capsys) -> None:
28 from helpers import persist_chat
29
30 monkeypatch.setattr(persist_chat, "_convert_v080_chats", lambda: None)
31 monkeypatch.setattr(
32 persist_chat.files,
33 "get_abs_path",
34 lambda *parts: "/" + "/".join(str(part).strip("/") for part in parts if part),
35 )
36 monkeypatch.setattr(
37 persist_chat.files,
38 "list_files",
39 lambda folder, pattern="*": ["orphan", "valid"],
40 )
41 monkeypatch.setattr(
42 persist_chat.files,
43 "exists",
44 lambda path: str(path).endswith("/valid/chat.json"),
45 )
46 monkeypatch.setattr(
47 persist_chat.files,
48 "read_file",
49 lambda path: json.dumps({"id": "valid"}),
50 )
51 monkeypatch.setattr(
52 persist_chat,
53 "_deserialize_context",
54 lambda data: SimpleNamespace(id=data["id"], data={}),
55 )
56
57 assert persist_chat.load_tmp_chats() == ["valid"]
58 assert "Error loading chat" not in capsys.readouterr().out
59
60
61 def test_save_tmp_chat_preserves_existing_file_until_atomic_replace(
62 monkeypatch, tmp_path
63 ) -> None:
64 from agent import AgentContextType
65 from helpers import persist_chat
66
67 path = tmp_path / "chat.json"
68 path.write_text("previous", encoding="utf-8")
69 context = SimpleNamespace(id="chat", type=AgentContextType.USER, data={})
70
71 monkeypatch.setattr(persist_chat, "_get_chat_file_path", lambda _ctxid: str(path))
72 monkeypatch.setattr(persist_chat, "_serialize_context", lambda _context: {"new": True})
73 real_replace = persist_chat.os.replace
74
75 def interrupted_replace(_source: str, destination: str) -> None:
76 assert Path(destination).read_text(encoding="utf-8") == "previous"
77 raise OSError("simulated interruption")
78
79 monkeypatch.setattr(persist_chat.os, "replace", interrupted_replace)
80 with pytest.raises(OSError, match="simulated interruption"):
81 persist_chat.save_tmp_chat(context)
82
83 assert path.read_text(encoding="utf-8") == "previous"
84 assert list(tmp_path.glob("*.tmp")) == []
85
86 monkeypatch.setattr(persist_chat.os, "replace", real_replace)
87 persist_chat.save_tmp_chat(context)
88
89 assert json.loads(path.read_text(encoding="utf-8")) == {"new": True}
90 assert context.data[persist_chat.SAVED_CHAT_CONTEXT_DATA_KEY] is True