from __future__ import annotations import pytest from yuxi.channel.metrics import ( Counter, Gauge, Histogram, channel_active_connections, channel_delivery_duration_seconds, channel_messages_delivered_total, channel_messages_failed_total, channel_messages_received_total, channel_rate_limited_total, channel_reconnect_total, dump, ) class TestCounter: def test_inc_without_labels(self): counter = Counter("test_counter") counter.inc() assert counter.get() == 1 def test_inc_with_labels(self): counter = Counter("test_counter") counter.inc(labels={"channel": "feishu"}) counter.inc(labels={"channel": "feishu"}) assert counter.get(labels={"channel": "feishu"}) == 2 def test_inc_with_amount(self): counter = Counter("test_counter") counter.inc(amount=5) assert counter.get() == 5 def test_inc_negative_raises(self): counter = Counter("test_counter") try: counter.inc(amount=-1) assert False, "expected ValueError" except ValueError as exc: assert "non-negative" in str(exc) def test_different_labels_are_isolated(self): counter = Counter("test_counter") counter.inc(labels={"a": "1"}) counter.inc(labels={"b": "2"}) assert counter.get(labels={"a": "1"}) == 1 assert counter.get(labels={"b": "2"}) == 1 assert counter.get() == 0 class TestGauge: def test_inc_and_dec(self): gauge = Gauge("test_gauge") gauge.inc() gauge.inc() gauge.dec() assert gauge.get() == 1 def test_set(self): gauge = Gauge("test_gauge") gauge.set(value=42) assert gauge.get() == 42 def test_with_labels(self): gauge = Gauge("test_gauge") gauge.inc(labels={"channel": "slack"}, amount=3) gauge.dec(labels={"channel": "slack"}) assert gauge.get(labels={"channel": "slack"}) == 2 class TestHistogram: def test_observe_count_and_sum(self): hist = Histogram("test_hist") hist.observe(value=0.05) hist.observe(value=1.5) result = hist.get() assert result["count"] == 2 assert result["sum"] == pytest.approx(1.55) def test_buckets(self): hist = Histogram("test_hist", buckets=(0.1, 1.0, 10.0)) hist.observe(value=0.05) hist.observe(value=0.5) hist.observe(value=5.0) hist.observe(value=20.0) result = hist.get() assert result["buckets"][0] == 1 # <= 0.1 assert result["buckets"][1] == 2 # <= 1.0 assert result["buckets"][2] == 3 # <= 10.0 assert result["buckets"][3] == 4 # > 10.0 def test_empty_get(self): hist = Histogram("test_hist") result = hist.get() assert result["count"] == 0 assert result["sum"] == 0 assert all(v == 0 for v in result["buckets"].values()) def test_with_labels(self): hist = Histogram("test_hist") hist.observe(labels={"channel": "dingtalk"}, value=0.5) result = hist.get(labels={"channel": "dingtalk"}) assert result["count"] == 1 class TestGlobalMetrics: def test_global_counters_exist(self): assert channel_messages_received_total.name == "channel_messages_received_total" assert channel_messages_delivered_total.name == "channel_messages_delivered_total" assert channel_messages_failed_total.name == "channel_messages_failed_total" assert channel_reconnect_total.name == "channel_reconnect_total" assert channel_rate_limited_total.name == "channel_rate_limited_total" def test_global_gauge_exists(self): assert channel_active_connections.name == "channel_active_connections" def test_global_histogram_exists(self): assert channel_delivery_duration_seconds.name == "channel_delivery_duration_seconds" class TestDump: def test_dump_structure(self): channel_messages_received_total.inc(labels={"channel_type": "feishu"}) channel_active_connections.set(labels={"channel_type": "slack"}, value=2) channel_delivery_duration_seconds.observe(value=0.1) snapshot = dump() assert "counters" in snapshot assert "gauges" in snapshot assert "histograms" in snapshot assert "channel_messages_received_total" in snapshot["counters"] assert "channel_active_connections" in snapshot["gauges"] assert "channel_delivery_duration_seconds" in snapshot["histograms"] def test_dump_label_formatting(self): counter = Counter("fmt_counter") counter.inc(labels={"a": "1", "b": "2"}) from yuxi.channel.metrics import _format_labels key = tuple(sorted({"a": "1", "b": "2"}.items())) assert _format_labels(key) == 'a="1",b="2"'