""" Tests for Research API endpoints. Phase 32: API Endpoint Tests - Tests for research-related API functionality. Tests research_functions.py API methods including quick_summary and deep_research. """ from unittest.mock import MagicMock, patch import pytest class TestInitSearchSystem: """Tests for _init_search_system function.""" @patch("local_deep_research.api.research_functions.get_llm") @patch("local_deep_research.api.research_functions.AdvancedSearchSystem") def test_init_search_system_basic(self, mock_system_class, mock_get_llm): """Test basic initialization of search system.""" from local_deep_research.api.research_functions import ( _init_search_system, ) mock_llm = MagicMock() mock_get_llm.return_value = mock_llm mock_system = MagicMock() mock_system_class.return_value = mock_system result = _init_search_system() mock_get_llm.assert_called_once() mock_system_class.assert_called_once() assert result == mock_system @patch("local_deep_research.api.research_functions.get_llm") @patch("local_deep_research.api.research_functions.AdvancedSearchSystem") def test_init_search_system_with_model_name( self, mock_system_class, mock_get_llm ): """Test initialization with custom model name.""" from local_deep_research.api.research_functions import ( _init_search_system, ) mock_llm = MagicMock() mock_get_llm.return_value = mock_llm mock_system = MagicMock() mock_system_class.return_value = mock_system _init_search_system(model_name="gpt-4") call_kwargs = mock_get_llm.call_args[1] assert call_kwargs.get("model_name") == "gpt-4" @patch("local_deep_research.api.research_functions.get_llm") @patch("local_deep_research.api.research_functions.AdvancedSearchSystem") def test_init_search_system_with_temperature( self, mock_system_class, mock_get_llm ): """Test initialization with custom temperature.""" from local_deep_research.api.research_functions import ( _init_search_system, ) mock_llm = MagicMock() mock_get_llm.return_value = mock_llm mock_system = MagicMock() mock_system_class.return_value = mock_system _init_search_system(temperature=0.5) call_kwargs = mock_get_llm.call_args[1] assert call_kwargs.get("temperature") == 0.5 @patch("local_deep_research.api.research_functions.get_llm") @patch("local_deep_research.api.research_functions.AdvancedSearchSystem") def test_init_search_system_with_provider( self, mock_system_class, mock_get_llm ): """Test initialization with custom provider.""" from local_deep_research.api.research_functions import ( _init_search_system, ) mock_llm = MagicMock() mock_get_llm.return_value = mock_llm mock_system = MagicMock() mock_system_class.return_value = mock_system _init_search_system(provider="anthropic") call_kwargs = mock_get_llm.call_args[1] assert call_kwargs.get("provider") == "anthropic" @patch("local_deep_research.api.research_functions.get_llm") @patch("local_deep_research.api.research_functions.AdvancedSearchSystem") def test_init_search_system_with_iterations( self, mock_system_class, mock_get_llm ): """Test initialization with custom iterations.""" from local_deep_research.api.research_functions import ( _init_search_system, ) mock_llm = MagicMock() mock_get_llm.return_value = mock_llm mock_system = MagicMock() mock_system_class.return_value = mock_system result = _init_search_system(iterations=5) assert result.max_iterations == 5 @patch("local_deep_research.api.research_functions.get_llm") @patch("local_deep_research.api.research_functions.AdvancedSearchSystem") def test_init_search_system_with_questions_per_iteration( self, mock_system_class, mock_get_llm ): """Test initialization with custom questions per iteration.""" from local_deep_research.api.research_functions import ( _init_search_system, ) mock_llm = MagicMock() mock_get_llm.return_value = mock_llm mock_system = MagicMock() mock_system_class.return_value = mock_system result = _init_search_system(questions_per_iteration=3) assert result.questions_per_iteration == 3 @patch("local_deep_research.api.research_functions.get_llm") @patch("local_deep_research.api.research_functions.AdvancedSearchSystem") def test_init_search_system_with_progress_callback( self, mock_system_class, mock_get_llm ): """Test initialization with progress callback.""" from local_deep_research.api.research_functions import ( _init_search_system, ) mock_llm = MagicMock() mock_get_llm.return_value = mock_llm mock_system = MagicMock() mock_system_class.return_value = mock_system callback = MagicMock() _init_search_system(progress_callback=callback) mock_system.set_progress_callback.assert_called_once_with(callback) @patch("local_deep_research.api.research_functions.get_llm") @patch("local_deep_research.api.research_functions.AdvancedSearchSystem") @patch("local_deep_research.api.research_functions.get_search") def test_init_search_system_with_search_tool( self, mock_get_search, mock_system_class, mock_get_llm ): """Test initialization with custom search tool.""" from local_deep_research.api.research_functions import ( _init_search_system, ) mock_llm = MagicMock() mock_get_llm.return_value = mock_llm mock_system = MagicMock() mock_system_class.return_value = mock_system mock_search = MagicMock() mock_get_search.return_value = mock_search _init_search_system(search_tool="arxiv") mock_get_search.assert_called_once() call_args = mock_get_search.call_args[0] assert call_args[0] == "arxiv" @patch("local_deep_research.api.research_functions.get_llm") @patch("local_deep_research.api.research_functions.AdvancedSearchSystem") def test_init_search_system_with_search_strategy( self, mock_system_class, mock_get_llm ): """Test initialization with custom search strategy.""" from local_deep_research.api.research_functions import ( _init_search_system, ) mock_llm = MagicMock() mock_get_llm.return_value = mock_llm mock_system = MagicMock() mock_system_class.return_value = mock_system _init_search_system(search_strategy="modular") call_kwargs = mock_system_class.call_args[1] assert call_kwargs.get("strategy_name") == "modular" @patch("local_deep_research.api.research_functions.get_llm") @patch("local_deep_research.api.research_functions.AdvancedSearchSystem") @patch( "local_deep_research.web_search_engines.retriever_registry.retriever_registry" ) def test_init_search_system_with_retrievers( self, mock_registry, mock_system_class, mock_get_llm ): """Test initialization with custom retrievers.""" from local_deep_research.api.research_functions import ( _init_search_system, ) mock_llm = MagicMock() mock_get_llm.return_value = mock_llm mock_system = MagicMock() mock_system_class.return_value = mock_system retrievers = {"custom": MagicMock()} _init_search_system(retrievers=retrievers) mock_registry.register_multiple.assert_called_once_with(retrievers) @patch("local_deep_research.api.research_functions.get_llm") @patch("local_deep_research.api.research_functions.AdvancedSearchSystem") @patch("local_deep_research.llm.register_llm") def test_init_search_system_with_llms( self, mock_register_llm, mock_system_class, mock_get_llm ): """Test initialization with custom LLMs.""" from local_deep_research.api.research_functions import ( _init_search_system, ) mock_llm = MagicMock() mock_get_llm.return_value = mock_llm mock_system = MagicMock() mock_system_class.return_value = mock_system custom_llm = MagicMock() llms = {"custom_llm": custom_llm} _init_search_system(llms=llms) mock_register_llm.assert_called_once_with("custom_llm", custom_llm) class TestQuickSummary: """Tests for quick_summary function.""" @patch("local_deep_research.api.research_functions._init_search_system") def test_quick_summary_basic(self, mock_init_system): """Test basic quick summary.""" from local_deep_research.api.research_functions import quick_summary mock_system = MagicMock() mock_system.analyze_topic.return_value = { "current_knowledge": "Summary content", "iterations": 1, "questions_by_iteration": {}, "all_links_of_system": [], } mock_init_system.return_value = mock_system result = quick_summary("What is AI?") assert "summary" in result or "current_knowledge" in result mock_system.analyze_topic.assert_called_once() @patch("local_deep_research.api.research_functions._init_search_system") def test_quick_summary_with_provider(self, mock_init_system): """Test quick summary with custom provider in settings_snapshot.""" from local_deep_research.api.research_functions import quick_summary mock_system = MagicMock() mock_system.analyze_topic.return_value = { "current_knowledge": "Summary", "iterations": 1, "questions_by_iteration": {}, "all_links_of_system": [], } mock_init_system.return_value = mock_system quick_summary("Query", provider="anthropic") # Provider is passed via settings_snapshot, not as a direct kwarg call_kwargs = mock_init_system.call_args[1] # Check that settings_snapshot was created and passed assert "settings_snapshot" in call_kwargs @patch("local_deep_research.api.research_functions._init_search_system") def test_quick_summary_with_temperature(self, mock_init_system): """Test quick summary with custom temperature in settings_snapshot.""" from local_deep_research.api.research_functions import quick_summary mock_system = MagicMock() mock_system.analyze_topic.return_value = { "current_knowledge": "Summary", "iterations": 1, "questions_by_iteration": {}, "all_links_of_system": [], } mock_init_system.return_value = mock_system quick_summary("Query", temperature=0.3) # Temperature is passed via settings_snapshot, not as a direct kwarg call_kwargs = mock_init_system.call_args[1] # Check that settings_snapshot was created and passed assert "settings_snapshot" in call_kwargs @patch("local_deep_research.api.research_functions._init_search_system") @patch( "local_deep_research.web_search_engines.retriever_registry.retriever_registry" ) def test_quick_summary_with_retrievers( self, mock_registry, mock_init_system ): """Test quick summary registers retrievers with registry.""" from local_deep_research.api.research_functions import quick_summary mock_system = MagicMock() mock_system.analyze_topic.return_value = { "current_knowledge": "Summary", "iterations": 1, "questions_by_iteration": {}, "all_links_of_system": [], } mock_init_system.return_value = mock_system retrievers = {"custom": MagicMock()} quick_summary("Query", retrievers=retrievers) # Retrievers are registered with the registry, not passed to _init_search_system mock_registry.register_multiple.assert_called_once_with(retrievers) @patch("local_deep_research.api.research_functions._init_search_system") def test_quick_summary_with_research_id(self, mock_init_system): """Test quick summary with research ID tracking.""" from local_deep_research.api.research_functions import quick_summary mock_system = MagicMock() mock_system.analyze_topic.return_value = { "current_knowledge": "Summary", "iterations": 1, "questions_by_iteration": {}, "all_links_of_system": [], } mock_init_system.return_value = mock_system quick_summary("Query", research_id="test-123") call_kwargs = mock_init_system.call_args[1] assert call_kwargs.get("research_id") == "test-123" @patch("local_deep_research.api.research_functions._init_search_system") def test_quick_summary_search_original_query_default( self, mock_init_system ): """Test quick summary search_original_query default is True.""" from local_deep_research.api.research_functions import quick_summary mock_system = MagicMock() mock_system.analyze_topic.return_value = { "current_knowledge": "Summary", "iterations": 1, "questions_by_iteration": {}, "all_links_of_system": [], } mock_init_system.return_value = mock_system quick_summary("Query") call_kwargs = mock_init_system.call_args[1] assert call_kwargs.get("search_original_query") is True @patch("local_deep_research.api.research_functions._init_search_system") def test_quick_summary_search_original_query_false(self, mock_init_system): """Test quick summary with search_original_query disabled.""" from local_deep_research.api.research_functions import quick_summary mock_system = MagicMock() mock_system.analyze_topic.return_value = { "current_knowledge": "Summary", "iterations": 1, "questions_by_iteration": {}, "all_links_of_system": [], } mock_init_system.return_value = mock_system quick_summary("Query", search_original_query=False) call_kwargs = mock_init_system.call_args[1] assert call_kwargs.get("search_original_query") is False class TestResearchAPIValidation: """Tests for API input validation.""" @patch("local_deep_research.api.research_functions._init_search_system") def test_quick_summary_empty_query(self, mock_init_system): """Test quick summary with empty query.""" from local_deep_research.api.research_functions import quick_summary mock_system = MagicMock() mock_system.analyze_topic.return_value = { "current_knowledge": "", "iterations": 0, "questions_by_iteration": {}, "all_links_of_system": [], } mock_init_system.return_value = mock_system # Should not raise, but may return empty results result = quick_summary("") assert result is not None @patch("local_deep_research.api.research_functions._init_search_system") def test_quick_summary_special_characters(self, mock_init_system): """Test quick summary with special characters.""" from local_deep_research.api.research_functions import quick_summary mock_system = MagicMock() mock_system.analyze_topic.return_value = { "current_knowledge": "Result", "iterations": 1, "questions_by_iteration": {}, "all_links_of_system": [], } mock_init_system.return_value = mock_system result = quick_summary("What about ?") assert result is not None @patch("local_deep_research.api.research_functions._init_search_system") def test_quick_summary_unicode_query(self, mock_init_system): """Test quick summary with unicode characters.""" from local_deep_research.api.research_functions import quick_summary mock_system = MagicMock() mock_system.analyze_topic.return_value = { "current_knowledge": "Result", "iterations": 1, "questions_by_iteration": {}, "all_links_of_system": [], } mock_init_system.return_value = mock_system result = quick_summary("什么是人工智能?") assert result is not None class TestResearchAPIErrorHandling: """Tests for API error handling.""" @patch("local_deep_research.api.research_functions._init_search_system") def test_quick_summary_system_error(self, mock_init_system): """Test quick summary handles system errors.""" from local_deep_research.api.research_functions import quick_summary mock_init_system.side_effect = Exception("System error") with pytest.raises(Exception): quick_summary("Query") @patch("local_deep_research.api.research_functions._init_search_system") def test_quick_summary_analyze_error(self, mock_init_system): """Test quick summary handles analyze_topic errors.""" from local_deep_research.api.research_functions import quick_summary mock_system = MagicMock() mock_system.analyze_topic.side_effect = Exception("Analysis error") mock_init_system.return_value = mock_system with pytest.raises(Exception): quick_summary("Query") class TestResearchAPIIntegration: """Integration tests for research API.""" @patch("local_deep_research.api.research_functions._init_search_system") def test_full_research_workflow(self, mock_init_system): """Test complete research workflow.""" from local_deep_research.api.research_functions import quick_summary mock_system = MagicMock() mock_system.analyze_topic.return_value = { "current_knowledge": "Comprehensive research results about AI", "iterations": 3, "questions_by_iteration": {1: ["Q1", "Q2"], 2: ["Q3"], 3: ["Q4"]}, "all_links_of_system": [ {"url": "http://source1.com", "title": "Source 1"}, {"url": "http://source2.com", "title": "Source 2"}, ], } mock_init_system.return_value = mock_system result = quick_summary( "What is artificial intelligence?", provider="openai", temperature=0.7, ) assert result is not None mock_system.analyze_topic.assert_called_once()