Skip to content

Commit

Permalink
reorganize tests
Browse files Browse the repository at this point in the history
  • Loading branch information
teocns committed Nov 27, 2024
1 parent 1e771a6 commit 9ba6e78
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 264 deletions.
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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):
Expand All @@ -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
227 changes: 0 additions & 227 deletions tests/test_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"

0 comments on commit 9ba6e78

Please sign in to comment.