import pytest from unittest.mock import MagicMock, patch import os import sys # Add project root to path sys.path.append(os.getcwd()) from yuxi.knowledge.graphs.upload_graph_service import UploadGraphService # For backward compatibility with the existing test GraphDatabase = UploadGraphService @pytest.mark.asyncio async def test_txt_add_vector_entity_parsing(): # Mock driver and session mock_driver = MagicMock() mock_session = MagicMock() mock_driver.session.return_value.__enter__.return_value = mock_session # Setup mock transaction mock_tx = MagicMock() def side_effect_execute_write(func, *args, **kwargs): return func(mock_tx, *args, **kwargs) # Mock execute_read to return empty list (no missing embeddings for this test) # The code calls _get_nodes_without_embedding which returns [record['name']] # If we return [], it means all nodes have embeddings or none found. # Actually, the code checks: # nodes_without_embedding = session.execute_read(_get_nodes_without_embedding, all_entities) # Let's mock it to return empty list so we skip embedding generation loop which simplifies test mock_session.execute_read.return_value = [] mock_session.execute_write.side_effect = side_effect_execute_write # Mock embedding model with patch("yuxi.models.select_embedding_model") as mock_select_model: mock_embed_model = MagicMock() mock_select_model.return_value = mock_embed_model # Create a mock connection object with driver and status as attributes # (not a ConnectionManager class, just a simple mock object) mock_connection = MagicMock() mock_connection.driver = mock_driver mock_connection.status = "open" # Instantiate GraphDatabase with mocked connection gd = GraphDatabase(mock_connection) # Set embed_model_name directly (this is a settable attribute) gd.embed_model_name = "test_model" # Mock config where it's imported in upload_graph_service.py # The import is `from yuxi import config`, so patch at the usage location mock_config = MagicMock() mock_config.embed_model = "test_model" mock_embed_info = MagicMock() mock_embed_info.dimension = 1024 mock_config.embed_model_names = {"test_model": mock_embed_info} with patch("yuxi.knowledge.graphs.upload_graph_service.config", mock_config): # Test data: Mixed format triples = [ # Legacy format {"h": "A", "r": "KNOWS", "t": "B"}, # Extended format { "h": {"name": "C", "age": 30}, "r": {"type": "LIKES", "weight": 0.8}, "t": {"name": "D", "role": "User"}, }, ] # Run the method await gd.txt_add_vector_entity(triples) # Verify calls to mock_tx.run merge_calls = [] for call in mock_tx.run.call_args_list: args, kwargs = call query = args[0] if args else kwargs.get("query", "") if "MERGE (h:Entity:Upload" in query: # The args are passed as kwargs to run: h_name=..., etc. merge_calls.append(kwargs) assert len(merge_calls) == 2, f"Expected 2 merge calls, got {len(merge_calls)}" # Call 1 (Legacy) call1 = merge_calls[0] assert call1["h_name"] == "A" assert call1["h_props"] == {} assert call1["t_name"] == "B" assert call1["t_props"] == {} assert call1["r_type"] == "KNOWS" assert call1["r_props"] == {} # Call 2 (Extended) call2 = merge_calls[1] assert call2["h_name"] == "C" assert call2["h_props"] == {"age": 30} assert call2["t_name"] == "D" assert call2["t_props"] == {"role": "User"} assert call2["r_type"] == "LIKES" assert call2["r_props"] == {"weight": 0.8} print("Verification passed!")