From 9ba6e783acb0b56eaec002cac6c6b02f06a080ff Mon Sep 17 00:00:00 2001 From: Teo Date: Tue, 26 Nov 2024 23:57:18 -0600 Subject: [PATCH] reorganize tests --- ...t_exporter.py => test_session_exporter.py} | 69 +++--- tests/test_session.py | 227 ------------------ 2 files changed, 32 insertions(+), 264 deletions(-) rename tests/session/{test_exporter.py => test_session_exporter.py} (85%) diff --git a/tests/session/test_exporter.py b/tests/session/test_session_exporter.py similarity index 85% rename from tests/session/test_exporter.py rename to tests/session/test_session_exporter.py index 4578c7c7..96853554 100644 --- a/tests/session/test_exporter.py +++ b/tests/session/test_session_exporter.py @@ -1,7 +1,7 @@ +import json import time # Add to existing imports from unittest.mock import Mock, patch from uuid import uuid4 -import json import pytest from opentelemetry.sdk.trace import ReadableSpan @@ -32,8 +32,8 @@ def test_generic_adapter_conversion(session): from datetime import datetime from uuid import UUID - from agentops.session.exporter import SessionSpanAdapter from agentops.event import ActionEvent + from agentops.session.exporter import SessionSpanAdapter # Create a test event with various attribute types test_event = ActionEvent( @@ -179,59 +179,29 @@ def test_span_processor_config(session): assert session._span_processor is not None -def test_event_batching(session, mock_req): - """Test that events are properly batched and exported""" - # Record multiple action events - for i in range(3): - event = ActionEvent(f"test_event_{i}") - session.record(event, flush_now=True) - time.sleep(0.1) - - # Find all create_events requests - create_events_requests = [req for req in mock_req.request_history if req.url.endswith("/v2/create_events")] - assert len(create_events_requests) > 0 - - # Get all events that were sent - all_events = [] - for req in create_events_requests: - events = req.json()["events"] - all_events.extend(events) - - assert len(all_events) == 3 - - # Verify event contents - for i, event in enumerate(all_events): - assert event["event_type"] == "actions" # The type should be "actions" - assert event["action_type"] == f"test_event_{i}" # The name becomes the action_type - - def test_event_recording(session, mock_req): """Test recording a single event""" event = ActionEvent("test_action") session.record(event, flush_now=True) - time.sleep(0.1) create_events_requests = [req for req in mock_req.request_history if req.url.endswith("/v2/create_events")] assert len(create_events_requests) > 0 events = create_events_requests[-1].json()["events"] assert len(events) == 1 - assert events[0]["event_type"] == "actions" # Type should be "actions" - assert events[0]["action_type"] == "test_action" # Name becomes action_type + assert events[0]["event_type"] == "test_action" # Type should be "actions" def test_multiple_event_types(session, mock_req): """Test recording different types of events""" - session.record(ActionEvent("test_action"), flush_now=True) - time.sleep(0.1) + session.record(ActionEvent("test_action2"), flush_now=True) create_events_requests = [req for req in mock_req.request_history if req.url.endswith("/v2/create_events")] assert len(create_events_requests) > 0 events = create_events_requests[-1].json()["events"] assert len(events) == 1 - assert events[0]["event_type"] == "actions" - assert events[0]["action_type"] == "test_action" + assert events[0]["event_type"] == "test_action2" def test_session_cleanup(mocker, session, mock_req): @@ -244,14 +214,39 @@ def test_session_cleanup(mocker, session, mock_req): # Record an event event = ActionEvent("test_cleanup") session.record(event, flush_now=True) - time.sleep(0.1) # End session session.end_session("Success") - time.sleep(0.1) # Verify update_session was called update_mock.assert_called() # Verify session end state assert session.end_state == "Success" + + +def test_event_export_through_processor(session): + """Test that events are properly exported through the span processor""" + # Create a mock for the export method + with patch("agentops.session.exporter.SessionExporter.export") as mock_export: + # Set up mock return value + mock_export.return_value = SpanExportResult.SUCCESS + + # Create and record an event + event = ActionEvent("test_action") + session.record(event) + + # Force flush to ensure export happens + session._span_processor.force_flush() + + # Verify exporter was called + assert mock_export.call_count > 0 + + # Get the exported spans + exported_spans = mock_export.call_args[0][0] + assert len(exported_spans) > 0 + + # Verify span attributes + exported_span = exported_spans[0] + assert exported_span.name == "test_action" + assert "event.type" in exported_span.attributes diff --git a/tests/test_session.py b/tests/test_session.py index 687ea5d7..72e185b0 100644 --- a/tests/test_session.py +++ b/tests/test_session.py @@ -413,230 +413,3 @@ def test_get_analytics_multiple_sessions(self, mock_req): session_1.end_session(end_state) session_2.end_session(end_state) - - -class TestSessionExporter: - def setup_method(self): - self.api_key = "11111111-1111-4111-8111-111111111111" - # Initialize agentops first - agentops.init(api_key=self.api_key, max_wait_time=50, auto_start_session=False) - self.session = agentops.start_session() - assert self.session is not None # Verify session was created - self.exporter = self.session._exporter - - def teardown_method(self): - """Clean up after each test""" - if self.session: - self.session.end_session("Success") - agentops.end_all_sessions() - clear_singletons() - - def create_test_span(self, name="test_span", attributes=None): - """Helper to create a test span with required attributes""" - if attributes is None: - attributes = {} - - # Ensure required attributes are present - base_attributes = { - "event.id": str(UUID(int=1)), - "event.type": "test_type", - "event.timestamp": datetime.now(timezone.utc).isoformat(), - "event.end_timestamp": datetime.now(timezone.utc).isoformat(), - "event.data": json.dumps({"test": "data"}), - "session.id": str(self.session.session_id), - } - base_attributes.update(attributes) - - context = SpanContext( - trace_id=0x000000000000000000000000DEADBEEF, - span_id=0x00000000DEADBEF0, - is_remote=False, - trace_state=TraceState(), - ) - - return ReadableSpan( - name=name, - context=context, - kind=SpanKind.INTERNAL, - status=Status(StatusCode.OK), - start_time=123, - end_time=456, - attributes=base_attributes, - events=[], - links=[], - resource=self.session._tracer_provider.resource, - ) - - def test_export_basic_span(self, mock_req): - """Test basic span export with all required fields""" - span = self.create_test_span() - result = self.exporter.export([span]) - - assert result == SpanExportResult.SUCCESS - assert len(mock_req.request_history) > 0 - - last_request = mock_req.last_request.json() - assert "events" in last_request - event = last_request["events"][0] - - # Verify required fields - assert "id" in event - assert "event_type" in event - assert "init_timestamp" in event - assert "end_timestamp" in event - assert "session_id" in event - - def test_export_action_event(self, mock_req): - """Test export of action event with specific formatting""" - action_attributes = { - "event.data": json.dumps( - {"action_type": "test_action", "params": {"param1": "value1"}, "returns": "test_return"} - ) - } - - span = self.create_test_span(name="actions", attributes=action_attributes) - result = self.exporter.export([span]) - - assert result == SpanExportResult.SUCCESS - - last_request = mock_req.request_history[-1].json() - event = last_request["events"][0] - - assert event["action_type"] == "test_action" - assert event["params"] == {"param1": "value1"} - assert event["returns"] == "test_return" - - def test_export_tool_event(self, mock_req): - """Test export of tool event with specific formatting""" - tool_attributes = { - "event.data": json.dumps({"name": "test_tool", "params": {"param1": "value1"}, "returns": "test_return"}) - } - - span = self.create_test_span(name="tools", attributes=tool_attributes) - result = self.exporter.export([span]) - - assert result == SpanExportResult.SUCCESS - - last_request = mock_req.request_history[-1].json() - event = last_request["events"][0] - - assert event["name"] == "test_tool" - assert event["params"] == {"param1": "value1"} - assert event["returns"] == "test_return" - - def test_export_with_missing_timestamp(self, mock_req): - """Test handling of missing end_timestamp""" - attributes = {"event.end_timestamp": None} # This should be handled gracefully - - span = self.create_test_span(attributes=attributes) - result = self.exporter.export([span]) - - assert result == SpanExportResult.SUCCESS - - last_request = mock_req.request_history[-1].json() - event = last_request["events"][0] - - # Verify end_timestamp is present and valid - assert "end_timestamp" in event - assert event["end_timestamp"] is not None - - def test_export_with_missing_timestamps_advanced(self, mock_req): - """Test handling of missing timestamps""" - attributes = {"event.timestamp": None, "event.end_timestamp": None} - - span = self.create_test_span(attributes=attributes) - result = self.exporter.export([span]) - - assert result == SpanExportResult.SUCCESS - - last_request = mock_req.request_history[-1].json() - event = last_request["events"][0] - - # Verify timestamps are present and valid - assert "init_timestamp" in event - assert "end_timestamp" in event - assert event["init_timestamp"] is not None - assert event["end_timestamp"] is not None - - # Verify timestamps are in ISO format - try: - datetime.fromisoformat(event["init_timestamp"].replace("Z", "+00:00")) - datetime.fromisoformat(event["end_timestamp"].replace("Z", "+00:00")) - except ValueError: - pytest.fail("Timestamps are not in valid ISO format") - - def test_export_with_shutdown(self, mock_req): - """Test export behavior when shutdown""" - self.exporter._shutdown.set() - span = self.create_test_span() - - result = self.exporter.export([span]) - assert result == SpanExportResult.SUCCESS - - # Verify no request was made - assert not any(req.url.endswith("/v2/create_events") for req in mock_req.request_history[-1:]) - - def test_export_llm_event(self, mock_req): - """Test export of LLM event with specific handling of timestamps""" - llm_attributes = { - "event.data": json.dumps( - { - "prompt": "test prompt", - "completion": "test completion", - "model": "test-model", - "tokens": 100, - "cost": 0.002, - } - ) - } - - span = self.create_test_span(name="llms", attributes=llm_attributes) - result = self.exporter.export([span]) - - assert result == SpanExportResult.SUCCESS - - last_request = mock_req.request_history[-1].json() - event = last_request["events"][0] - - # Verify LLM specific fields - assert event["prompt"] == "test prompt" - assert event["completion"] == "test completion" - assert event["model"] == "test-model" - assert event["tokens"] == 100 - assert event["cost"] == 0.002 - - # Verify timestamps - assert event["init_timestamp"] is not None - assert event["end_timestamp"] is not None - - def test_export_with_missing_id(self, mock_req): - """Test handling of missing event ID""" - attributes = {"event.id": None} - - span = self.create_test_span(attributes=attributes) - result = self.exporter.export([span]) - - assert result == SpanExportResult.SUCCESS - - last_request = mock_req.request_history[-1].json() - event = last_request["events"][0] - - # Verify ID is present and valid UUID - assert "id" in event - assert event["id"] is not None - try: - UUID(event["id"]) - except ValueError: - pytest.fail("Event ID is not a valid UUID") - - @patch("agentops.session.exporter.SessionExporter") - def test_event_export(self, mock_exporter): - session = agentops.start_session() - event = ActionEvent("test_action") - - session.record(event) - - # Verify exporter called with correct span - mock_exporter.return_value.export.assert_called_once() - exported_span = mock_exporter.return_value.export.call_args[0][0][0] - assert exported_span.name == "test_action"