From 9e5afef1b344d3fa3ccfe68be2a62c1edd36d9ed Mon Sep 17 00:00:00 2001 From: Cyber MacGeddon Date: Wed, 3 Dec 2025 19:04:12 +0000 Subject: [PATCH] Tests (in the wrong directory) --- trustgraph-base/tests/__init__.py | 1 + trustgraph-base/tests/test_api_integration.py | 384 ++++++++++++++++++ 2 files changed, 385 insertions(+) create mode 100644 trustgraph-base/tests/__init__.py create mode 100644 trustgraph-base/tests/test_api_integration.py diff --git a/trustgraph-base/tests/__init__.py b/trustgraph-base/tests/__init__.py new file mode 100644 index 00000000..e4bb602b --- /dev/null +++ b/trustgraph-base/tests/__init__.py @@ -0,0 +1 @@ +# Integration tests for TrustGraph Python API diff --git a/trustgraph-base/tests/test_api_integration.py b/trustgraph-base/tests/test_api_integration.py new file mode 100644 index 00000000..e222681e --- /dev/null +++ b/trustgraph-base/tests/test_api_integration.py @@ -0,0 +1,384 @@ +""" +Integration tests for TrustGraph Python API + +These tests require a running TrustGraph Gateway API server. +Set environment variable TRUSTGRAPH_URL to point to your server. +Set TRUSTGRAPH_TOKEN if authentication is required. + +Example: + export TRUSTGRAPH_URL=http://localhost:8088/ + export TRUSTGRAPH_TOKEN=your-token-here + pytest tests/test_api_integration.py -v +""" + +import os +import pytest +import asyncio +from typing import List, Dict, Any + +from trustgraph.api import ( + Api, + Triple, + AgentThought, + AgentObservation, + AgentAnswer, + RAGChunk, +) + + +# Configuration from environment +GATEWAY_URL = os.getenv("TRUSTGRAPH_URL", "http://localhost:8088/") +AUTH_TOKEN = os.getenv("TRUSTGRAPH_TOKEN", None) +TEST_FLOW_ID = os.getenv("TRUSTGRAPH_TEST_FLOW", "test-flow") +TEST_USER = "test-user" +TEST_COLLECTION = "test-collection" + +# Skip tests if gateway is not available +SKIP_INTEGRATION = os.getenv("SKIP_INTEGRATION_TESTS", "false").lower() == "true" +skip_if_no_gateway = pytest.mark.skipif( + SKIP_INTEGRATION, + reason="Integration tests disabled (set SKIP_INTEGRATION_TESTS=false to enable)" +) + + +class TestBasicConnection: + """Test basic API instantiation and connectivity""" + + @skip_if_no_gateway + def test_api_instantiation(self): + """Test that Api class can be instantiated""" + api = Api(url=GATEWAY_URL, timeout=60, token=AUTH_TOKEN) + assert api.url.endswith("api/v1/") + assert api.timeout == 60 + assert api.token == AUTH_TOKEN + + @skip_if_no_gateway + def test_api_with_context_manager(self): + """Test Api works as context manager""" + with Api(url=GATEWAY_URL, timeout=60, token=AUTH_TOKEN) as api: + assert api is not None + # Context manager should work + # Should exit cleanly + + @skip_if_no_gateway + def test_client_lazy_initialization(self): + """Test that clients are lazily initialized""" + api = Api(url=GATEWAY_URL, timeout=60, token=AUTH_TOKEN) + + # Clients should be None initially + assert api._socket_client is None + assert api._bulk_client is None + assert api._async_flow is None + + # Access should initialize + socket = api.socket() + assert api._socket_client is not None + assert socket is api._socket_client + + # Second access should return same instance + socket2 = api.socket() + assert socket2 is socket + + +class TestRESTAPI: + """Test REST API functionality""" + + @skip_if_no_gateway + def test_flow_list(self): + """Test listing flows via REST API""" + api = Api(url=GATEWAY_URL, timeout=60, token=AUTH_TOKEN) + flow = api.flow() + + # Should be able to list flows + flows = flow.list() + assert isinstance(flows, list) + + @skip_if_no_gateway + def test_flow_class_list(self): + """Test listing flow classes""" + api = Api(url=GATEWAY_URL, timeout=60, token=AUTH_TOKEN) + flow = api.flow() + + # Should be able to list classes + classes = flow.list_classes() + assert isinstance(classes, list) + + @skip_if_no_gateway + def test_flow_instance_creation(self): + """Test creating flow instance""" + api = Api(url=GATEWAY_URL, timeout=60, token=AUTH_TOKEN) + flow = api.flow() + flow_instance = flow.id(TEST_FLOW_ID) + + assert flow_instance is not None + assert flow_instance.id == TEST_FLOW_ID + + @skip_if_no_gateway + def test_graph_embeddings_query_method_exists(self): + """Test that graph_embeddings_query method exists""" + api = Api(url=GATEWAY_URL, timeout=60, token=AUTH_TOKEN) + flow = api.flow() + flow_instance = flow.id(TEST_FLOW_ID) + + assert hasattr(flow_instance, 'graph_embeddings_query') + + +class TestAsyncRESTAPI: + """Test asynchronous REST API functionality""" + + @skip_if_no_gateway + @pytest.mark.asyncio + async def test_async_flow_list(self): + """Test listing flows via async REST API""" + api = Api(url=GATEWAY_URL, timeout=60, token=AUTH_TOKEN) + async_flow = api.async_flow() + + # Should be able to list flows + flows = await async_flow.list() + assert isinstance(flows, list) + + @skip_if_no_gateway + @pytest.mark.asyncio + async def test_async_flow_class_list(self): + """Test listing flow classes asynchronously""" + api = Api(url=GATEWAY_URL, timeout=60, token=AUTH_TOKEN) + async_flow = api.async_flow() + + # Should be able to list classes + classes = await async_flow.list_classes() + assert isinstance(classes, list) + + @skip_if_no_gateway + @pytest.mark.asyncio + async def test_async_flow_instance_creation(self): + """Test creating async flow instance""" + api = Api(url=GATEWAY_URL, timeout=60, token=AUTH_TOKEN) + async_flow = api.async_flow() + flow_instance = async_flow.id(TEST_FLOW_ID) + + assert flow_instance is not None + assert flow_instance.flow_id == TEST_FLOW_ID + + @skip_if_no_gateway + @pytest.mark.asyncio + async def test_async_context_manager(self): + """Test async context manager""" + async with Api(url=GATEWAY_URL, timeout=60, token=AUTH_TOKEN) as api: + async_flow = api.async_flow() + flows = await async_flow.list() + assert isinstance(flows, list) + + +class TestWebSocketAPI: + """Test WebSocket API functionality""" + + @skip_if_no_gateway + def test_socket_client_creation(self): + """Test WebSocket client instantiation""" + api = Api(url=GATEWAY_URL, timeout=60, token=AUTH_TOKEN) + socket = api.socket() + + assert socket is not None + assert socket.url.startswith("ws://") or socket.url.startswith("wss://") + assert socket.token == AUTH_TOKEN + + @skip_if_no_gateway + def test_socket_flow_instance(self): + """Test WebSocket flow instance creation""" + api = Api(url=GATEWAY_URL, timeout=60, token=AUTH_TOKEN) + socket = api.socket() + flow_instance = socket.flow(TEST_FLOW_ID) + + assert flow_instance is not None + assert flow_instance.flow_id == TEST_FLOW_ID + + @skip_if_no_gateway + def test_socket_methods_exist(self): + """Test that all expected WebSocket methods exist""" + api = Api(url=GATEWAY_URL, timeout=60, token=AUTH_TOKEN) + socket = api.socket() + flow_instance = socket.flow(TEST_FLOW_ID) + + # Check all methods exist + expected_methods = [ + 'agent', 'text_completion', 'graph_rag', 'document_rag', + 'prompt', 'graph_embeddings_query', 'embeddings', + 'triples_query', 'objects_query', 'mcp_tool' + ] + + for method in expected_methods: + assert hasattr(flow_instance, method), f"Missing method: {method}" + + +class TestAsyncWebSocketAPI: + """Test async WebSocket API functionality""" + + @skip_if_no_gateway + def test_async_socket_client_creation(self): + """Test async WebSocket client instantiation""" + api = Api(url=GATEWAY_URL, timeout=60, token=AUTH_TOKEN) + async_socket = api.async_socket() + + assert async_socket is not None + assert async_socket.url.startswith("ws://") or async_socket.url.startswith("wss://") + + @skip_if_no_gateway + def test_async_socket_flow_instance(self): + """Test async WebSocket flow instance creation""" + api = Api(url=GATEWAY_URL, timeout=60, token=AUTH_TOKEN) + async_socket = api.async_socket() + flow_instance = async_socket.flow(TEST_FLOW_ID) + + assert flow_instance is not None + assert flow_instance.flow_id == TEST_FLOW_ID + + @skip_if_no_gateway + def test_async_socket_methods_exist(self): + """Test that all expected async WebSocket methods exist""" + api = Api(url=GATEWAY_URL, timeout=60, token=AUTH_TOKEN) + async_socket = api.async_socket() + flow_instance = async_socket.flow(TEST_FLOW_ID) + + # Check all methods exist + expected_methods = [ + 'agent', 'text_completion', 'graph_rag', 'document_rag', + 'prompt', 'graph_embeddings_query', 'embeddings', + 'triples_query', 'objects_query', 'mcp_tool' + ] + + for method in expected_methods: + assert hasattr(flow_instance, method), f"Missing method: {method}" + + +class TestBulkOperations: + """Test bulk operations functionality""" + + @skip_if_no_gateway + def test_bulk_client_creation(self): + """Test bulk client instantiation""" + api = Api(url=GATEWAY_URL, timeout=60, token=AUTH_TOKEN) + bulk = api.bulk() + + assert bulk is not None + assert bulk.url.startswith("ws://") or bulk.url.startswith("wss://") + + @skip_if_no_gateway + def test_bulk_methods_exist(self): + """Test that all expected bulk methods exist""" + api = Api(url=GATEWAY_URL, timeout=60, token=AUTH_TOKEN) + bulk = api.bulk() + + # Check all methods exist + expected_methods = [ + 'import_triples', 'export_triples', + 'import_graph_embeddings', 'export_graph_embeddings', + 'import_document_embeddings', 'export_document_embeddings', + 'import_entity_contexts', 'export_entity_contexts', + 'import_objects' + ] + + for method in expected_methods: + assert hasattr(bulk, method), f"Missing method: {method}" + + @skip_if_no_gateway + def test_async_bulk_client_creation(self): + """Test async bulk client instantiation""" + api = Api(url=GATEWAY_URL, timeout=60, token=AUTH_TOKEN) + async_bulk = api.async_bulk() + + assert async_bulk is not None + assert async_bulk.url.startswith("ws://") or async_bulk.url.startswith("wss://") + + +class TestMetrics: + """Test metrics functionality""" + + @skip_if_no_gateway + def test_metrics_client_creation(self): + """Test metrics client instantiation""" + api = Api(url=GATEWAY_URL, timeout=60, token=AUTH_TOKEN) + metrics = api.metrics() + + assert metrics is not None + assert hasattr(metrics, 'get') + + @skip_if_no_gateway + def test_async_metrics_client_creation(self): + """Test async metrics client instantiation""" + api = Api(url=GATEWAY_URL, timeout=60, token=AUTH_TOKEN) + async_metrics = api.async_metrics() + + assert async_metrics is not None + assert hasattr(async_metrics, 'get') + + @skip_if_no_gateway + @pytest.mark.asyncio + async def test_async_metrics_get(self): + """Test getting metrics asynchronously""" + try: + api = Api(url=GATEWAY_URL, timeout=60, token=AUTH_TOKEN) + async_metrics = api.async_metrics() + + metrics_text = await async_metrics.get() + assert isinstance(metrics_text, str) + # Prometheus metrics should contain "# HELP" lines + assert "# HELP" in metrics_text or len(metrics_text) > 0 + except Exception as e: + # Metrics endpoint might not be available + pytest.skip(f"Metrics endpoint not available: {e}") + + +class TestStreamingTypes: + """Test streaming chunk type classes""" + + def test_agent_thought_creation(self): + """Test AgentThought chunk creation""" + chunk = AgentThought(content="thinking...", end_of_message=False) + assert chunk.content == "thinking..." + assert chunk.end_of_message is False + assert chunk.chunk_type == "thought" + + def test_agent_observation_creation(self): + """Test AgentObservation chunk creation""" + chunk = AgentObservation(content="observing...", end_of_message=False) + assert chunk.content == "observing..." + assert chunk.chunk_type == "observation" + + def test_agent_answer_creation(self): + """Test AgentAnswer chunk creation""" + chunk = AgentAnswer( + content="answer", + end_of_message=True, + end_of_dialog=True + ) + assert chunk.content == "answer" + assert chunk.end_of_message is True + assert chunk.end_of_dialog is True + assert chunk.chunk_type == "final-answer" + + def test_rag_chunk_creation(self): + """Test RAGChunk creation""" + chunk = RAGChunk( + content="response chunk", + end_of_stream=False, + error=None + ) + assert chunk.content == "response chunk" + assert chunk.end_of_stream is False + assert chunk.error is None + + +class TestTripleType: + """Test Triple type""" + + def test_triple_creation(self): + """Test Triple creation""" + triple = Triple(s="subject", p="predicate", o="object") + assert triple.s == "subject" + assert triple.p == "predicate" + assert triple.o == "object" + + +# Run tests with: pytest tests/test_api_integration.py -v +if __name__ == "__main__": + pytest.main([__file__, "-v"])