Spaces:
Sleeping
Sleeping
| from unittest.mock import AsyncMock, patch | |
| import pytest | |
| from messaging.handler import ClaudeMessageHandler | |
| from messaging.models import IncomingMessage | |
| from messaging.session import SessionStore | |
| from messaging.trees.queue_manager import TreeQueueManager | |
| 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.replace_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" | |
| 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.replace_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" | |