from __future__ import annotations import os from opentelemetry.sdk.trace.export import SpanExportResult import app.utils.structured_otlp as structured_otlp class _RecordingExporter: def __init__(self, *args: object, **kwargs: object) -> None: self.exported: list[int] = [] self.shutdown_calls = 0 def export(self, spans: object) -> SpanExportResult: self.exported.append(len(list(spans))) return SpanExportResult.SUCCESS def shutdown(self) -> None: self.shutdown_calls += 1 def force_flush(self, timeout_millis: int = 30_000) -> bool: return True def _reset_provider_without_shutdown() -> None: with structured_otlp._TRACER_LOCK: structured_otlp._TRACER = None structured_otlp._TRACER_PROVIDER = None structured_otlp._TRACER_ENDPOINT = "" def test_span_export_is_batched_not_synchronous(monkeypatch) -> None: old_endpoint = os.environ.get("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT") os.environ["OTEL_EXPORTER_OTLP_TRACES_ENDPOINT"] = "http://localhost:4318/v1/traces" exporter = _RecordingExporter() monkeypatch.setattr( structured_otlp, "OTLPSpanExporter", lambda *args, **kwargs: exporter, ) _reset_provider_without_shutdown() try: structured_otlp.publish_structured_span( { "session_id": "0123456789abcdef0123456789abcdef", "tipo_evento": "audio_in", "dat_hora_inicio": "2026-07-24T12:00:00.000000Z", "dat_hora_fim": "2026-07-24T12:00:00.010000Z", } ) assert exporter.exported == [] assert structured_otlp.force_flush_structured_otlp(timeout_millis=5_000) assert sum(exporter.exported) >= 1 finally: structured_otlp.shutdown_structured_otlp() if old_endpoint is None: os.environ.pop("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", None) else: os.environ["OTEL_EXPORTER_OTLP_TRACES_ENDPOINT"] = old_endpoint def test_shutdown_flushes_provider_once(monkeypatch) -> None: old_endpoint = os.environ.get("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT") os.environ["OTEL_EXPORTER_OTLP_TRACES_ENDPOINT"] = "http://localhost:4318/v1/traces" exporter = _RecordingExporter() monkeypatch.setattr( structured_otlp, "OTLPSpanExporter", lambda *args, **kwargs: exporter, ) _reset_provider_without_shutdown() try: assert structured_otlp._tracer() is not None structured_otlp.shutdown_structured_otlp() structured_otlp.shutdown_structured_otlp() assert exporter.shutdown_calls == 1 assert structured_otlp._TRACER_PROVIDER is None finally: structured_otlp.shutdown_structured_otlp() if old_endpoint is None: os.environ.pop("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", None) else: os.environ["OTEL_EXPORTER_OTLP_TRACES_ENDPOINT"] = old_endpoint