free-claude-code/tests/messaging/test_session_store_edge_cases.py
Cursor Agent 4b4f87515d Phase 7: Directory restructuring (messaging/ and tests/)
- Create messaging/platforms/ (base, discord, telegram, factory)
- Create messaging/rendering/ (discord_markdown, telegram_markdown)
- Create messaging/trees/ (data, repository, processor, queue_manager)
- Organize tests/ into api/, providers/, messaging/, cli/, config/
- Add backward-compatible re-exports at old locations
- Update handler.py and test_messaging_factory.py imports
- Fix Telegram type hints for TELEGRAM_AVAILABLE=False case
- Fix Python 3 except syntax in discord_markdown

Co-authored-by: Ali Khokhar <alishahryar2@gmail.com>
2026-02-17 02:25:42 +00:00

167 lines
5.9 KiB
Python

"""Edge case tests for messaging/session.py SessionStore."""
import json
import pytest
from unittest.mock import patch
from messaging.session import SessionStore
@pytest.fixture
def tmp_store(tmp_path):
"""Create a SessionStore using a temp file."""
path = str(tmp_path / "sessions.json")
return SessionStore(storage_path=path)
class TestSessionStoreLoadEdgeCases:
"""Tests for loading corrupted/malformed data."""
def test_load_corrupted_json(self, tmp_path):
"""Corrupted JSON file is handled gracefully (logs error, starts empty)."""
path = str(tmp_path / "sessions.json")
with open(path, "w") as f:
f.write("{invalid json")
store = SessionStore(storage_path=path)
assert len(store._trees) == 0
def test_load_truncated_json(self, tmp_path):
"""Truncated JSON file is handled gracefully."""
path = str(tmp_path / "sessions.json")
with open(path, "w") as f:
f.write('{"sessions": {"s1": {"session_id": "s1"')
store = SessionStore(storage_path=path)
assert len(store._trees) == 0
def test_load_empty_file(self, tmp_path):
"""Empty file is handled gracefully."""
path = str(tmp_path / "sessions.json")
with open(path, "w") as f:
f.write("")
store = SessionStore(storage_path=path)
assert len(store._trees) == 0
def test_load_nonexistent_file(self, tmp_path):
"""Non-existent file starts with empty state."""
path = str(tmp_path / "nonexistent.json")
store = SessionStore(storage_path=path)
assert len(store._trees) == 0
def test_load_legacy_sessions_ignored(self, tmp_path):
"""Legacy sessions in file are ignored; trees and message_log load."""
path = str(tmp_path / "sessions.json")
data = {
"sessions": {
"s1": {
"session_id": "s1",
"chat_id": 12345,
"initial_msg_id": 100,
"last_msg_id": 200,
"platform": "telegram",
"created_at": "2025-01-01T00:00:00+00:00",
"updated_at": "2025-01-01T00:00:00+00:00",
}
},
"trees": {"r1": {"root_id": "r1", "nodes": {"r1": {}}}},
"node_to_tree": {"r1": "r1"},
"message_log": {},
}
with open(path, "w") as f:
json.dump(data, f)
store = SessionStore(storage_path=path)
assert store.get_tree("r1") is not None
class TestSessionStoreSaveEdgeCases:
"""Tests for save failure handling."""
def test_save_io_error_handled(self, tmp_store):
"""Write failure in _save() is logged but doesn't raise."""
tmp_store.save_tree("r1", {"root_id": "r1", "nodes": {"r1": {}}})
with patch("builtins.open", side_effect=IOError("disk full")):
tmp_store._save()
# Should not raise
class TestSessionStoreClearAll:
def test_clear_all_wipes_state_and_persists(self, tmp_path):
path = str(tmp_path / "sessions.json")
store = SessionStore(storage_path=path)
store.save_tree(
"root1",
{
"root_id": "root1",
"nodes": {
"root1": {
"node_id": "root1",
"incoming": {
"text": "hello",
"chat_id": "c1",
"user_id": "u1",
"message_id": "m1",
"platform": "telegram",
"reply_to_message_id": None,
"username": None,
},
"status_message_id": "status1",
"state": "pending",
"parent_id": None,
"session_id": None,
"children_ids": [],
"created_at": "2025-01-01T00:00:00+00:00",
"completed_at": None,
"error_message": None,
}
},
},
)
store.clear_all()
assert store.get_all_trees() == {}
assert store.get_node_mapping() == {}
with open(path, "r", encoding="utf-8") as f:
data = json.load(f)
assert data["trees"] == {}
assert data["node_to_tree"] == {}
assert data["message_log"] == {}
store2 = SessionStore(storage_path=path)
assert len(store2._trees) == 0
def test_message_log_persists_and_dedups(self, tmp_path):
path = str(tmp_path / "sessions.json")
store = SessionStore(storage_path=path)
store.record_message_id("telegram", "c1", "1", direction="in", kind="command")
store.record_message_id("telegram", "c1", "2", direction="out", kind="command")
store.record_message_id("telegram", "c1", "2", direction="out", kind="command")
ids = store.get_message_ids_for_chat("telegram", "c1")
assert ids == ["1", "2"]
store.flush_pending_save()
store2 = SessionStore(storage_path=path)
assert store2.get_message_ids_for_chat("telegram", "c1") == ["1", "2"]
class TestSessionStoreCleanupEdgeCases:
"""Tests for cleanup with malformed data."""
def test_cleanup_trees_malformed_timestamp(self, tmp_store):
"""Malformed created_at in cleanup_old_trees doesn't crash."""
tmp_store._trees["root1"] = {"nodes": {"root1": {"created_at": "bad-date"}}}
removed = tmp_store.cleanup_old_trees(max_age_days=0)
assert removed == 0 # Skipped due to parse error
def test_cleanup_trees_missing_created_at(self, tmp_store):
"""Tree node without created_at is not cleaned up."""
tmp_store._trees["root1"] = {"nodes": {"root1": {}}}
removed = tmp_store.cleanup_old_trees(max_age_days=0)
assert removed == 0