""" Extended tests for news/preference_manager/storage.py Tests cover: - SQLPreferenceStorage initialization - CRUD operations (create, read, update, delete) - get_user_preferences() method - upsert_preferences() method - add_liked_item() and add_disliked_item() methods - update_preference_embedding() method - list() with filters """ import pytest from unittest.mock import Mock, MagicMock, patch @pytest.fixture def mock_session(): """Create a mock database session.""" session = MagicMock() session.__enter__ = Mock(return_value=session) session.__exit__ = Mock(return_value=False) return session class TestSQLPreferenceStorageInit: """Tests for SQLPreferenceStorage initialization.""" def test_raises_without_session(self): """Raises ValueError when session is None.""" from local_deep_research.news.preference_manager.storage import ( SQLPreferenceStorage, ) with pytest.raises(ValueError, match="Session is required"): SQLPreferenceStorage(None) def test_stores_session(self, mock_session): """Stores the session.""" from local_deep_research.news.preference_manager.storage import ( SQLPreferenceStorage, ) storage = SQLPreferenceStorage(mock_session) assert storage._session is mock_session def test_session_property(self, mock_session): """Session property returns the session.""" from local_deep_research.news.preference_manager.storage import ( SQLPreferenceStorage, ) storage = SQLPreferenceStorage(mock_session) assert storage.session is mock_session class TestSQLPreferenceStorageCreate: """Tests for create() method.""" def test_creates_preference(self, mock_session): """Creates a preference record.""" from local_deep_research.news.preference_manager.storage import ( SQLPreferenceStorage, ) mock_pref = Mock() mock_pref.id = 1 with patch( "local_deep_research.news.preference_manager.storage.UserPreference" ) as MockPref: MockPref.return_value = mock_pref mock_session.add = Mock() mock_session.commit = Mock() storage = SQLPreferenceStorage(mock_session) result = storage.create({"user_id": "user1"}) assert result == "1" def test_create_with_all_fields(self, mock_session): """Creates preference with all fields.""" from local_deep_research.news.preference_manager.storage import ( SQLPreferenceStorage, ) mock_pref = Mock() mock_pref.id = 2 with patch( "local_deep_research.news.preference_manager.storage.UserPreference" ) as MockPref: MockPref.return_value = mock_pref mock_session.add = Mock() mock_session.commit = Mock() storage = SQLPreferenceStorage(mock_session) data = { "user_id": "user1", "liked_categories": ["tech", "science"], "disliked_categories": ["sports"], "liked_topics": ["AI"], "disliked_topics": ["politics"], "impact_threshold": 7, "focus_preferences": {"topic": "tech"}, "custom_prompt": "My prompt", "custom_search_terms": "AI ML", "preference_embedding": [0.1, 0.2], "liked_news_ids": ["id1"], "disliked_news_ids": ["id2"], } result = storage.create(data) assert result == "2" MockPref.assert_called_once() class TestSQLPreferenceStorageGet: """Tests for get() method.""" def test_get_existing_preference(self, mock_session): """Gets existing preference by ID.""" from local_deep_research.news.preference_manager.storage import ( SQLPreferenceStorage, ) mock_pref = Mock() mock_pref.to_dict.return_value = {"id": 1, "user_id": "user1"} mock_session.query.return_value.filter_by.return_value.first.return_value = mock_pref storage = SQLPreferenceStorage(mock_session) result = storage.get("1") assert result["id"] == 1 def test_get_nonexistent_preference(self, mock_session): """Returns None for nonexistent preference.""" from local_deep_research.news.preference_manager.storage import ( SQLPreferenceStorage, ) mock_session.query.return_value.filter_by.return_value.first.return_value = None storage = SQLPreferenceStorage(mock_session) result = storage.get("999") assert result is None class TestSQLPreferenceStorageUpdate: """Tests for update() method.""" def test_update_existing_preference(self, mock_session): """Updates existing preference.""" from local_deep_research.news.preference_manager.storage import ( SQLPreferenceStorage, ) mock_pref = Mock() mock_pref.liked_categories = [] mock_session.query.return_value.filter_by.return_value.first.return_value = mock_pref mock_session.commit = Mock() storage = SQLPreferenceStorage(mock_session) result = storage.update("1", {"liked_categories": ["tech"]}) assert result is True def test_update_nonexistent_preference(self, mock_session): """Returns False for nonexistent preference.""" from local_deep_research.news.preference_manager.storage import ( SQLPreferenceStorage, ) mock_session.query.return_value.filter_by.return_value.first.return_value = None storage = SQLPreferenceStorage(mock_session) result = storage.update("999", {"liked_categories": ["tech"]}) assert result is False class TestSQLPreferenceStorageDelete: """Tests for delete() method.""" def test_delete_existing_preference(self, mock_session): """Deletes existing preference.""" from local_deep_research.news.preference_manager.storage import ( SQLPreferenceStorage, ) mock_pref = Mock() mock_session.query.return_value.filter_by.return_value.first.return_value = mock_pref mock_session.delete = Mock() mock_session.commit = Mock() storage = SQLPreferenceStorage(mock_session) result = storage.delete("1") assert result is True mock_session.delete.assert_called_once_with(mock_pref) def test_delete_nonexistent_preference(self, mock_session): """Returns False for nonexistent preference.""" from local_deep_research.news.preference_manager.storage import ( SQLPreferenceStorage, ) mock_session.query.return_value.filter_by.return_value.first.return_value = None storage = SQLPreferenceStorage(mock_session) result = storage.delete("999") assert result is False class TestSQLPreferenceStorageList: """Tests for list() method.""" def test_list_all_preferences(self, mock_session): """Lists all preferences.""" from local_deep_research.news.preference_manager.storage import ( SQLPreferenceStorage, ) mock_pref1 = Mock() mock_pref1.to_dict.return_value = {"id": 1, "user_id": "user1"} mock_pref2 = Mock() mock_pref2.to_dict.return_value = {"id": 2, "user_id": "user2"} mock_query = MagicMock() mock_query.limit.return_value.offset.return_value.all.return_value = [ mock_pref1, mock_pref2, ] mock_session.query.return_value = mock_query storage = SQLPreferenceStorage(mock_session) result = storage.list() assert len(result) == 2 def test_list_with_user_filter(self, mock_session): """Lists preferences filtered by user_id.""" from local_deep_research.news.preference_manager.storage import ( SQLPreferenceStorage, ) mock_pref = Mock() mock_pref.to_dict.return_value = {"id": 1, "user_id": "user1"} mock_query = MagicMock() mock_query.filter_by.return_value.limit.return_value.offset.return_value.all.return_value = [ mock_pref ] mock_session.query.return_value = mock_query storage = SQLPreferenceStorage(mock_session) result = storage.list(filters={"user_id": "user1"}) assert len(result) == 1 def test_list_with_limit(self, mock_session): """Lists preferences with limit.""" from local_deep_research.news.preference_manager.storage import ( SQLPreferenceStorage, ) mock_query = MagicMock() mock_query.limit.return_value.offset.return_value.all.return_value = [] mock_session.query.return_value = mock_query storage = SQLPreferenceStorage(mock_session) storage.list(limit=10) mock_query.limit.assert_called_with(10) def test_list_with_offset(self, mock_session): """Lists preferences with offset.""" from local_deep_research.news.preference_manager.storage import ( SQLPreferenceStorage, ) mock_query = MagicMock() mock_query.limit.return_value.offset.return_value.all.return_value = [] mock_session.query.return_value = mock_query storage = SQLPreferenceStorage(mock_session) storage.list(offset=5) mock_query.limit.return_value.offset.assert_called_with(5) class TestGetUserPreferences: """Tests for get_user_preferences() method.""" def test_get_existing_user_preferences(self, mock_session): """Gets preferences for existing user.""" from local_deep_research.news.preference_manager.storage import ( SQLPreferenceStorage, ) mock_pref = Mock() mock_pref.to_dict.return_value = { "id": 1, "user_id": "user1", "liked_categories": ["tech"], } mock_session.query.return_value.filter_by.return_value.first.return_value = mock_pref storage = SQLPreferenceStorage(mock_session) result = storage.get_user_preferences("user1") assert result["user_id"] == "user1" def test_get_nonexistent_user_preferences(self, mock_session): """Returns None for user without preferences.""" from local_deep_research.news.preference_manager.storage import ( SQLPreferenceStorage, ) mock_session.query.return_value.filter_by.return_value.first.return_value = None storage = SQLPreferenceStorage(mock_session) result = storage.get_user_preferences("new_user") assert result is None class TestUpsertPreferences: """Tests for upsert_preferences() method.""" def test_creates_when_not_exists(self, mock_session): """Creates preferences when user has none.""" from local_deep_research.news.preference_manager.storage import ( SQLPreferenceStorage, ) mock_session.query.return_value.filter_by.return_value.first.return_value = None mock_pref = Mock() mock_pref.id = 1 with patch( "local_deep_research.news.preference_manager.storage.UserPreference" ) as MockPref: MockPref.return_value = mock_pref mock_session.add = Mock() mock_session.commit = Mock() storage = SQLPreferenceStorage(mock_session) result = storage.upsert_preferences( "new_user", {"liked_categories": ["tech"]} ) assert result == "1" def test_updates_when_exists(self, mock_session): """Updates preferences when user has existing ones.""" from local_deep_research.news.preference_manager.storage import ( SQLPreferenceStorage, ) mock_pref = Mock() mock_pref.id = 1 mock_pref.to_dict.return_value = {"id": 1, "user_id": "user1"} mock_pref.liked_categories = [] mock_session.query.return_value.filter_by.return_value.first.return_value = mock_pref mock_session.commit = Mock() storage = SQLPreferenceStorage(mock_session) result = storage.upsert_preferences( "user1", {"liked_categories": ["tech"]} ) assert result == "1" class TestAddLikedItem: """Tests for add_liked_item() method.""" def test_adds_liked_news_to_existing_prefs(self, mock_session): """Adds liked news item to existing preferences.""" from local_deep_research.news.preference_manager.storage import ( SQLPreferenceStorage, ) mock_pref = Mock() mock_pref.liked_news_ids = ["id1"] mock_session.query.return_value.filter_by.return_value.first.return_value = mock_pref mock_session.commit = Mock() storage = SQLPreferenceStorage(mock_session) result = storage.add_liked_item("user1", "id2", item_type="news") assert result is True assert "id2" in mock_pref.liked_news_ids def test_creates_prefs_when_none_exist(self, mock_session): """Creates preferences when user has none.""" from local_deep_research.news.preference_manager.storage import ( SQLPreferenceStorage, ) mock_session.query.return_value.filter_by.return_value.first.return_value = None mock_session.add = Mock() mock_session.commit = Mock() with patch( "local_deep_research.news.preference_manager.storage.UserPreference" ): storage = SQLPreferenceStorage(mock_session) result = storage.add_liked_item("new_user", "id1", item_type="news") assert result is True mock_session.add.assert_called_once() def test_does_not_add_duplicate(self, mock_session): """Does not add duplicate liked item.""" from local_deep_research.news.preference_manager.storage import ( SQLPreferenceStorage, ) mock_pref = Mock() mock_pref.liked_news_ids = ["id1"] mock_session.query.return_value.filter_by.return_value.first.return_value = mock_pref mock_session.commit = Mock() storage = SQLPreferenceStorage(mock_session) storage.add_liked_item("user1", "id1", item_type="news") # Should still be only one item assert mock_pref.liked_news_ids.count("id1") == 1 def test_limits_to_100_items(self, mock_session): """Limits liked items to last 100.""" from local_deep_research.news.preference_manager.storage import ( SQLPreferenceStorage, ) mock_pref = Mock() mock_pref.liked_news_ids = [f"id{i}" for i in range(100)] mock_session.query.return_value.filter_by.return_value.first.return_value = mock_pref mock_session.commit = Mock() storage = SQLPreferenceStorage(mock_session) storage.add_liked_item("user1", "new_id", item_type="news") assert len(mock_pref.liked_news_ids) == 100 class TestAddDislikedItem: """Tests for add_disliked_item() method.""" def test_adds_disliked_news_to_existing_prefs(self, mock_session): """Adds disliked news item to existing preferences.""" from local_deep_research.news.preference_manager.storage import ( SQLPreferenceStorage, ) mock_pref = Mock() mock_pref.disliked_news_ids = ["id1"] mock_session.query.return_value.filter_by.return_value.first.return_value = mock_pref mock_session.commit = Mock() storage = SQLPreferenceStorage(mock_session) result = storage.add_disliked_item("user1", "id2", item_type="news") assert result is True assert "id2" in mock_pref.disliked_news_ids def test_creates_prefs_when_none_exist_dislike(self, mock_session): """Creates preferences when user has none for dislike.""" from local_deep_research.news.preference_manager.storage import ( SQLPreferenceStorage, ) mock_session.query.return_value.filter_by.return_value.first.return_value = None mock_session.add = Mock() mock_session.commit = Mock() with patch( "local_deep_research.news.preference_manager.storage.UserPreference" ): storage = SQLPreferenceStorage(mock_session) result = storage.add_disliked_item( "new_user", "id1", item_type="news" ) assert result is True mock_session.add.assert_called_once() def test_limits_disliked_to_100_items(self, mock_session): """Limits disliked items to last 100.""" from local_deep_research.news.preference_manager.storage import ( SQLPreferenceStorage, ) mock_pref = Mock() mock_pref.disliked_news_ids = [f"id{i}" for i in range(100)] mock_session.query.return_value.filter_by.return_value.first.return_value = mock_pref mock_session.commit = Mock() storage = SQLPreferenceStorage(mock_session) storage.add_disliked_item("user1", "new_id", item_type="news") assert len(mock_pref.disliked_news_ids) == 100 class TestUpdatePreferenceEmbedding: """Tests for update_preference_embedding() method.""" def test_updates_existing_embedding(self, mock_session): """Updates embedding for existing preferences.""" from local_deep_research.news.preference_manager.storage import ( SQLPreferenceStorage, ) mock_pref = Mock() mock_pref.preference_embedding = None mock_session.query.return_value.filter_by.return_value.first.return_value = mock_pref mock_session.commit = Mock() storage = SQLPreferenceStorage(mock_session) result = storage.update_preference_embedding("user1", [0.1, 0.2, 0.3]) assert result is True assert mock_pref.preference_embedding == [0.1, 0.2, 0.3] def test_creates_prefs_for_embedding(self, mock_session): """Creates preferences when user has none for embedding.""" from local_deep_research.news.preference_manager.storage import ( SQLPreferenceStorage, ) mock_session.query.return_value.filter_by.return_value.first.return_value = None mock_session.add = Mock() mock_session.commit = Mock() with patch( "local_deep_research.news.preference_manager.storage.UserPreference" ): storage = SQLPreferenceStorage(mock_session) result = storage.update_preference_embedding("new_user", [0.1, 0.2]) assert result is True mock_session.add.assert_called_once() class TestPreferenceStorageEdgeCases: """Edge case tests for SQLPreferenceStorage.""" def test_empty_user_id(self, mock_session): """Handles empty user_id.""" from local_deep_research.news.preference_manager.storage import ( SQLPreferenceStorage, ) mock_session.query.return_value.filter_by.return_value.first.return_value = None storage = SQLPreferenceStorage(mock_session) result = storage.get_user_preferences("") assert result is None def test_unicode_user_id(self, mock_session): """Handles unicode user_id.""" from local_deep_research.news.preference_manager.storage import ( SQLPreferenceStorage, ) mock_pref = Mock() mock_pref.to_dict.return_value = {"user_id": "用户"} mock_session.query.return_value.filter_by.return_value.first.return_value = mock_pref storage = SQLPreferenceStorage(mock_session) result = storage.get_user_preferences("用户") assert result is not None def test_empty_embedding_list(self, mock_session): """Handles empty embedding list.""" from local_deep_research.news.preference_manager.storage import ( SQLPreferenceStorage, ) mock_pref = Mock() mock_session.query.return_value.filter_by.return_value.first.return_value = mock_pref mock_session.commit = Mock() storage = SQLPreferenceStorage(mock_session) result = storage.update_preference_embedding("user1", []) assert result is True def test_none_liked_news_ids(self, mock_session): """Handles None liked_news_ids.""" from local_deep_research.news.preference_manager.storage import ( SQLPreferenceStorage, ) mock_pref = Mock() mock_pref.liked_news_ids = None mock_session.query.return_value.filter_by.return_value.first.return_value = mock_pref mock_session.commit = Mock() storage = SQLPreferenceStorage(mock_session) result = storage.add_liked_item("user1", "id1", item_type="news") assert result is True