55import os
66from unittest .mock import patch , MagicMock
77import argparse
8+ from mcp .server .fastmcp .exceptions import ToolError # Import ToolError
9+ import json # Import json for parsing results
10+
811
912# Add pytest-asyncio marker
1013pytest_plugins = ['pytest_asyncio' ]
@@ -261,4 +264,150 @@ def test_required_args_for_cloud_client():
261264 # Check that error was called for missing api-key (the first check in the code)
262265 mock_error .assert_called_with (
263266 "API key must be provided via --api-key flag or CHROMA_API_KEY environment variable when using cloud client"
264- )
267+ )
268+
269+ # --- Tests for chroma_update_documents ---
270+
271+ @pytest .mark .asyncio
272+ async def test_update_documents_success ():
273+ """Test successful document update."""
274+ collection_name = "test_update_collection_success"
275+ doc_ids = ["doc1" , "doc2" ]
276+ initial_docs = ["Initial doc 1" , "Initial doc 2" ]
277+ initial_metadatas = [{"source" : "initial" }, {"source" : "initial" }]
278+ updated_docs = ["Updated doc 1" , initial_docs [1 ]] # Update only first doc content
279+ updated_metadatas = [initial_metadatas [0 ], {"source" : "updated" }] # Update only second doc metadata
280+
281+ try :
282+ # 1. Create collection
283+ await mcp .call_tool ("chroma_create_collection" , {"collection_name" : collection_name })
284+
285+ # 2. Add initial documents
286+ await mcp .call_tool ("chroma_add_documents" , {
287+ "collection_name" : collection_name ,
288+ "documents" : initial_docs ,
289+ "metadatas" : initial_metadatas ,
290+ "ids" : doc_ids
291+ })
292+
293+ # 3. Update documents (pass both documents and metadatas)
294+ update_result = await mcp .call_tool ("chroma_update_documents" , {
295+ "collection_name" : collection_name ,
296+ "ids" : doc_ids ,
297+ "documents" : updated_docs ,
298+ "metadatas" : updated_metadatas
299+ })
300+ assert len (update_result ) == 1
301+ # Updated success message check
302+ assert (
303+ f"Successfully processed update request for { len (doc_ids )} documents"
304+ in update_result [0 ].text
305+ )
306+
307+ # 4. Verify updates
308+ get_result_raw = await mcp .call_tool ("chroma_get_documents" , {
309+ "collection_name" : collection_name ,
310+ "ids" : doc_ids ,
311+ "include" : ["documents" , "metadatas" ]
312+ })
313+ # Corrected: Parse the JSON string from TextContent
314+ assert len (get_result_raw ) == 1
315+ get_result = json .loads (get_result_raw [0 ].text )
316+ assert isinstance (get_result , dict )
317+
318+ assert get_result .get ("ids" ) == doc_ids
319+ # Check updated document content
320+ assert get_result .get ("documents" ) == updated_docs
321+ # Check updated metadata
322+ assert get_result .get ("metadatas" ) == updated_metadatas
323+
324+ finally :
325+ # Clean up
326+ await mcp .call_tool ("chroma_delete_collection" , {"collection_name" : collection_name })
327+
328+ @pytest .mark .asyncio
329+ async def test_update_documents_invalid_args ():
330+ """Test update documents with invalid arguments."""
331+ collection_name = "test_update_collection_invalid"
332+
333+ try :
334+ await mcp .call_tool ("chroma_create_collection" , {"collection_name" : collection_name })
335+ await mcp .call_tool ("chroma_add_documents" , {
336+ "collection_name" : collection_name ,
337+ "documents" : ["Test doc" ],
338+ "ids" : ["doc1" ]
339+ })
340+
341+ # Test with empty IDs list - Expect ToolError wrapping ValueError
342+ with pytest .raises (ToolError , match = "The 'ids' list cannot be empty." ):
343+ await mcp .call_tool ("chroma_update_documents" , {
344+ "collection_name" : collection_name ,
345+ "ids" : [],
346+ "documents" : ["New content" ]
347+ })
348+
349+ # Test with no update fields provided - Expect ToolError wrapping ValueError
350+ with pytest .raises (
351+ ToolError ,
352+ match = "At least one of 'embeddings', 'metadatas', or 'documents' must be provided"
353+ ):
354+ await mcp .call_tool ("chroma_update_documents" , {
355+ "collection_name" : collection_name ,
356+ "ids" : ["doc1" ]
357+ # No embeddings, metadatas, or documents
358+ })
359+
360+ finally :
361+ # Clean up
362+ await mcp .call_tool ("chroma_delete_collection" , {"collection_name" : collection_name })
363+
364+ @pytest .mark .asyncio
365+ async def test_update_documents_collection_not_found ():
366+ """Test updating documents in a non-existent collection."""
367+ # Expect ToolError wrapping the Exception from the function
368+ with pytest .raises (ToolError , match = "Failed to get collection" ):
369+ await mcp .call_tool ("chroma_update_documents" , {
370+ "collection_name" : "non_existent_collection" ,
371+ "ids" : ["doc1" ],
372+ "documents" : ["New content" ]
373+ })
374+
375+ @pytest .mark .asyncio
376+ async def test_update_documents_id_not_found ():
377+ """Test updating a document with an ID that does not exist. Expect no exception."""
378+ collection_name = "test_update_id_not_found"
379+ try :
380+ await mcp .call_tool ("chroma_create_collection" , {"collection_name" : collection_name })
381+ await mcp .call_tool ("chroma_add_documents" , {
382+ "collection_name" : collection_name ,
383+ "documents" : ["Test doc" ],
384+ "ids" : ["existing_id" ]
385+ })
386+
387+ # Attempt to update a non-existent ID - should not raise Exception
388+ update_result = await mcp .call_tool ("chroma_update_documents" , {
389+ "collection_name" : collection_name ,
390+ "ids" : ["non_existent_id" ],
391+ "documents" : ["New content" ]
392+ })
393+ # Check the success message (even though the ID didn't exist)
394+ assert len (update_result ) == 1
395+ assert "Successfully processed update request" in update_result [0 ].text
396+
397+ # Optionally, verify that the existing document was not changed
398+ get_result_raw = await mcp .call_tool ("chroma_get_documents" , {
399+ "collection_name" : collection_name ,
400+ "ids" : ["existing_id" ],
401+ "include" : ["documents" ]
402+ })
403+ # Corrected assertion: Parse JSON and check structure/content
404+ assert len (get_result_raw ) == 1
405+ get_result = json .loads (get_result_raw [0 ].text )
406+ assert isinstance (get_result , dict )
407+ assert "documents" in get_result
408+ assert isinstance (get_result ["documents" ], list )
409+ assert get_result ["documents" ] == ["Test doc" ]
410+
411+ finally :
412+ # Clean up
413+ await mcp .call_tool ("chroma_delete_collection" , {"collection_name" : collection_name })
0 commit comments