|
1 | 1 | import asyncio |
| 2 | +import importlib |
| 3 | +import sys |
2 | 4 |
|
3 | | -from collections.abc import Generator |
| 5 | +from collections.abc import Callable, Generator |
4 | 6 | from typing import Any, NoReturn |
5 | 7 | from unittest import mock |
6 | 8 |
|
@@ -30,6 +32,35 @@ def patch_trace_get_tracer( |
30 | 32 | yield |
31 | 33 |
|
32 | 34 |
|
| 35 | +@pytest.fixture |
| 36 | +def reload_telemetry_module( |
| 37 | + monkeypatch: pytest.MonkeyPatch, |
| 38 | +) -> Generator[Callable[[str | None], Any], None, None]: |
| 39 | + """Fixture to handle telemetry module reloading with env var control.""" |
| 40 | + |
| 41 | + def _reload(env_value: str | None = None) -> Any: |
| 42 | + if env_value is None: |
| 43 | + monkeypatch.delenv( |
| 44 | + 'OTEL_A2A_SDK_INSTRUMENTATION_ENABLED', raising=False |
| 45 | + ) |
| 46 | + else: |
| 47 | + monkeypatch.setenv( |
| 48 | + 'OTEL_A2A_SDK_INSTRUMENTATION_ENABLED', env_value |
| 49 | + ) |
| 50 | + |
| 51 | + # Remove from sys.modules to force fresh top-level execution |
| 52 | + sys.modules.pop('a2a.utils.telemetry', None) |
| 53 | + |
| 54 | + # importlib.import_module is often cleaner than 'import' in dynamic contexts |
| 55 | + module = importlib.import_module('a2a.utils.telemetry') |
| 56 | + return module |
| 57 | + |
| 58 | + yield _reload |
| 59 | + |
| 60 | + # Cleanup to ensure other tests aren't affected by a "poisoned" sys.modules |
| 61 | + sys.modules.pop('a2a.utils.telemetry', None) |
| 62 | + |
| 63 | + |
33 | 64 | def test_trace_function_sync_success(mock_span: mock.MagicMock) -> None: |
34 | 65 | @trace_function |
35 | 66 | def foo(x, y): |
@@ -198,3 +229,43 @@ def foo(self) -> str: |
198 | 229 | assert obj.foo() == 'foo' |
199 | 230 | assert hasattr(obj.foo, '__wrapped__') |
200 | 231 | assert hasattr(obj, 'x') |
| 232 | + |
| 233 | + |
| 234 | +@pytest.mark.xdist_group(name='telemetry_isolation') |
| 235 | +@pytest.mark.parametrize( |
| 236 | + 'env_value,expected_tracing', |
| 237 | + [ |
| 238 | + (None, True), # Default: env var not set, tracing enabled |
| 239 | + ('true', True), # Explicitly enabled |
| 240 | + ('True', True), # Case insensitive |
| 241 | + ('false', False), # Disabled |
| 242 | + ('', False), # Empty string = false |
| 243 | + ], |
| 244 | +) |
| 245 | +def test_env_var_controls_instrumentation( |
| 246 | + reload_telemetry_module: Callable[[str | None], Any], |
| 247 | + env_value: str | None, |
| 248 | + expected_tracing: bool, |
| 249 | +) -> None: |
| 250 | + """Test OTEL_A2A_SDK_INSTRUMENTATION_ENABLED controls span creation.""" |
| 251 | + telemetry_module = reload_telemetry_module(env_value) |
| 252 | + |
| 253 | + is_noop = type(telemetry_module.trace).__name__ == '_NoOp' |
| 254 | + |
| 255 | + assert is_noop != expected_tracing |
| 256 | + |
| 257 | + |
| 258 | +@pytest.mark.xdist_group(name='telemetry_isolation') |
| 259 | +def test_env_var_disabled_logs_message( |
| 260 | + reload_telemetry_module: Callable[[str | None], Any], |
| 261 | + caplog: pytest.LogCaptureFixture, |
| 262 | +) -> None: |
| 263 | + """Test that disabling via env var logs appropriate debug message.""" |
| 264 | + with caplog.at_level('DEBUG', logger='a2a.utils.telemetry'): |
| 265 | + reload_telemetry_module('false') |
| 266 | + |
| 267 | + assert ( |
| 268 | + 'A2A OTEL instrumentation disabled via environment variable' |
| 269 | + in caplog.text |
| 270 | + ) |
| 271 | + assert 'OTEL_A2A_SDK_INSTRUMENTATION_ENABLED' in caplog.text |
0 commit comments