import importlib from app.config import ObservabilityConfig def _reload_metrics_module(): metrics_module = importlib.import_module("app.runtime.metrics") return importlib.reload(metrics_module) def _make_observability_config(**overrides) -> ObservabilityConfig: payload = { "enabled": True, "service_name": "g2s-aggregator", "otlp_endpoint": "http://collector:4317", "otlp_insecure": True, } payload.update(overrides) return ObservabilityConfig(**payload) def test_configure_metrics_skips_bootstrap_when_disabled(monkeypatch) -> None: metrics_module = _reload_metrics_module() calls: list[object] = [] monkeypatch.setattr( metrics_module, "_build_meter_provider", lambda observability: calls.append(observability), ) metrics_module.configure_metrics(_make_observability_config(enabled=False)) assert calls == [] def test_build_meter_provider_uses_otlp_exporter_and_service_name(monkeypatch) -> None: metrics_module = _reload_metrics_module() created: dict[str, object] = {} class FakeExporter: def __init__(self, *, endpoint: str, insecure: bool) -> None: created["endpoint"] = endpoint created["insecure"] = insecure class FakeReader: def __init__(self, exporter: object) -> None: created["exporter"] = exporter class FakeMeterProvider: def __init__(self, *, resource, metric_readers: list[object]) -> None: self.resource = resource self.metric_readers = metric_readers monkeypatch.setattr(metrics_module, "OTLPMetricExporter", FakeExporter) monkeypatch.setattr(metrics_module, "PeriodicExportingMetricReader", FakeReader) monkeypatch.setattr(metrics_module, "MeterProvider", FakeMeterProvider) provider = metrics_module._build_meter_provider( _make_observability_config( otlp_endpoint="http://metrics-backend:4317", otlp_insecure=False, ) ) assert created["endpoint"] == "http://metrics-backend:4317" assert created["insecure"] is False assert provider.resource.attributes["service.name"] == "g2s-aggregator" assert len(provider.metric_readers) == 1 assert created["exporter"] is not None def test_cache_metrics_create_and_record_expected_instruments(monkeypatch) -> None: metrics_module = _reload_metrics_module() created: dict[str, object] = {} class FakeInstrument: def __init__(self, name: str) -> None: self.name = name self.records: list[tuple[float, dict[str, str] | None]] = [] def add(self, value: float, attributes: dict[str, str]) -> None: self.records.append((value, attributes)) def record( self, value: float, attributes: dict[str, str] | None = None ) -> None: self.records.append((value, attributes)) class FakeMeter: def create_counter(self, name: str, **kwargs) -> FakeInstrument: del kwargs instrument = FakeInstrument(name) created[name] = instrument return instrument def create_histogram(self, name: str, **kwargs) -> FakeInstrument: del kwargs instrument = FakeInstrument(name) created[name] = instrument return instrument monkeypatch.setattr(metrics_module.metrics, "get_meter", lambda _name: FakeMeter()) cache_metrics = metrics_module.CacheMetrics() cache_metrics.record_request(operation="get", outcome="hit") cache_metrics.record_error(operation="set", error_type="ConnectionError") cache_metrics.record_hit(hit=True) cache_metrics.record_hit(hit=False) assert set(created) == { "cache_requests_total", "cache_errors_total", "cache_hit_ratio", } assert created["cache_requests_total"].records == [ (1, {"operation": "get", "outcome": "hit"}) ] assert created["cache_errors_total"].records == [ (1, {"operation": "set", "error.type": "ConnectionError"}) ] assert created["cache_hit_ratio"].records == [(1.0, None), (0.0, None)]