Files
stack/tests/perf/test_init.py
kert 7696c1c6da feat: add perf module — OTel pipeline telemetry with Grafana dashboards
Add `perf` as the 12th skinny package with full OpenTelemetry
instrumentation for pipeline traces, Prometheus metrics, and
Loki-correlated logs.

- src/perf/: TracerProvider, MeterProvider, LoggerProvider bridge,
  PipelineCollector, psutil system metrics, FileExporter fallback,
  FastAPI middleware, auto-file Gitea issues on step/test failure
- infra/otel/: OTel Collector fan-out (traces→Jaeger, metrics→Prometheus,
  logs→Loki), all services migrated from jaeger:4317 to collector
- infra/grafana/dashboards/: 8-panel pipeline performance dashboard
- Pipeline runner auto-instruments all 189 steps with zero-cost
  MockTracer/MockSpan no-ops when telemetry is disabled
- 82 new tests, 12,055 total passing

Closes #203, closes #204, closes #205, closes #206, closes #207,
closes #208, closes #209, closes #210, closes #211, closes #212,
closes #213
2026-03-24 21:37:41 -04:00

202 lines
5.8 KiB
Python

"""Tests for the perf package scaffold — public API, mock objects, config."""
from __future__ import annotations
class TestEnabled:
"""perf._enabled() resolves from env var and stack.toml."""
def test_env_true(self, monkeypatch):
monkeypatch.setenv("STACK_TELEMETRY", "true")
from perf import _enabled
assert _enabled() is True
def test_env_false(self, monkeypatch):
monkeypatch.setenv("STACK_TELEMETRY", "false")
from perf import _enabled
assert _enabled() is False
def test_env_1(self, monkeypatch):
monkeypatch.setenv("STACK_TELEMETRY", "1")
from perf import _enabled
assert _enabled() is True
def test_env_0(self, monkeypatch):
monkeypatch.setenv("STACK_TELEMETRY", "0")
from perf import _enabled
assert _enabled() is False
def test_fallback_to_config(self, monkeypatch):
monkeypatch.delenv("STACK_TELEMETRY", raising=False)
from perf import _enabled
# stack.toml has enabled = false by default
assert _enabled() is False
class TestMockTracer:
"""MockTracer and MockSpan are zero-cost no-ops."""
def test_mock_span_context_manager(self):
from perf._tracer import MockSpan
span = MockSpan()
with span as s:
s.set_attribute("key", "value")
s.set_status("OK")
s.record_exception(ValueError("test"))
s.add_event("test_event", {"k": "v"})
def test_mock_tracer_start_as_current_span(self):
from perf._tracer import MockTracer
tracer = MockTracer()
with tracer.start_as_current_span("test_span") as span:
span.set_attribute("rows.out", 100)
def test_mock_tracer_start_span(self):
from perf._tracer import MockTracer
tracer = MockTracer()
span = tracer.start_span("test_span")
assert span is not None
class TestMockMeter:
"""MockMeter instruments are zero-cost no-ops."""
def test_mock_counter(self):
from perf._meter import MockMeter
meter = MockMeter()
counter = meter.create_counter("test_counter")
counter.add(1, {"pipeline": "test"})
def test_mock_histogram(self):
from perf._meter import MockMeter
meter = MockMeter()
hist = meter.create_histogram("test_histogram")
hist.record(0.5, {"step": "test"})
def test_mock_up_down_counter(self):
from perf._meter import MockMeter
meter = MockMeter()
udc = meter.create_up_down_counter("test_udc")
udc.add(-1)
def test_mock_observable_gauge(self):
from perf._meter import MockMeter
meter = MockMeter()
gauge = meter.create_observable_gauge("test_gauge")
gauge.set(42.0)
class TestPublicAPI:
"""perf.tracer() and perf.meter() return mocks when disabled."""
def test_tracer_returns_mock_when_disabled(self, monkeypatch):
monkeypatch.setenv("STACK_TELEMETRY", "false")
from perf import tracer
from perf._tracer import MockTracer
t = tracer("test.scope")
assert isinstance(t, MockTracer)
def test_meter_returns_mock_when_disabled(self, monkeypatch):
monkeypatch.setenv("STACK_TELEMETRY", "false")
from perf import meter
from perf._meter import MockMeter
m = meter("test.scope")
assert isinstance(m, MockMeter)
def test_init_noop_when_disabled(self, monkeypatch):
monkeypatch.setenv("STACK_TELEMETRY", "false")
import perf
perf._initialised = False # reset state
perf.init()
# Should not raise — just a no-op
perf.shutdown()
class TestFileSpanExporter:
"""FileSpanExporter writes JSONL to disk."""
def test_export_writes_jsonl(self, tmp_path):
import json
from unittest.mock import MagicMock
from perf.export import FileSpanExporter
out = tmp_path / "spans.jsonl"
exporter = FileSpanExporter(path=out)
# Build a mock span
mock_ctx = MagicMock()
mock_ctx.trace_id = 0x1234567890ABCDEF1234567890ABCDEF
mock_ctx.span_id = 0xABCDEF1234567890
mock_status = MagicMock()
mock_status.status_code.name = "OK"
mock_span = MagicMock()
mock_span.get_span_context.return_value = mock_ctx
mock_span.name = "test.step"
mock_span.start_time = 1000000000
mock_span.end_time = 2000000000
mock_span.status = mock_status
mock_span.attributes = {"rows.out": 42}
result = exporter.export([mock_span])
assert result == 0
lines = out.read_text().strip().split("\n")
assert len(lines) == 1
record = json.loads(lines[0])
assert record["name"] == "test.step"
assert record["attributes"]["rows.out"] == 42
assert len(record["trace_id"]) == 32
assert len(record["span_id"]) == 16
class TestResource:
"""_resource.py builds OTel-compatible resource attributes."""
def test_service_name_from_env(self, monkeypatch):
monkeypatch.setenv("OTEL_SERVICE_NAME", "my-service")
from perf._resource import _service_name
assert _service_name() == "my-service"
def test_service_name_default(self, monkeypatch):
monkeypatch.delenv("OTEL_SERVICE_NAME", raising=False)
from perf._resource import _service_name
# Falls back to stack.toml or "stack"
name = _service_name()
assert isinstance(name, str)
assert len(name) > 0
def test_git_sha(self):
from perf._resource import _git_sha
sha = _git_sha()
# We're in a git repo, so this should return a short SHA
assert sha is not None
assert len(sha) >= 7
def test_version(self):
from perf._resource import _version
v = _version()
assert isinstance(v, str)