""" Tests for web_search_engines/search_engine_base.py Tests cover: - BaseSearchEngine class attributes - AdaptiveWait strategy - Engine class loading - Search methods """ import pytest from unittest.mock import Mock class TestBaseSearchEngineAttributes: """Tests for BaseSearchEngine class attributes.""" def test_is_public_default(self): """Test default is_public attribute.""" from local_deep_research.web_search_engines.search_engine_base import ( BaseSearchEngine, ) assert BaseSearchEngine.is_public is False def test_is_generic_default(self): """Test default is_generic attribute.""" from local_deep_research.web_search_engines.search_engine_base import ( BaseSearchEngine, ) assert BaseSearchEngine.is_generic is False def test_is_scientific_default(self): """Test default is_scientific attribute.""" from local_deep_research.web_search_engines.search_engine_base import ( BaseSearchEngine, ) assert BaseSearchEngine.is_scientific is False def test_is_local_default(self): """Test default is_local attribute.""" from local_deep_research.web_search_engines.search_engine_base import ( BaseSearchEngine, ) assert BaseSearchEngine.is_local is False def test_is_news_default(self): """Test default is_news attribute.""" from local_deep_research.web_search_engines.search_engine_base import ( BaseSearchEngine, ) assert BaseSearchEngine.is_news is False def test_is_code_default(self): """Test default is_code attribute.""" from local_deep_research.web_search_engines.search_engine_base import ( BaseSearchEngine, ) assert BaseSearchEngine.is_code is False class TestAdaptiveWait: """Tests for AdaptiveWait class.""" def test_adaptive_wait_init(self): """Test AdaptiveWait initialization.""" from local_deep_research.web_search_engines.search_engine_base import ( AdaptiveWait, ) def get_wait(): return 5.0 wait = AdaptiveWait(get_wait) assert wait.get_wait_func == get_wait def test_adaptive_wait_call(self): """Test AdaptiveWait __call__ method.""" from local_deep_research.web_search_engines.search_engine_base import ( AdaptiveWait, ) def get_wait(): return 2.5 wait = AdaptiveWait(get_wait) mock_retry_state = Mock() result = wait(mock_retry_state) assert result == 2.5 class TestLoadEngineClass: """Tests for _load_engine_class method.""" def test_load_engine_class_missing_module_path(self): """Test loading engine with missing module_path.""" from local_deep_research.web_search_engines.search_engine_base import ( BaseSearchEngine, ) config = {"class_name": "SomeClass"} # Missing module_path success, engine_class, error_msg = BaseSearchEngine._load_engine_class( "test_engine", config ) assert success is False assert engine_class is None assert "Missing module_path" in error_msg def test_load_engine_class_missing_class_name(self): """Test loading engine with missing class_name.""" from local_deep_research.web_search_engines.search_engine_base import ( BaseSearchEngine, ) config = {"module_path": "some.module"} # Missing class_name success, engine_class, error_msg = BaseSearchEngine._load_engine_class( "test_engine", config ) assert success is False assert engine_class is None assert "Missing" in error_msg def test_load_engine_class_success(self): """Test successful engine class loading.""" from local_deep_research.web_search_engines.search_engine_base import ( BaseSearchEngine, ) # Use a relative module path (required by the security whitelist) config = { "module_path": ".search_engine_base", "class_name": "BaseSearchEngine", } success, engine_class, error_msg = BaseSearchEngine._load_engine_class( "test_engine", config ) assert success is True assert engine_class is BaseSearchEngine assert error_msg is None def test_load_engine_class_relative_import(self): """Test loading with relative import.""" from local_deep_research.web_search_engines.search_engine_base import ( BaseSearchEngine, ) config = { "module_path": ".search_engine_base", "class_name": "BaseSearchEngine", } success, engine_class, error_msg = BaseSearchEngine._load_engine_class( "test_engine", config ) assert success is True assert engine_class is BaseSearchEngine def test_load_engine_class_nonexistent_module(self): """Test loading with nonexistent module.""" from local_deep_research.web_search_engines.search_engine_base import ( BaseSearchEngine, ) config = { "module_path": "nonexistent.module.path", "class_name": "SomeClass", } success, engine_class, error_msg = BaseSearchEngine._load_engine_class( "test_engine", config ) assert success is False assert engine_class is None assert error_msg is not None class TestConcreteSearchEngine: """Tests using a concrete search engine implementation.""" @pytest.fixture def concrete_engine_class(self): """Create a concrete search engine for testing.""" from local_deep_research.web_search_engines.search_engine_base import ( BaseSearchEngine, ) class ConcreteSearchEngine(BaseSearchEngine): is_public = True is_generic = True def __init__(self, **kwargs): self.llm = kwargs.get("llm") self.max_results = kwargs.get("max_results", 10) def _get_previews(self, query, num_results=None, *args, **kwargs): return [ {"title": "Result 1", "link": "http://example.com/1"}, {"title": "Result 2", "link": "http://example.com/2"}, ] def _get_full_content(self, relevant_items): return [ { "title": r["title"], "link": r["link"], "full_content": "content", } for r in relevant_items ] return ConcreteSearchEngine def test_concrete_engine_class_attributes(self, concrete_engine_class): """Test concrete engine class attributes.""" assert concrete_engine_class.is_public is True assert concrete_engine_class.is_generic is True assert concrete_engine_class.is_scientific is False def test_concrete_engine_init(self, concrete_engine_class): """Test concrete engine initialization.""" engine = concrete_engine_class(max_results=5) assert engine.max_results == 5 def test_concrete_engine_get_previews(self, concrete_engine_class): """Test concrete engine _get_previews.""" engine = concrete_engine_class() results = engine._get_previews("test query") assert len(results) == 2 assert results[0]["title"] == "Result 1" def test_concrete_engine_get_full_content(self, concrete_engine_class): """Test concrete engine _get_full_content.""" engine = concrete_engine_class() items = [{"title": "Test", "link": "http://test.com"}] results = engine._get_full_content(items) assert len(results) == 1 assert "full_content" in results[0]