""" Comprehensive tests for search integration module. Tests NewsSearchCallback, create_search_wrapper, and related functions. """ import pytest from unittest.mock import Mock, patch import uuid class TestNewsSearchCallbackInit: """Tests for NewsSearchCallback initialization.""" def test_initializes_tracking_enabled_to_none(self): """Test _tracking_enabled is None on init.""" from local_deep_research.news.core.search_integration import ( NewsSearchCallback, ) callback = NewsSearchCallback() assert callback._tracking_enabled is None def test_creates_instance(self): """Test can create instance.""" from local_deep_research.news.core.search_integration import ( NewsSearchCallback, ) callback = NewsSearchCallback() assert callback is not None class TestNewsSearchCallbackTrackingEnabled: """Tests for tracking_enabled property.""" def test_returns_false_by_default(self): """Test tracking is disabled by default.""" from local_deep_research.news.core.search_integration import ( NewsSearchCallback, ) callback = NewsSearchCallback() result = callback.tracking_enabled assert result is False def test_caches_value_after_first_call(self): """Test value is cached after first access.""" from local_deep_research.news.core.search_integration import ( NewsSearchCallback, ) callback = NewsSearchCallback() _ = callback.tracking_enabled assert callback._tracking_enabled is not None def test_returns_cached_value_on_subsequent_calls(self): """Test returns cached value on subsequent calls.""" from local_deep_research.news.core.search_integration import ( NewsSearchCallback, ) callback = NewsSearchCallback() result1 = callback.tracking_enabled result2 = callback.tracking_enabled assert result1 == result2 class TestNewsSearchCallbackCall: """Tests for __call__ method.""" def test_callable(self): """Test instance is callable.""" from local_deep_research.news.core.search_integration import ( NewsSearchCallback, ) callback = NewsSearchCallback() assert callable(callback) def test_does_not_track_when_disabled(self): """Test does not track when tracking is disabled.""" from local_deep_research.news.core.search_integration import ( NewsSearchCallback, ) callback = NewsSearchCallback() callback._tracking_enabled = False with patch.object(callback, "_track_user_search") as mock_track: callback("test query", {"findings": []}, {"is_user_search": True}) mock_track.assert_not_called() def test_does_not_track_non_user_searches(self): """Test does not track non-user searches.""" from local_deep_research.news.core.search_integration import ( NewsSearchCallback, ) callback = NewsSearchCallback() callback._tracking_enabled = True with patch.object(callback, "_track_user_search") as mock_track: callback("test query", {"findings": []}, {"is_user_search": False}) mock_track.assert_not_called() def test_tracks_when_enabled_and_user_search(self): """Test tracks when enabled and is_user_search.""" from local_deep_research.news.core.search_integration import ( NewsSearchCallback, ) callback = NewsSearchCallback() callback._tracking_enabled = True with patch.object(callback, "_track_user_search") as mock_track: callback("test query", {"findings": []}, {"is_user_search": True}) mock_track.assert_called_once() def test_defaults_is_user_search_to_true(self): """Test defaults is_user_search to True.""" from local_deep_research.news.core.search_integration import ( NewsSearchCallback, ) callback = NewsSearchCallback() callback._tracking_enabled = True with patch.object(callback, "_track_user_search") as mock_track: callback("test query", {"findings": []}, {}) mock_track.assert_called_once() def test_defaults_user_id_to_anonymous(self): """Test defaults user_id to 'anonymous'.""" from local_deep_research.news.core.search_integration import ( NewsSearchCallback, ) callback = NewsSearchCallback() callback._tracking_enabled = True with patch.object(callback, "_track_user_search") as mock_track: callback("test query", {"findings": []}, {"is_user_search": True}) call_args = mock_track.call_args assert call_args[1]["user_id"] == "anonymous" def test_uses_provided_user_id(self): """Test uses provided user_id from context.""" from local_deep_research.news.core.search_integration import ( NewsSearchCallback, ) callback = NewsSearchCallback() callback._tracking_enabled = True with patch.object(callback, "_track_user_search") as mock_track: callback( "test query", {"findings": []}, {"is_user_search": True, "user_id": "user123"}, ) call_args = mock_track.call_args assert call_args[1]["user_id"] == "user123" def test_generates_search_id_if_not_provided(self): """Test generates search_id if not in context.""" from local_deep_research.news.core.search_integration import ( NewsSearchCallback, ) callback = NewsSearchCallback() callback._tracking_enabled = True with patch.object(callback, "_track_user_search") as mock_track: callback("test query", {"findings": []}, {"is_user_search": True}) call_args = mock_track.call_args # Should be a valid UUID search_id = call_args[1]["search_id"] uuid.UUID(search_id) def test_uses_provided_search_id(self): """Test uses provided search_id from context.""" from local_deep_research.news.core.search_integration import ( NewsSearchCallback, ) callback = NewsSearchCallback() callback._tracking_enabled = True with patch.object(callback, "_track_user_search") as mock_track: callback( "test query", {"findings": []}, {"is_user_search": True, "search_id": "custom-id"}, ) call_args = mock_track.call_args assert call_args[1]["search_id"] == "custom-id" class TestTrackUserSearch: """Tests for _track_user_search method.""" def test_track_user_search_method_exists(self): """Test _track_user_search method exists.""" from local_deep_research.news.core.search_integration import ( NewsSearchCallback, ) callback = NewsSearchCallback() assert hasattr(callback, "_track_user_search") assert callable(callback._track_user_search) class TestCalculateQuality: """Tests for _calculate_quality method.""" def test_returns_float(self): """Test returns a float.""" from local_deep_research.news.core.search_integration import ( NewsSearchCallback, ) callback = NewsSearchCallback() result = callback._calculate_quality({"findings": []}) assert isinstance(result, float) def test_returns_zero_for_no_findings(self): """Test returns 0 when no findings.""" from local_deep_research.news.core.search_integration import ( NewsSearchCallback, ) callback = NewsSearchCallback() result = callback._calculate_quality({"findings": []}) assert result == 0.0 def test_returns_zero_for_missing_findings(self): """Test returns 0 when findings key is missing.""" from local_deep_research.news.core.search_integration import ( NewsSearchCallback, ) callback = NewsSearchCallback() result = callback._calculate_quality({}) assert result == 0.0 def test_returns_higher_for_more_findings(self): """Test returns higher score for more findings.""" from local_deep_research.news.core.search_integration import ( NewsSearchCallback, ) callback = NewsSearchCallback() few_findings = {"findings": [{}] * 3} many_findings = {"findings": [{}] * 10} score_few = callback._calculate_quality(few_findings) score_many = callback._calculate_quality(many_findings) assert score_many > score_few def test_caps_count_score_at_one(self): """Test count score is capped at 1.""" from local_deep_research.news.core.search_integration import ( NewsSearchCallback, ) callback = NewsSearchCallback() # 20 findings should have count_score of 1.0 many_findings = {"findings": [{}] * 20} result = callback._calculate_quality(many_findings) # (1.0 + 0.5) / 2 = 0.75 (no content) assert result <= 1.0 def test_higher_with_content(self): """Test returns higher score when findings have content.""" from local_deep_research.news.core.search_integration import ( NewsSearchCallback, ) callback = NewsSearchCallback() no_content = {"findings": [{}] * 5} with_content = {"findings": [{"content": "text"}] * 5} score_no_content = callback._calculate_quality(no_content) score_with_content = callback._calculate_quality(with_content) assert score_with_content > score_no_content def test_checks_first_five_findings_for_content(self): """Test only checks first 5 findings for content.""" from local_deep_research.news.core.search_integration import ( NewsSearchCallback, ) callback = NewsSearchCallback() # Content in 6th finding shouldn't affect score findings = [{}] * 5 + [{"content": "text"}] * 5 result1 = callback._calculate_quality({"findings": findings}) # Content in first 5 findings2 = [{"content": "text"}] * 5 + [{}] * 5 result2 = callback._calculate_quality({"findings": findings2}) # result2 should be higher (has content in first 5) assert result2 > result1 class TestCreateSearchWrapper: """Tests for create_search_wrapper function.""" def test_function_exists(self): """Test create_search_wrapper function exists.""" from local_deep_research.news.core.search_integration import ( create_search_wrapper, ) assert create_search_wrapper is not None assert callable(create_search_wrapper) def test_returns_callable(self): """Test returns a callable.""" from local_deep_research.news.core.search_integration import ( create_search_wrapper, ) def original(self, query, **kwargs): return {} wrapped = create_search_wrapper(original) assert callable(wrapped) def test_preserves_method_name(self): """Test preserves original method name.""" from local_deep_research.news.core.search_integration import ( create_search_wrapper, ) def original_search(self, query, **kwargs): """Original docstring.""" return {} wrapped = create_search_wrapper(original_search) assert wrapped.__name__ == "original_search" def test_preserves_docstring(self): """Test preserves original docstring.""" from local_deep_research.news.core.search_integration import ( create_search_wrapper, ) def original_search(self, query, **kwargs): """Original docstring.""" return {} wrapped = create_search_wrapper(original_search) assert wrapped.__doc__ == """Original docstring.""" def test_wrapped_function_calls_original(self): """Test wrapped function calls original.""" from local_deep_research.news.core.search_integration import ( create_search_wrapper, ) call_count = [0] def original(self, query, **kwargs): call_count[0] += 1 return {"findings": []} wrapped = create_search_wrapper(original) wrapped(Mock(), "test query") assert call_count[0] == 1 def test_wrapped_function_returns_result(self): """Test wrapped function returns original result.""" from local_deep_research.news.core.search_integration import ( create_search_wrapper, ) def original(self, query, **kwargs): return {"findings": [{"content": "test"}]} wrapped = create_search_wrapper(original) result = wrapped(Mock(), "query") assert result == {"findings": [{"content": "test"}]} def test_strips_special_kwargs(self): """Test strips special kwargs before calling original.""" from local_deep_research.news.core.search_integration import ( create_search_wrapper, ) received_kwargs = [None] def original(self, query, **kwargs): received_kwargs[0] = kwargs return {} wrapped = create_search_wrapper(original) wrapped( Mock(), "query", is_user_search=True, is_news_search=True, user_id="user123", extra_param="value", ) # Special kwargs should be stripped assert "is_user_search" not in received_kwargs[0] assert "is_news_search" not in received_kwargs[0] assert "user_id" not in received_kwargs[0] # Regular kwargs should pass through assert received_kwargs[0].get("extra_param") == "value" class TestSearchIntegrationEdgeCases: """Edge case tests for search integration.""" def test_calculate_quality_with_none_content(self): """Test calculate_quality with None content values.""" from local_deep_research.news.core.search_integration import ( NewsSearchCallback, ) callback = NewsSearchCallback() result = callback._calculate_quality( {"findings": [{"content": None}, {"content": "text"}]} ) assert result > 0 def test_wrapper_with_method_that_raises(self): """Test wrapper handles original method that raises.""" from local_deep_research.news.core.search_integration import ( create_search_wrapper, ) def original_that_raises(self, query, **kwargs): raise ValueError("Search failed") wrapped = create_search_wrapper(original_that_raises) self_obj = Mock() with pytest.raises(ValueError, match="Search failed"): wrapped(self_obj, "query") def test_callback_tracking_enabled_is_boolean(self): """Test tracking_enabled returns boolean.""" from local_deep_research.news.core.search_integration import ( NewsSearchCallback, ) callback = NewsSearchCallback() result = callback.tracking_enabled assert isinstance(result, bool) class TestSearchIntegrationImports: """Tests for module imports.""" def test_imports_create_search_wrapper(self): """Test create_search_wrapper can be imported.""" from local_deep_research.news.core.search_integration import ( create_search_wrapper, ) assert create_search_wrapper is not None assert callable(create_search_wrapper)