"""Tests for application/api/answer/services/stream_processor.py — get_prompt and helpers. Extended coverage for StreamProcessor including: - get_prompt: all presets and DB fallback - StreamProcessor init, _resolve_agent_id, _get_prompt_content - _get_required_tool_actions - _get_attachments_content: valid, invalid, empty - _configure_retriever - _validate_and_set_model - _get_agent_key - _get_data_from_api_key - _configure_source - pre_fetch_docs """ from unittest.mock import MagicMock, patch import pytest from application.api.answer.services.stream_processor import get_prompt class TestGetPrompt: @pytest.mark.unit def test_default_preset(self): prompt = get_prompt("default") assert isinstance(prompt, str) assert len(prompt) > 0 @pytest.mark.unit def test_creative_preset(self): prompt = get_prompt("creative") assert isinstance(prompt, str) @pytest.mark.unit def test_strict_preset(self): prompt = get_prompt("strict") assert isinstance(prompt, str) @pytest.mark.unit def test_reduce_preset(self): prompt = get_prompt("reduce") assert isinstance(prompt, str) @pytest.mark.unit def test_agentic_default_preset(self): prompt = get_prompt("agentic_default") assert isinstance(prompt, str) @pytest.mark.unit def test_agentic_creative_preset(self): prompt = get_prompt("agentic_creative") assert isinstance(prompt, str) @pytest.mark.unit def test_agentic_strict_preset(self): prompt = get_prompt("agentic_strict") assert isinstance(prompt, str) @pytest.mark.unit def test_mongo_prompt_by_id(self): mock_collection = MagicMock() mock_collection.find_one.return_value = {"_id": "abc", "content": "Custom prompt"} prompt = get_prompt("507f1f77bcf86cd799439011", prompts_collection=mock_collection) assert prompt == "Custom prompt" @pytest.mark.unit def test_mongo_prompt_not_found_raises(self): mock_collection = MagicMock() mock_collection.find_one.return_value = None with pytest.raises(ValueError, match="Invalid prompt ID"): get_prompt("507f1f77bcf86cd799439011", prompts_collection=mock_collection) @pytest.mark.unit def test_invalid_id_raises(self): mock_collection = MagicMock() mock_collection.find_one.side_effect = Exception("bad id") with pytest.raises(ValueError, match="Invalid prompt ID"): get_prompt("not-an-objectid", prompts_collection=mock_collection) @pytest.mark.unit def test_mongo_fallback_when_no_collection(self): """When no collection passed, it reads from MongoDB.""" mock_collection = MagicMock() mock_collection.find_one.return_value = {"content": "From DB"} mock_db = MagicMock() mock_db.__getitem__ = MagicMock(return_value=mock_collection) with patch("application.api.answer.services.stream_processor.MongoDB") as MockMongo, \ patch("application.api.answer.services.stream_processor.settings") as mock_settings: mock_settings.MONGO_DB_NAME = "test_db" MockMongo.get_client.return_value = {"test_db": mock_db} prompt = get_prompt("507f1f77bcf86cd799439011") assert prompt == "From DB" class TestStreamProcessorInit: @pytest.mark.unit def test_init_sets_attributes(self): mock_db = MagicMock() mock_client = {"docsgpt": mock_db} with patch("application.api.answer.services.stream_processor.MongoDB") as MockMongo, \ patch("application.api.answer.services.stream_processor.settings") as mock_settings: mock_settings.MONGO_DB_NAME = "docsgpt" MockMongo.get_client.return_value = mock_client from application.api.answer.services.stream_processor import StreamProcessor sp = StreamProcessor( request_data={"conversation_id": "conv1", "agent_id": "a1"}, decoded_token={"sub": "user1"}, ) assert sp.conversation_id == "conv1" assert sp.initial_user_id == "user1" assert sp.agent_id == "a1" assert sp.history == [] assert sp.attachments == [] @pytest.mark.unit def test_init_no_token(self): mock_db = MagicMock() mock_client = {"docsgpt": mock_db} with patch("application.api.answer.services.stream_processor.MongoDB") as MockMongo, \ patch("application.api.answer.services.stream_processor.settings") as mock_settings: mock_settings.MONGO_DB_NAME = "docsgpt" MockMongo.get_client.return_value = mock_client from application.api.answer.services.stream_processor import StreamProcessor sp = StreamProcessor(request_data={}, decoded_token=None) assert sp.initial_user_id is None @pytest.mark.unit def test_init_default_model_and_config(self): mock_db = MagicMock() with patch("application.api.answer.services.stream_processor.MongoDB") as MockMongo, \ patch("application.api.answer.services.stream_processor.settings") as mock_settings: mock_settings.MONGO_DB_NAME = "docsgpt" MockMongo.get_client.return_value = {"docsgpt": mock_db} from application.api.answer.services.stream_processor import StreamProcessor sp = StreamProcessor(request_data={}, decoded_token={"sub": "u"}) assert sp.model_id is None assert sp.is_shared_usage is False assert sp.shared_token is None assert sp.compressed_summary is None assert sp.compressed_summary_tokens == 0 class TestGetAttachmentsContent: @pytest.mark.unit def test_empty_ids_returns_empty(self): mock_db = MagicMock() with patch("application.api.answer.services.stream_processor.MongoDB") as MockMongo, \ patch("application.api.answer.services.stream_processor.settings") as mock_settings: mock_settings.MONGO_DB_NAME = "docsgpt" MockMongo.get_client.return_value = {"docsgpt": mock_db} from application.api.answer.services.stream_processor import StreamProcessor sp = StreamProcessor(request_data={}, decoded_token={"sub": "u"}) result = sp._get_attachments_content([], "u") assert result == [] @pytest.mark.unit def test_returns_matching_attachments(self): mock_db = MagicMock() mock_attachments = MagicMock() mock_attachments.find_one.return_value = {"_id": "att1", "content": "data"} mock_db.__getitem__ = MagicMock(return_value=mock_attachments) with patch("application.api.answer.services.stream_processor.MongoDB") as MockMongo, \ patch("application.api.answer.services.stream_processor.settings") as mock_settings: mock_settings.MONGO_DB_NAME = "docsgpt" MockMongo.get_client.return_value = {"docsgpt": mock_db} from application.api.answer.services.stream_processor import StreamProcessor sp = StreamProcessor(request_data={}, decoded_token={"sub": "u"}) result = sp._get_attachments_content(["507f1f77bcf86cd799439011"], "u") assert len(result) == 1 @pytest.mark.unit def test_invalid_attachment_id_continues(self): mock_db = MagicMock() mock_attachments = MagicMock() mock_attachments.find_one.side_effect = Exception("bad id") mock_db.__getitem__ = MagicMock(return_value=mock_attachments) with patch("application.api.answer.services.stream_processor.MongoDB") as MockMongo, \ patch("application.api.answer.services.stream_processor.settings") as mock_settings: mock_settings.MONGO_DB_NAME = "docsgpt" MockMongo.get_client.return_value = {"docsgpt": mock_db} from application.api.answer.services.stream_processor import StreamProcessor sp = StreamProcessor(request_data={}, decoded_token={"sub": "u"}) result = sp._get_attachments_content(["bad"], "u") assert result == [] @pytest.mark.unit def test_none_ids_returns_empty(self): mock_db = MagicMock() with patch("application.api.answer.services.stream_processor.MongoDB") as MockMongo, \ patch("application.api.answer.services.stream_processor.settings") as mock_settings: mock_settings.MONGO_DB_NAME = "docsgpt" MockMongo.get_client.return_value = {"docsgpt": mock_db} from application.api.answer.services.stream_processor import StreamProcessor sp = StreamProcessor(request_data={}, decoded_token={"sub": "u"}) result = sp._get_attachments_content(None, "u") assert result == [] class TestResolveAgentId: @pytest.mark.unit def test_from_request_data(self): mock_db = MagicMock() with patch("application.api.answer.services.stream_processor.MongoDB") as MockMongo, \ patch("application.api.answer.services.stream_processor.settings") as mock_settings: mock_settings.MONGO_DB_NAME = "docsgpt" MockMongo.get_client.return_value = {"docsgpt": mock_db} from application.api.answer.services.stream_processor import StreamProcessor sp = StreamProcessor( request_data={"agent_id": "agent_123"}, decoded_token={"sub": "u"}, ) assert sp._resolve_agent_id() == "agent_123" @pytest.mark.unit def test_no_agent_no_conversation(self): mock_db = MagicMock() with patch("application.api.answer.services.stream_processor.MongoDB") as MockMongo, \ patch("application.api.answer.services.stream_processor.settings") as mock_settings: mock_settings.MONGO_DB_NAME = "docsgpt" MockMongo.get_client.return_value = {"docsgpt": mock_db} from application.api.answer.services.stream_processor import StreamProcessor sp = StreamProcessor(request_data={}, decoded_token={"sub": "u"}) assert sp._resolve_agent_id() is None @pytest.mark.unit def test_from_conversation(self): mock_db = MagicMock() with patch("application.api.answer.services.stream_processor.MongoDB") as MockMongo, \ patch("application.api.answer.services.stream_processor.settings") as mock_settings: mock_settings.MONGO_DB_NAME = "docsgpt" MockMongo.get_client.return_value = {"docsgpt": mock_db} from application.api.answer.services.stream_processor import StreamProcessor sp = StreamProcessor( request_data={"conversation_id": "conv1"}, decoded_token={"sub": "u"}, ) sp.conversation_service = MagicMock() sp.conversation_service.get_conversation.return_value = {"agent_id": "from_conv"} assert sp._resolve_agent_id() == "from_conv" @pytest.mark.unit def test_conversation_not_found(self): mock_db = MagicMock() with patch("application.api.answer.services.stream_processor.MongoDB") as MockMongo, \ patch("application.api.answer.services.stream_processor.settings") as mock_settings: mock_settings.MONGO_DB_NAME = "docsgpt" MockMongo.get_client.return_value = {"docsgpt": mock_db} from application.api.answer.services.stream_processor import StreamProcessor sp = StreamProcessor( request_data={"conversation_id": "conv1"}, decoded_token={"sub": "u"}, ) sp.conversation_service = MagicMock() sp.conversation_service.get_conversation.return_value = None assert sp._resolve_agent_id() is None @pytest.mark.unit def test_conversation_lookup_exception(self): mock_db = MagicMock() with patch("application.api.answer.services.stream_processor.MongoDB") as MockMongo, \ patch("application.api.answer.services.stream_processor.settings") as mock_settings: mock_settings.MONGO_DB_NAME = "docsgpt" MockMongo.get_client.return_value = {"docsgpt": mock_db} from application.api.answer.services.stream_processor import StreamProcessor sp = StreamProcessor( request_data={"conversation_id": "conv1"}, decoded_token={"sub": "u"}, ) sp.conversation_service = MagicMock() sp.conversation_service.get_conversation.side_effect = Exception("db error") assert sp._resolve_agent_id() is None @pytest.mark.unit def test_conversation_without_agent_id(self): mock_db = MagicMock() with patch("application.api.answer.services.stream_processor.MongoDB") as MockMongo, \ patch("application.api.answer.services.stream_processor.settings") as mock_settings: mock_settings.MONGO_DB_NAME = "docsgpt" MockMongo.get_client.return_value = {"docsgpt": mock_db} from application.api.answer.services.stream_processor import StreamProcessor sp = StreamProcessor( request_data={"conversation_id": "conv1"}, decoded_token={"sub": "u"}, ) sp.conversation_service = MagicMock() sp.conversation_service.get_conversation.return_value = {"name": "test conv"} assert sp._resolve_agent_id() is None class TestGetPromptContent: @pytest.mark.unit def test_caches_result(self): mock_db = MagicMock() with patch("application.api.answer.services.stream_processor.MongoDB") as MockMongo, \ patch("application.api.answer.services.stream_processor.settings") as mock_settings: mock_settings.MONGO_DB_NAME = "docsgpt" MockMongo.get_client.return_value = {"docsgpt": mock_db} from application.api.answer.services.stream_processor import StreamProcessor sp = StreamProcessor(request_data={}, decoded_token={"sub": "u"}) sp.agent_config = {"prompt_id": "default"} result1 = sp._get_prompt_content() result2 = sp._get_prompt_content() assert result1 == result2 assert result1 is not None @pytest.mark.unit def test_no_prompt_id(self): mock_db = MagicMock() with patch("application.api.answer.services.stream_processor.MongoDB") as MockMongo, \ patch("application.api.answer.services.stream_processor.settings") as mock_settings: mock_settings.MONGO_DB_NAME = "docsgpt" MockMongo.get_client.return_value = {"docsgpt": mock_db} from application.api.answer.services.stream_processor import StreamProcessor sp = StreamProcessor(request_data={}, decoded_token={"sub": "u"}) sp.agent_config = {} assert sp._get_prompt_content() is None @pytest.mark.unit def test_invalid_prompt_id_returns_none(self): mock_db = MagicMock() mock_prompts = MagicMock() mock_prompts.find_one.side_effect = Exception("bad") mock_db.__getitem__ = MagicMock(return_value=mock_prompts) with patch("application.api.answer.services.stream_processor.MongoDB") as MockMongo, \ patch("application.api.answer.services.stream_processor.settings") as mock_settings: mock_settings.MONGO_DB_NAME = "docsgpt" MockMongo.get_client.return_value = {"docsgpt": mock_db} from application.api.answer.services.stream_processor import StreamProcessor sp = StreamProcessor(request_data={}, decoded_token={"sub": "u"}) sp.agent_config = {"prompt_id": "bad_id"} assert sp._get_prompt_content() is None @pytest.mark.unit def test_agent_config_not_dict(self): mock_db = MagicMock() with patch("application.api.answer.services.stream_processor.MongoDB") as MockMongo, \ patch("application.api.answer.services.stream_processor.settings") as mock_settings: mock_settings.MONGO_DB_NAME = "docsgpt" MockMongo.get_client.return_value = {"docsgpt": mock_db} from application.api.answer.services.stream_processor import StreamProcessor sp = StreamProcessor(request_data={}, decoded_token={"sub": "u"}) sp.agent_config = "not_a_dict" assert sp._get_prompt_content() is None class TestGetRequiredToolActions: @pytest.mark.unit def test_no_prompt_returns_none(self): mock_db = MagicMock() with patch("application.api.answer.services.stream_processor.MongoDB") as MockMongo, \ patch("application.api.answer.services.stream_processor.settings") as mock_settings: mock_settings.MONGO_DB_NAME = "docsgpt" MockMongo.get_client.return_value = {"docsgpt": mock_db} from application.api.answer.services.stream_processor import StreamProcessor sp = StreamProcessor(request_data={}, decoded_token={"sub": "u"}) sp.agent_config = {} assert sp._get_required_tool_actions() is None @pytest.mark.unit def test_no_template_syntax_returns_empty(self): mock_db = MagicMock() with patch("application.api.answer.services.stream_processor.MongoDB") as MockMongo, \ patch("application.api.answer.services.stream_processor.settings") as mock_settings: mock_settings.MONGO_DB_NAME = "docsgpt" MockMongo.get_client.return_value = {"docsgpt": mock_db} from application.api.answer.services.stream_processor import StreamProcessor sp = StreamProcessor(request_data={}, decoded_token={"sub": "u"}) sp.agent_config = {"prompt_id": "default"} sp._prompt_content = "No template syntax here" result = sp._get_required_tool_actions() assert result == {} @pytest.mark.unit def test_caches_result(self): mock_db = MagicMock() with patch("application.api.answer.services.stream_processor.MongoDB") as MockMongo, \ patch("application.api.answer.services.stream_processor.settings") as mock_settings: mock_settings.MONGO_DB_NAME = "docsgpt" MockMongo.get_client.return_value = {"docsgpt": mock_db} from application.api.answer.services.stream_processor import StreamProcessor sp = StreamProcessor(request_data={}, decoded_token={"sub": "u"}) sp._required_tool_actions = {"tool1": {"action1"}} result = sp._get_required_tool_actions() assert result == {"tool1": {"action1"}} class TestConfigureRetriever: @pytest.mark.unit def test_default_values(self): mock_db = MagicMock() with patch("application.api.answer.services.stream_processor.MongoDB") as MockMongo, \ patch("application.api.answer.services.stream_processor.settings") as mock_settings: mock_settings.MONGO_DB_NAME = "docsgpt" MockMongo.get_client.return_value = {"docsgpt": mock_db} from application.api.answer.services.stream_processor import StreamProcessor sp = StreamProcessor( request_data={"question": "Q"}, decoded_token={"sub": "u"}, ) sp.model_id = "test-model" sp.agent_key = None sp._configure_retriever() assert sp.retriever_config["retriever_name"] == "classic" assert sp.retriever_config["chunks"] == 2 @pytest.mark.unit def test_isNoneDoc_sets_zero_chunks(self): mock_db = MagicMock() with patch("application.api.answer.services.stream_processor.MongoDB") as MockMongo, \ patch("application.api.answer.services.stream_processor.settings") as mock_settings: mock_settings.MONGO_DB_NAME = "docsgpt" MockMongo.get_client.return_value = {"docsgpt": mock_db} from application.api.answer.services.stream_processor import StreamProcessor sp = StreamProcessor( request_data={"question": "Q", "isNoneDoc": True}, decoded_token={"sub": "u"}, ) sp.model_id = "test-model" sp.agent_key = None sp._configure_retriever() assert sp.retriever_config["chunks"] == 0 @pytest.mark.unit def test_custom_retriever_and_chunks(self): mock_db = MagicMock() with patch("application.api.answer.services.stream_processor.MongoDB") as MockMongo, \ patch("application.api.answer.services.stream_processor.settings") as mock_settings: mock_settings.MONGO_DB_NAME = "docsgpt" MockMongo.get_client.return_value = {"docsgpt": mock_db} from application.api.answer.services.stream_processor import StreamProcessor sp = StreamProcessor( request_data={"question": "Q", "retriever": "hybrid", "chunks": "5"}, decoded_token={"sub": "u"}, ) sp.model_id = "test-model" sp.agent_key = None sp._configure_retriever() assert sp.retriever_config["retriever_name"] == "hybrid" assert sp.retriever_config["chunks"] == 5 class TestConfigureSource: @pytest.mark.unit def test_active_docs_from_request(self): mock_db = MagicMock() with patch("application.api.answer.services.stream_processor.MongoDB") as MockMongo, \ patch("application.api.answer.services.stream_processor.settings") as mock_settings: mock_settings.MONGO_DB_NAME = "docsgpt" MockMongo.get_client.return_value = {"docsgpt": mock_db} from application.api.answer.services.stream_processor import StreamProcessor sp = StreamProcessor( request_data={"question": "Q", "active_docs": "source_123"}, decoded_token={"sub": "u"}, ) sp.agent_key = None sp._configure_source() assert sp.source == {"active_docs": "source_123"} @pytest.mark.unit def test_no_source_config(self): mock_db = MagicMock() with patch("application.api.answer.services.stream_processor.MongoDB") as MockMongo, \ patch("application.api.answer.services.stream_processor.settings") as mock_settings: mock_settings.MONGO_DB_NAME = "docsgpt" MockMongo.get_client.return_value = {"docsgpt": mock_db} from application.api.answer.services.stream_processor import StreamProcessor sp = StreamProcessor( request_data={"question": "Q"}, decoded_token={"sub": "u"}, ) sp.agent_key = None sp._configure_source() assert sp.source == {} assert sp.all_sources == []