Bundled commit across unrelated threads: perf collector/meter/logs/export
updates, SSO bootstrap and traefik/nginx/gitea infra, dev backend scripts
(gitea/github/woodpecker), diag test updates, and misc config.
Also fix test pollution: five tests were doing raw sys.modules.pop("conf")
to simulate ImportError on `import conf`, but raw dict mutation isn't
tracked by monkeypatch and leaked the eviction into subsequent tests.
Downstream tests that did `from conf import secret` at module level held
references to the pre-eviction conf, while re-imports inside tests got a
fresh conf, so patches to conf.cfg._data didn't land on the secret()
closure's cfg — causing tests/conf/test_conf.py::TestSecret and
tests/conf/test_connect.py::TestDuckdb::test_custom_db_name to fail.
Fix: use monkeypatch.delitem(sys.modules, "conf", raising=False) so the
eviction is reverted on teardown. Applied to test_diag_ci.py, test_init.py,
test_tracer.py, and test_resource.py (two sites).
268 lines
8.3 KiB
Python
268 lines
8.3 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()
|
|
|
|
def test_init_idempotent(self, monkeypatch):
|
|
monkeypatch.setenv("STACK_TELEMETRY", "false")
|
|
import perf
|
|
|
|
perf._initialised = True
|
|
perf.init() # early-return path
|
|
assert perf._initialised is True
|
|
perf._initialised = False
|
|
|
|
def test_init_and_shutdown_when_enabled(self, monkeypatch):
|
|
monkeypatch.setenv("STACK_TELEMETRY", "true")
|
|
from opentelemetry import trace
|
|
from opentelemetry.metrics import _internal as metrics_internal
|
|
|
|
import perf
|
|
import perf._meter as meter_mod
|
|
import perf._tracer as tracer_mod
|
|
|
|
# Snapshot the global once-flags so we can restore them and let
|
|
# other tests install their own providers afterward.
|
|
trace_done = trace._TRACER_PROVIDER_SET_ONCE._done
|
|
metrics_done = metrics_internal._METER_PROVIDER_SET_ONCE._done
|
|
original_trace_provider = trace._TRACER_PROVIDER
|
|
original_metric_provider = metrics_internal._METER_PROVIDER
|
|
|
|
perf._initialised = False
|
|
meter_mod._provider_ready = False
|
|
tracer_mod._provider_ready = False
|
|
try:
|
|
perf.init()
|
|
assert perf._initialised is True
|
|
assert tracer_mod._provider_ready is True
|
|
assert meter_mod._provider_ready is True
|
|
perf.shutdown()
|
|
assert perf._initialised is False
|
|
finally:
|
|
perf._initialised = False
|
|
meter_mod._provider_ready = False
|
|
tracer_mod._provider_ready = False
|
|
# Restore globals so subsequent tests get a clean slate.
|
|
trace._TRACER_PROVIDER = original_trace_provider
|
|
metrics_internal._METER_PROVIDER = original_metric_provider
|
|
trace._TRACER_PROVIDER_SET_ONCE._done = trace_done
|
|
metrics_internal._METER_PROVIDER_SET_ONCE._done = metrics_done
|
|
|
|
def test_enabled_falls_through_when_conf_unavailable(self, monkeypatch):
|
|
monkeypatch.delenv("STACK_TELEMETRY", raising=False)
|
|
import sys
|
|
|
|
from perf import _enabled
|
|
|
|
real_import = (
|
|
__builtins__["__import__"]
|
|
if isinstance(__builtins__, dict)
|
|
else __builtins__.__import__
|
|
)
|
|
|
|
def fake_import(name, *args, **kwargs):
|
|
if name == "conf":
|
|
raise ImportError("conf unavailable")
|
|
return real_import(name, *args, **kwargs)
|
|
|
|
monkeypatch.setattr("builtins.__import__", fake_import)
|
|
monkeypatch.delitem(sys.modules, "conf", raising=False)
|
|
assert _enabled() is False
|
|
|
|
|
|
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)
|