mirror of
https://github.com/Alishahryar1/free-claude-code.git
synced 2026-04-28 03:20:01 +00:00
- 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>
111 lines
4 KiB
Python
111 lines
4 KiB
Python
import pytest
|
|
from unittest.mock import AsyncMock, patch
|
|
|
|
from messaging.handler import ClaudeMessageHandler
|
|
from messaging.session import SessionStore
|
|
from messaging.tree_queue import TreeQueueManager
|
|
from messaging.models import IncomingMessage
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_reply_to_old_status_message_after_restore_routes_to_parent(
|
|
tmp_path, mock_platform, mock_cli_manager
|
|
):
|
|
# Build a persisted tree with a root node A and a bot status message id.
|
|
store_path = tmp_path / "sessions.json"
|
|
store = SessionStore(storage_path=str(store_path))
|
|
|
|
handler1 = ClaudeMessageHandler(mock_platform, mock_cli_manager, store)
|
|
a_incoming = IncomingMessage(
|
|
text="A",
|
|
chat_id="chat_1",
|
|
user_id="user_1",
|
|
message_id="A",
|
|
platform="telegram",
|
|
)
|
|
tree = await handler1.tree_queue.create_tree(
|
|
"A", a_incoming, status_message_id="status_A"
|
|
)
|
|
handler1.tree_queue.register_node("status_A", tree.root_id)
|
|
store.register_node("status_A", tree.root_id)
|
|
store.save_tree(tree.root_id, tree.to_dict())
|
|
store.flush_pending_save()
|
|
|
|
# "Restart": new store instance loads from disk, and we restore TreeQueueManager.
|
|
store2 = SessionStore(storage_path=str(store_path))
|
|
handler2 = ClaudeMessageHandler(mock_platform, mock_cli_manager, store2)
|
|
handler2.tree_queue = TreeQueueManager.from_dict(
|
|
{"trees": store2.get_all_trees(), "node_to_tree": store2.get_node_mapping()},
|
|
queue_update_callback=handler2._update_queue_positions,
|
|
node_started_callback=handler2._mark_node_processing,
|
|
)
|
|
|
|
# Prevent background task scheduling; we only want to validate routing/tree mutation.
|
|
mock_platform.queue_send_message = AsyncMock(return_value="status_reply")
|
|
|
|
reply = IncomingMessage(
|
|
text="R1",
|
|
chat_id="chat_1",
|
|
user_id="user_1",
|
|
message_id="R1",
|
|
platform="telegram",
|
|
reply_to_message_id="status_A",
|
|
)
|
|
|
|
with patch.object(handler2.tree_queue, "enqueue", AsyncMock(return_value=False)):
|
|
await handler2.handle_message(reply)
|
|
|
|
restored_tree = handler2.tree_queue.get_tree_for_node("A")
|
|
assert restored_tree is not None
|
|
node_r1 = restored_tree.get_node("R1")
|
|
assert node_r1 is not None
|
|
assert node_r1.parent_id == "A"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_reply_to_old_status_message_without_mapping_creates_new_conversation(
|
|
tmp_path, mock_platform, mock_cli_manager
|
|
):
|
|
store_path = tmp_path / "sessions.json"
|
|
store = SessionStore(storage_path=str(store_path))
|
|
|
|
handler1 = ClaudeMessageHandler(mock_platform, mock_cli_manager, store)
|
|
a_incoming = IncomingMessage(
|
|
text="A",
|
|
chat_id="chat_1",
|
|
user_id="user_1",
|
|
message_id="A",
|
|
platform="telegram",
|
|
)
|
|
tree = await handler1.tree_queue.create_tree(
|
|
"A", a_incoming, status_message_id="status_A"
|
|
)
|
|
# Intentionally do NOT register "status_A" mapping.
|
|
store.save_tree(tree.root_id, tree.to_dict())
|
|
store.flush_pending_save()
|
|
|
|
store2 = SessionStore(storage_path=str(store_path))
|
|
handler2 = ClaudeMessageHandler(mock_platform, mock_cli_manager, store2)
|
|
handler2.tree_queue = TreeQueueManager.from_dict(
|
|
{"trees": store2.get_all_trees(), "node_to_tree": store2.get_node_mapping()},
|
|
queue_update_callback=handler2._update_queue_positions,
|
|
node_started_callback=handler2._mark_node_processing,
|
|
)
|
|
mock_platform.queue_send_message = AsyncMock(return_value="status_reply")
|
|
|
|
reply = IncomingMessage(
|
|
text="R1",
|
|
chat_id="chat_1",
|
|
user_id="user_1",
|
|
message_id="R1",
|
|
platform="telegram",
|
|
reply_to_message_id="status_A",
|
|
)
|
|
|
|
with patch.object(handler2.tree_queue, "enqueue", AsyncMock(return_value=False)):
|
|
await handler2.handle_message(reply)
|
|
|
|
# Since the mapping is missing, this should be treated as a new conversation.
|
|
new_tree = handler2.tree_queue.get_tree_for_node("R1")
|
|
assert new_tree is not None
|
|
assert new_tree.root_id == "R1"
|