""" Comprehensive tests for ResearchSourcesService. Tests saving, retrieving, and updating research sources. """ from unittest.mock import Mock, patch class TestSaveResearchSources: """Tests for save_research_sources method.""" def test_returns_zero_for_empty_sources(self, sample_research_id): """Test that zero is returned for empty sources list.""" from local_deep_research.web.services.research_sources_service import ( ResearchSourcesService, ) result = ResearchSourcesService.save_research_sources( sample_research_id, [] ) assert result == 0 def test_returns_zero_for_none_sources(self, sample_research_id): """Test that zero is returned for None sources.""" from local_deep_research.web.services.research_sources_service import ( ResearchSourcesService, ) result = ResearchSourcesService.save_research_sources( sample_research_id, None ) assert result == 0 def test_skips_if_resources_already_exist( self, mock_db_session, sample_research_id, sample_sources ): """Test that saving is skipped if resources already exist.""" from local_deep_research.web.services.research_sources_service import ( ResearchSourcesService, ) mock_db_session.query.return_value.filter_by.return_value.count.return_value = 5 with patch( "local_deep_research.web.services.research_sources_service.get_user_db_session" ) as mock_get_session: mock_get_session.return_value.__enter__.return_value = ( mock_db_session ) mock_get_session.return_value.__exit__.return_value = False result = ResearchSourcesService.save_research_sources( sample_research_id, sample_sources ) assert result == 5 mock_db_session.add.assert_not_called() def test_saves_sources_when_none_exist( self, mock_db_session, sample_research_id, sample_sources ): """Test that sources are saved when none exist.""" from local_deep_research.web.services.research_sources_service import ( ResearchSourcesService, ) mock_db_session.query.return_value.filter_by.return_value.count.return_value = 0 with patch( "local_deep_research.web.services.research_sources_service.get_user_db_session" ) as mock_get_session: mock_get_session.return_value.__enter__.return_value = ( mock_db_session ) mock_get_session.return_value.__exit__.return_value = False with patch( "local_deep_research.web.services.research_sources_service.ResearchResource" ): result = ResearchSourcesService.save_research_sources( sample_research_id, sample_sources ) assert result > 0 mock_db_session.commit.assert_called_once() def test_extracts_url_from_url_key( self, mock_db_session, sample_research_id ): """Test extraction of URL from 'url' key.""" from local_deep_research.web.services.research_sources_service import ( ResearchSourcesService, ) sources = [{"url": "https://example.com", "title": "Test"}] mock_db_session.query.return_value.filter_by.return_value.count.return_value = 0 with patch( "local_deep_research.web.services.research_sources_service.get_user_db_session" ) as mock_get_session: mock_get_session.return_value.__enter__.return_value = ( mock_db_session ) mock_get_session.return_value.__exit__.return_value = False with patch( "local_deep_research.web.services.research_sources_service.ResearchResource" ) as mock_resource: ResearchSourcesService.save_research_sources( sample_research_id, sources ) call_kwargs = mock_resource.call_args[1] assert call_kwargs["url"] == "https://example.com" def test_extracts_url_from_link_key( self, mock_db_session, sample_research_id ): """Test extraction of URL from 'link' key.""" from local_deep_research.web.services.research_sources_service import ( ResearchSourcesService, ) sources = [{"link": "https://example.com", "title": "Test"}] mock_db_session.query.return_value.filter_by.return_value.count.return_value = 0 with patch( "local_deep_research.web.services.research_sources_service.get_user_db_session" ) as mock_get_session: mock_get_session.return_value.__enter__.return_value = ( mock_db_session ) mock_get_session.return_value.__exit__.return_value = False with patch( "local_deep_research.web.services.research_sources_service.ResearchResource" ) as mock_resource: ResearchSourcesService.save_research_sources( sample_research_id, sources ) call_kwargs = mock_resource.call_args[1] assert call_kwargs["url"] == "https://example.com" def test_extracts_title_from_title_key( self, mock_db_session, sample_research_id ): """Test extraction of title from 'title' key.""" from local_deep_research.web.services.research_sources_service import ( ResearchSourcesService, ) sources = [{"url": "https://example.com", "title": "My Title"}] mock_db_session.query.return_value.filter_by.return_value.count.return_value = 0 with patch( "local_deep_research.web.services.research_sources_service.get_user_db_session" ) as mock_get_session: mock_get_session.return_value.__enter__.return_value = ( mock_db_session ) mock_get_session.return_value.__exit__.return_value = False with patch( "local_deep_research.web.services.research_sources_service.ResearchResource" ) as mock_resource: ResearchSourcesService.save_research_sources( sample_research_id, sources ) call_kwargs = mock_resource.call_args[1] assert call_kwargs["title"] == "My Title" def test_extracts_title_from_name_key( self, mock_db_session, sample_research_id ): """Test extraction of title from 'name' key.""" from local_deep_research.web.services.research_sources_service import ( ResearchSourcesService, ) sources = [{"url": "https://example.com", "name": "My Name"}] mock_db_session.query.return_value.filter_by.return_value.count.return_value = 0 with patch( "local_deep_research.web.services.research_sources_service.get_user_db_session" ) as mock_get_session: mock_get_session.return_value.__enter__.return_value = ( mock_db_session ) mock_get_session.return_value.__exit__.return_value = False with patch( "local_deep_research.web.services.research_sources_service.ResearchResource" ) as mock_resource: ResearchSourcesService.save_research_sources( sample_research_id, sources ) call_kwargs = mock_resource.call_args[1] assert call_kwargs["title"] == "My Name" def test_uses_untitled_for_missing_title( self, mock_db_session, sample_research_id ): """Test that 'Untitled' is used when title is missing.""" from local_deep_research.web.services.research_sources_service import ( ResearchSourcesService, ) sources = [{"url": "https://example.com"}] mock_db_session.query.return_value.filter_by.return_value.count.return_value = 0 with patch( "local_deep_research.web.services.research_sources_service.get_user_db_session" ) as mock_get_session: mock_get_session.return_value.__enter__.return_value = ( mock_db_session ) mock_get_session.return_value.__exit__.return_value = False with patch( "local_deep_research.web.services.research_sources_service.ResearchResource" ) as mock_resource: ResearchSourcesService.save_research_sources( sample_research_id, sources ) call_kwargs = mock_resource.call_args[1] assert call_kwargs["title"] == "Untitled" def test_skips_sources_without_url( self, mock_db_session, sample_research_id ): """Test that sources without URL are skipped.""" from local_deep_research.web.services.research_sources_service import ( ResearchSourcesService, ) sources = [ {"title": "No URL"}, {"url": "https://example.com", "title": "Has URL"}, ] mock_db_session.query.return_value.filter_by.return_value.count.return_value = 0 with patch( "local_deep_research.web.services.research_sources_service.get_user_db_session" ) as mock_get_session: mock_get_session.return_value.__enter__.return_value = ( mock_db_session ) mock_get_session.return_value.__exit__.return_value = False with patch( "local_deep_research.web.services.research_sources_service.ResearchResource" ): result = ResearchSourcesService.save_research_sources( sample_research_id, sources ) # Only one source should be saved assert result == 1 def test_truncates_long_snippets(self, mock_db_session, sample_research_id): """Test that snippets are truncated to 1000 characters.""" from local_deep_research.web.services.research_sources_service import ( ResearchSourcesService, ) long_snippet = "x" * 2000 sources = [ { "url": "https://example.com", "title": "Test", "snippet": long_snippet, } ] mock_db_session.query.return_value.filter_by.return_value.count.return_value = 0 with patch( "local_deep_research.web.services.research_sources_service.get_user_db_session" ) as mock_get_session: mock_get_session.return_value.__enter__.return_value = ( mock_db_session ) mock_get_session.return_value.__exit__.return_value = False with patch( "local_deep_research.web.services.research_sources_service.ResearchResource" ) as mock_resource: ResearchSourcesService.save_research_sources( sample_research_id, sources ) call_kwargs = mock_resource.call_args[1] assert len(call_kwargs["content_preview"]) == 1000 def test_extracts_snippet_from_multiple_keys( self, mock_db_session, sample_research_id ): """Test extraction of snippet from various keys.""" from local_deep_research.web.services.research_sources_service import ( ResearchSourcesService, ) # Test content_preview key sources = [ {"url": "https://example.com", "content_preview": "Preview text"} ] mock_db_session.query.return_value.filter_by.return_value.count.return_value = 0 with patch( "local_deep_research.web.services.research_sources_service.get_user_db_session" ) as mock_get_session: mock_get_session.return_value.__enter__.return_value = ( mock_db_session ) mock_get_session.return_value.__exit__.return_value = False with patch( "local_deep_research.web.services.research_sources_service.ResearchResource" ) as mock_resource: ResearchSourcesService.save_research_sources( sample_research_id, sources ) call_kwargs = mock_resource.call_args[1] assert call_kwargs["content_preview"] == "Preview text" class TestGetResearchSources: """Tests for get_research_sources method.""" def test_returns_list_of_sources(self, mock_db_session, sample_research_id): """Test that list of sources is returned.""" from local_deep_research.web.services.research_sources_service import ( ResearchSourcesService, ) mock_resource = Mock() mock_resource.id = 1 mock_resource.url = "https://example.com" mock_resource.title = "Test" mock_resource.content_preview = "Preview" mock_resource.source_type = "web" mock_resource.resource_metadata = {} mock_resource.created_at = "2024-01-01" mock_db_session.query.return_value.filter_by.return_value.order_by.return_value.all.return_value = [ mock_resource ] with patch( "local_deep_research.web.services.research_sources_service.get_user_db_session" ) as mock_get_session: mock_get_session.return_value.__enter__.return_value = ( mock_db_session ) mock_get_session.return_value.__exit__.return_value = False result = ResearchSourcesService.get_research_sources( sample_research_id ) assert isinstance(result, list) assert len(result) == 1 def test_returns_correct_fields(self, mock_db_session, sample_research_id): """Test that sources have correct fields.""" from local_deep_research.web.services.research_sources_service import ( ResearchSourcesService, ) mock_resource = Mock() mock_resource.id = 1 mock_resource.url = "https://example.com" mock_resource.title = "Test Title" mock_resource.content_preview = "Preview text" mock_resource.source_type = "web" mock_resource.resource_metadata = {"key": "value"} mock_resource.created_at = "2024-01-01" mock_db_session.query.return_value.filter_by.return_value.order_by.return_value.all.return_value = [ mock_resource ] with patch( "local_deep_research.web.services.research_sources_service.get_user_db_session" ) as mock_get_session: mock_get_session.return_value.__enter__.return_value = ( mock_db_session ) mock_get_session.return_value.__exit__.return_value = False result = ResearchSourcesService.get_research_sources( sample_research_id ) source = result[0] assert source["id"] == 1 assert source["url"] == "https://example.com" assert source["title"] == "Test Title" assert source["snippet"] == "Preview text" assert source["content_preview"] == "Preview text" assert source["source_type"] == "web" assert source["metadata"] == {"key": "value"} def test_returns_empty_list_when_no_sources( self, mock_db_session, sample_research_id ): """Test that empty list is returned when no sources exist.""" from local_deep_research.web.services.research_sources_service import ( ResearchSourcesService, ) mock_db_session.query.return_value.filter_by.return_value.order_by.return_value.all.return_value = [] with patch( "local_deep_research.web.services.research_sources_service.get_user_db_session" ) as mock_get_session: mock_get_session.return_value.__enter__.return_value = ( mock_db_session ) mock_get_session.return_value.__exit__.return_value = False result = ResearchSourcesService.get_research_sources( sample_research_id ) assert result == [] def test_handles_null_metadata(self, mock_db_session, sample_research_id): """Test handling of null resource_metadata.""" from local_deep_research.web.services.research_sources_service import ( ResearchSourcesService, ) mock_resource = Mock() mock_resource.id = 1 mock_resource.url = "https://example.com" mock_resource.title = "Test" mock_resource.content_preview = "Preview" mock_resource.source_type = "web" mock_resource.resource_metadata = None mock_resource.created_at = "2024-01-01" mock_db_session.query.return_value.filter_by.return_value.order_by.return_value.all.return_value = [ mock_resource ] with patch( "local_deep_research.web.services.research_sources_service.get_user_db_session" ) as mock_get_session: mock_get_session.return_value.__enter__.return_value = ( mock_db_session ) mock_get_session.return_value.__exit__.return_value = False result = ResearchSourcesService.get_research_sources( sample_research_id ) assert result[0]["metadata"] == {} def test_passes_username_to_session( self, mock_db_session, sample_research_id ): """Test that username is passed to get_user_db_session.""" from local_deep_research.web.services.research_sources_service import ( ResearchSourcesService, ) mock_db_session.query.return_value.filter_by.return_value.order_by.return_value.all.return_value = [] with patch( "local_deep_research.web.services.research_sources_service.get_user_db_session" ) as mock_get_session: mock_get_session.return_value.__enter__.return_value = ( mock_db_session ) mock_get_session.return_value.__exit__.return_value = False ResearchSourcesService.get_research_sources( sample_research_id, username="testuser" ) mock_get_session.assert_called_once_with("testuser") class TestUpdateResearchWithSources: """Tests for update_research_with_sources method.""" def test_calls_save_research_sources( self, mock_db_session, sample_research_id, sample_sources ): """Test that save_research_sources is called.""" from local_deep_research.web.services.research_sources_service import ( ResearchSourcesService, ) mock_research = Mock() mock_research.research_meta = None mock_db_session.query.return_value.filter_by.return_value.first.return_value = mock_research with patch( "local_deep_research.web.services.research_sources_service.get_user_db_session" ) as mock_get_session: mock_get_session.return_value.__enter__.return_value = ( mock_db_session ) mock_get_session.return_value.__exit__.return_value = False with patch.object( ResearchSourcesService, "save_research_sources", return_value=3, ) as mock_save: ResearchSourcesService.update_research_with_sources( sample_research_id, sample_sources ) mock_save.assert_called_once() def test_updates_research_metadata( self, mock_db_session, sample_research_id, sample_sources ): """Test that research metadata is updated with source count.""" from local_deep_research.web.services.research_sources_service import ( ResearchSourcesService, ) mock_research = Mock() mock_research.research_meta = {} mock_db_session.query.return_value.filter_by.return_value.first.return_value = mock_research with patch( "local_deep_research.web.services.research_sources_service.get_user_db_session" ) as mock_get_session: mock_get_session.return_value.__enter__.return_value = ( mock_db_session ) mock_get_session.return_value.__exit__.return_value = False with patch.object( ResearchSourcesService, "save_research_sources", return_value=3, ): ResearchSourcesService.update_research_with_sources( sample_research_id, sample_sources ) assert mock_research.research_meta["sources_count"] == 3 assert mock_research.research_meta["has_sources"] is True def test_returns_true_on_success( self, mock_db_session, sample_research_id, sample_sources ): """Test that True is returned on success.""" from local_deep_research.web.services.research_sources_service import ( ResearchSourcesService, ) mock_research = Mock() mock_research.research_meta = {} mock_db_session.query.return_value.filter_by.return_value.first.return_value = mock_research with patch( "local_deep_research.web.services.research_sources_service.get_user_db_session" ) as mock_get_session: mock_get_session.return_value.__enter__.return_value = ( mock_db_session ) mock_get_session.return_value.__exit__.return_value = False with patch.object( ResearchSourcesService, "save_research_sources", return_value=3, ): result = ResearchSourcesService.update_research_with_sources( sample_research_id, sample_sources ) assert result is True def test_returns_false_when_research_not_found( self, mock_db_session, sample_research_id, sample_sources ): """Test that False is returned when research not found.""" from local_deep_research.web.services.research_sources_service import ( ResearchSourcesService, ) mock_db_session.query.return_value.filter_by.return_value.first.return_value = None with patch( "local_deep_research.web.services.research_sources_service.get_user_db_session" ) as mock_get_session: mock_get_session.return_value.__enter__.return_value = ( mock_db_session ) mock_get_session.return_value.__exit__.return_value = False with patch.object( ResearchSourcesService, "save_research_sources", return_value=3, ): result = ResearchSourcesService.update_research_with_sources( sample_research_id, sample_sources ) assert result is False def test_returns_false_on_exception( self, mock_db_session, sample_research_id, sample_sources ): """Test that False is returned on exception.""" from local_deep_research.web.services.research_sources_service import ( ResearchSourcesService, ) with patch( "local_deep_research.web.services.research_sources_service.get_user_db_session" ) as mock_get_session: mock_get_session.return_value.__enter__.return_value = ( mock_db_session ) mock_get_session.return_value.__exit__.return_value = False with patch.object( ResearchSourcesService, "save_research_sources", side_effect=Exception("DB error"), ): result = ResearchSourcesService.update_research_with_sources( sample_research_id, sample_sources ) assert result is False def test_initializes_null_metadata( self, mock_db_session, sample_research_id, sample_sources ): """Test that null research_meta is initialized.""" from local_deep_research.web.services.research_sources_service import ( ResearchSourcesService, ) mock_research = Mock() mock_research.research_meta = None mock_db_session.query.return_value.filter_by.return_value.first.return_value = mock_research with patch( "local_deep_research.web.services.research_sources_service.get_user_db_session" ) as mock_get_session: mock_get_session.return_value.__enter__.return_value = ( mock_db_session ) mock_get_session.return_value.__exit__.return_value = False with patch.object( ResearchSourcesService, "save_research_sources", return_value=2, ): ResearchSourcesService.update_research_with_sources( sample_research_id, sample_sources ) assert mock_research.research_meta is not None assert isinstance(mock_research.research_meta, dict)