from datetime import datetime from unittest.mock import patch import pytest import pytest_asyncio from langchain_community.indexes._document_manager import MongoDocumentManager @pytest.fixture @pytest.mark.requires("pymongo") def manager() -> MongoDocumentManager: """Initialize the test MongoDB and yield the DocumentManager instance.""" document_manager = MongoDocumentManager( namespace="kittens", mongodb_url="mongodb://langchain:langchain@localhost:6022/", db_name="test_db", collection_name="test_collection", ) return document_manager @pytest_asyncio.fixture @pytest.mark.requires("motor") async def amanager() -> MongoDocumentManager: """Initialize the test MongoDB and yield the DocumentManager instance.""" document_manager = MongoDocumentManager( namespace="kittens", mongodb_url="mongodb://langchain:langchain@localhost:6022/", db_name="test_db", collection_name="test_collection", ) return document_manager @pytest.mark.requires("pymongo") def test_update(manager: MongoDocumentManager) -> None: """Test updating records in the MongoDB.""" read_keys = manager.list_keys() updated_keys = ["update_key1", "update_key2", "update_key3"] manager.update(updated_keys) all_keys = manager.list_keys() assert sorted(all_keys) == sorted(read_keys + updated_keys) @pytest.mark.asyncio @pytest.mark.requires("motor") async def test_aupdate(amanager: MongoDocumentManager) -> None: """Test updating records in the MongoDB.""" read_keys = await amanager.alist_keys() aupdated_keys = ["aupdate_key1", "aupdate_key2", "aupdate_key3"] await amanager.aupdate(aupdated_keys) all_keys = await amanager.alist_keys() assert sorted(all_keys) == sorted(read_keys + aupdated_keys) @pytest.mark.requires("pymongo") def test_update_timestamp(manager: MongoDocumentManager) -> None: """Test updating records with timestamps in MongoDB.""" with patch.object( manager, "get_time", return_value=datetime(2024, 2, 23).timestamp() ): manager.update(["key1"]) records = list( manager.sync_collection.find({"namespace": manager.namespace, "key": "key1"}) ) assert [ { "key": record["key"], "namespace": record["namespace"], "updated_at": record["updated_at"], "group_id": record.get("group_id"), } for record in records ] == [ { "group_id": None, "key": "key1", "namespace": "kittens", "updated_at": datetime(2024, 2, 23).timestamp(), } ] @pytest.mark.requires("motor") async def test_aupdate_timestamp(amanager: MongoDocumentManager) -> None: """Test asynchronously updating records with timestamps in MongoDB.""" with patch.object( amanager, "aget_time", return_value=datetime(2024, 2, 23).timestamp() ): await amanager.aupdate(["key1"]) records = [ doc async for doc in amanager.async_collection.find( {"namespace": amanager.namespace, "key": "key1"} ) ] assert [ { "key": record["key"], "namespace": record["namespace"], "updated_at": record["updated_at"], "group_id": record.get("group_id"), } for record in records ] == [ { "group_id": None, "key": "key1", "namespace": "kittens", "updated_at": datetime(2024, 2, 23).timestamp(), } ] @pytest.mark.requires("pymongo") def test_exists(manager: MongoDocumentManager) -> None: """Test checking if keys exist in MongoDB.""" keys = ["key1", "key2", "key3"] manager.update(keys) exists = manager.exists(keys) assert len(exists) == len(keys) assert all(exists) exists = manager.exists(["key1", "key4"]) assert len(exists) == 2 assert exists == [True, False] @pytest.mark.requires("motor") async def test_aexists(amanager: MongoDocumentManager) -> None: """Test asynchronously checking if keys exist in MongoDB.""" keys = ["key1", "key2", "key3"] await amanager.aupdate(keys) exists = await amanager.aexists(keys) assert len(exists) == len(keys) assert all(exists) exists = await amanager.aexists(["key1", "key4"]) assert len(exists) == 2 assert exists == [True, False] @pytest.mark.requires("pymongo") def test_list_keys(manager: MongoDocumentManager) -> None: """Test listing keys in MongoDB.""" manager.delete_keys(manager.list_keys()) with patch.object( manager, "get_time", return_value=datetime(2021, 1, 1).timestamp() ): manager.update(["key1"]) with patch.object( manager, "get_time", return_value=datetime(2022, 1, 1).timestamp() ): manager.update(["key2"]) with patch.object( manager, "get_time", return_value=datetime(2023, 1, 1).timestamp() ): manager.update(["key3"]) with patch.object( manager, "get_time", return_value=datetime(2024, 1, 1).timestamp() ): manager.update(["key4"], group_ids=["group1"]) assert sorted(manager.list_keys()) == sorted(["key1", "key2", "key3", "key4"]) assert sorted(manager.list_keys(after=datetime(2022, 2, 1).timestamp())) == sorted( ["key3", "key4"] ) assert sorted(manager.list_keys(group_ids=["group1", "group2"])) == sorted(["key4"]) @pytest.mark.requires("motor") async def test_alist_keys(amanager: MongoDocumentManager) -> None: """Test asynchronously listing keys in MongoDB.""" await amanager.adelete_keys(await amanager.alist_keys()) with patch.object( amanager, "aget_time", return_value=datetime(2021, 1, 1).timestamp() ): await amanager.aupdate(["key1"]) with patch.object( amanager, "aget_time", return_value=datetime(2022, 1, 1).timestamp() ): await amanager.aupdate(["key2"]) with patch.object( amanager, "aget_time", return_value=datetime(2023, 1, 1).timestamp() ): await amanager.aupdate(["key3"]) with patch.object( amanager, "aget_time", return_value=datetime(2024, 1, 1).timestamp() ): await amanager.aupdate(["key4"], group_ids=["group1"]) assert sorted(await amanager.alist_keys()) == sorted( ["key1", "key2", "key3", "key4"] ) assert sorted( await amanager.alist_keys(after=datetime(2022, 2, 1).timestamp()) ) == sorted(["key3", "key4"]) assert sorted(await amanager.alist_keys(group_ids=["group1", "group2"])) == sorted( ["key4"] ) @pytest.mark.requires("pymongo") def test_namespace_is_used(manager: MongoDocumentManager) -> None: """Verify that namespace is taken into account for all operations in MongoDB.""" manager.delete_keys(manager.list_keys()) manager.update(["key1", "key2"], group_ids=["group1", "group2"]) manager.sync_collection.insert_many( [ {"key": "key1", "namespace": "puppies", "group_id": None}, {"key": "key3", "namespace": "puppies", "group_id": None}, ] ) assert sorted(manager.list_keys()) == sorted(["key1", "key2"]) manager.delete_keys(["key1"]) assert sorted(manager.list_keys()) == sorted(["key2"]) manager.update(["key3"], group_ids=["group3"]) assert ( manager.sync_collection.find_one({"key": "key3", "namespace": "kittens"})[ "group_id" ] == "group3" ) @pytest.mark.requires("motor") async def test_anamespace_is_used(amanager: MongoDocumentManager) -> None: """ Verify that namespace is taken into account for all operations in MongoDB asynchronously. """ await amanager.adelete_keys(await amanager.alist_keys()) await amanager.aupdate(["key1", "key2"], group_ids=["group1", "group2"]) await amanager.async_collection.insert_many( [ {"key": "key1", "namespace": "puppies", "group_id": None}, {"key": "key3", "namespace": "puppies", "group_id": None}, ] ) assert sorted(await amanager.alist_keys()) == sorted(["key1", "key2"]) await amanager.adelete_keys(["key1"]) assert sorted(await amanager.alist_keys()) == sorted(["key2"]) await amanager.aupdate(["key3"], group_ids=["group3"]) assert ( await amanager.async_collection.find_one( {"key": "key3", "namespace": "kittens"} ) )["group_id"] == "group3" @pytest.mark.requires("pymongo") def test_delete_keys(manager: MongoDocumentManager) -> None: """Test deleting keys from MongoDB.""" manager.update(["key1", "key2", "key3"]) manager.delete_keys(["key1", "key2"]) remaining_keys = manager.list_keys() assert sorted(remaining_keys) == sorted(["key3"]) @pytest.mark.requires("motor") async def test_adelete_keys(amanager: MongoDocumentManager) -> None: """Test asynchronously deleting keys from MongoDB.""" await amanager.aupdate(["key1", "key2", "key3"]) await amanager.adelete_keys(["key1", "key2"]) remaining_keys = await amanager.alist_keys() assert sorted(remaining_keys) == sorted(["key3"])