|
| 1 | +import dataclasses |
| 2 | +from typing import TYPE_CHECKING |
| 3 | + |
| 4 | +from asgi_lifespan import LifespanManager |
| 5 | +from fastapi import status |
| 6 | +from httpx import ASGITransport, AsyncClient |
| 7 | +from opentelemetry import trace |
| 8 | +from opentelemetry.sdk.trace import TracerProvider |
| 9 | +from opentelemetry.sdk.trace.export import SimpleSpanProcessor |
| 10 | +from opentelemetry.sdk.trace.export.in_memory_span_exporter import InMemorySpanExporter |
| 11 | + |
| 12 | +from app.application import build_app |
| 13 | +from app.settings import Settings, settings |
| 14 | + |
| 15 | + |
| 16 | +if TYPE_CHECKING: |
| 17 | + import pytest |
| 18 | + |
| 19 | + |
| 20 | +async def test_db_queries_produce_sqlalchemy_spans(monkeypatch: pytest.MonkeyPatch) -> None: |
| 21 | + traced_config = dataclasses.replace(settings.api_bootstrapper_config, opentelemetry_log_traces=True) |
| 22 | + monkeypatch.setattr(Settings, "api_bootstrapper_config", traced_config) |
| 23 | + exporter = InMemorySpanExporter() |
| 24 | + app = build_app() |
| 25 | + tracer_provider = trace.get_tracer_provider() |
| 26 | + assert isinstance(tracer_provider, TracerProvider) |
| 27 | + tracer_provider.add_span_processor(SimpleSpanProcessor(exporter)) |
| 28 | + |
| 29 | + async with LifespanManager(app), AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as client: |
| 30 | + response = await client.get("/api/decks/") |
| 31 | + |
| 32 | + assert response.status_code == status.HTTP_200_OK |
| 33 | + db_spans = [ |
| 34 | + span |
| 35 | + for span in exporter.get_finished_spans() |
| 36 | + if span.instrumentation_scope is not None |
| 37 | + and span.instrumentation_scope.name == "opentelemetry.instrumentation.sqlalchemy" |
| 38 | + and span.name.startswith("SELECT") |
| 39 | + ] |
| 40 | + assert db_spans |
| 41 | + assert all(span.parent is not None for span in db_spans) |
0 commit comments