"""Synchronous end-to-end traces for the Google ADK integration. Mirrors the AgentCore ``test_sync.py`` class layout: ``TestSimpleApp``, ``TestToolApp``, ``TestMultipleToolsApp``, ``TestDeepEvalFeatures``. Each test produces a real trace via the ADK ``InMemoryRunner`` (which runs the agent on Gemini under the hood) and asserts its shape against a JSON schema in ``schemas/``. Schema regeneration: ``GENERATE_SCHEMAS=true pytest tests/test_integrations/test_googleadk/test_sync.py``. See ``schemas/README.md`` for the full workflow. Skipped without ``GOOGLE_API_KEY`` — the underlying Gemini call would fail authentication otherwise. Span-level configuration migrates to per-call ``with next_*_span(...)`` blocks; ``init_*_googleadk(...)`` carries trace-level kwargs only. """ import os import pytest from deepeval.metrics import AnswerRelevancyMetric from deepeval.tracing import next_agent_span, next_llm_span, next_tool_span from tests.test_integrations.test_googleadk.apps.googleadk_simple_app import ( init_simple_googleadk, invoke_simple_agent, ) from tests.test_integrations.test_googleadk.apps.googleadk_tool_app import ( init_tool_googleadk, invoke_tool_agent, ) from tests.test_integrations.test_googleadk.apps.googleadk_multiple_tools_app import ( init_multiple_tools_googleadk, invoke_multiple_tools_agent, ) from tests.test_integrations.test_googleadk.apps.googleadk_eval_app import ( init_evals_googleadk, invoke_evals_agent, ) from tests.test_integrations.test_googleadk.conftest import trace_test pytestmark = pytest.mark.skipif( not os.getenv("GOOGLE_API_KEY"), reason="GOOGLE_API_KEY is required to run Google ADK tests against Gemini.", ) class TestSimpleApp: @trace_test("googleadk_simple_schema.json") def test_simple_greeting(self): invoke_func = init_simple_googleadk( name="googleadk-simple-test", tags=["googleadk", "simple"], metadata={"test_type": "simple"}, thread_id="simple-123", user_id="test-user", ) result = invoke_simple_agent( "Say hello in exactly three words.", invoke_func=invoke_func, ) assert result is not None assert len(result) > 0 class TestToolApp: @trace_test("googleadk_tool_schema.json") def test_tool_calculation(self): invoke_func = init_tool_googleadk( name="googleadk-tool-test", tags=["googleadk", "tool"], metadata={"test_type": "tool"}, thread_id="tool-123", user_id="test-user", ) result = invoke_tool_agent( "What is 7 multiplied by 8?", invoke_func=invoke_func, ) assert result is not None assert "56" in result @trace_test("googleadk_tool_metric_collection_schema.json") def test_tool_metric_collection(self): """Tool-level metric_collection now flows through ``with next_tool_span(metric_collection=...)`` at the call site instead of a top-level ``tool_metric_collection_map`` kwarg on ``instrument_google_adk``. ``next_tool_span`` is one-shot — it hits the FIRST tool span emitted inside the ``with`` block, which matches the single-tool-call test below.""" invoke_func = init_tool_googleadk( name="googleadk-tool-metric-test", tags=["googleadk", "tool", "metric-collection"], metadata={"test_type": "tool_metric_collection"}, thread_id="tool-metric-123", user_id="test-user", ) with next_tool_span(metric_collection="calculator-metrics"): result = invoke_tool_agent( "What is 15 plus 25?", invoke_func=invoke_func, ) assert result is not None assert "40" in result class TestMultipleToolsApp: @trace_test("googleadk_multiple_tools_weather_schema.json") def test_multiple_tools_weather_only(self): invoke_func = init_multiple_tools_googleadk( name="googleadk-multiple-tools-weather", tags=["googleadk", "multiple-tools", "weather"], metadata={"test_type": "multiple_tools_weather"}, thread_id="multiple-tools-weather-123", user_id="test-user", ) result = invoke_multiple_tools_agent( "Use the get_weather tool exactly once to get the weather in Tokyo.", invoke_func=invoke_func, ) assert result is not None assert "72" in result or "sunny" in result.lower() @trace_test("googleadk_multiple_tools_time_schema.json") def test_multiple_tools_time_only(self): invoke_func = init_multiple_tools_googleadk( name="googleadk-multiple-tools-time", tags=["googleadk", "multiple-tools", "time"], metadata={"test_type": "multiple_tools_time"}, thread_id="multiple-tools-time-123", user_id="test-user", ) result = invoke_multiple_tools_agent( "Use the get_time tool exactly once to get the current time in London.", invoke_func=invoke_func, ) assert result is not None assert "7:00" in result or "GMT" in result @trace_test("googleadk_parallel_tools_schema.json") def test_parallel_tool_calls(self): invoke_func = init_multiple_tools_googleadk( name="googleadk-parallel-tools", tags=["googleadk", "parallel-tools"], metadata={"test_type": "parallel_tools"}, thread_id="parallel-tools-123", user_id="test-user", ) result = invoke_multiple_tools_agent( "Use both the get_weather tool AND the get_time tool for Paris. " "Call both tools exactly once each.", invoke_func=invoke_func, ) assert result is not None assert "62" in result or "cloudy" in result.lower() assert "8:00" in result or "CET" in result class TestDeepEvalFeatures: """Span-level configuration migrates to per-call ``with next_*_span(...)``. Previously ``init_evals_googleadk`` accepted ``agent_metric_collection`` / ``llm_metric_collection`` / ``tool_metric_collection_map`` / ``agent_metrics`` and stamped them onto every span at instrument time. Now the test wraps the agent invocation in stacked ``with`` blocks that stage values for the next agent / LLM / tool span emitted inside the wrapper. The ``special_tool`` itself uses ``update_current_span(...)`` from inside its body for its own metric collection — handled in ``apps/googleadk_eval_app.py``.""" @trace_test("googleadk_features_sync.json") def test_full_features_sync(self): invoke_func = init_evals_googleadk( name="googleadk-full-features-sync", tags=["googleadk", "features", "sync"], metadata={"env": "testing", "priority": "high"}, thread_id="thread-sync-features-001", user_id="user-sync-001", metric_collection="trace_metrics_override_v1", ) with next_agent_span( metric_collection="agent_metrics_v1", metrics=[AnswerRelevancyMetric()], ), next_llm_span(metric_collection="llm_metrics_v1"): result = invoke_evals_agent( "Use the special_tool to process 'Sync Data'", invoke_func=invoke_func, ) assert result is not None