141 lines
5.7 KiB
Python
141 lines
5.7 KiB
Python
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import pytest
|
||
|
|
|
||
|
|
from yuxi.channels.adapters.nostr.metrics import NostrMetrics, MetricsSnapshot
|
||
|
|
|
||
|
|
|
||
|
|
class TestNostrMetrics:
|
||
|
|
@pytest.fixture
|
||
|
|
def metrics(self):
|
||
|
|
return NostrMetrics()
|
||
|
|
|
||
|
|
def test_increment_counter(self, metrics):
|
||
|
|
metrics.increment("test.counter", 1)
|
||
|
|
assert metrics.get_counter("test.counter") == 1
|
||
|
|
|
||
|
|
def test_increment_multiple(self, metrics):
|
||
|
|
metrics.increment("test.counter", 5)
|
||
|
|
metrics.increment("test.counter", 3)
|
||
|
|
assert metrics.get_counter("test.counter") == 8
|
||
|
|
|
||
|
|
def test_increment_default_value(self, metrics):
|
||
|
|
metrics.increment("default_val")
|
||
|
|
assert metrics.get_counter("default_val") == 1
|
||
|
|
|
||
|
|
def test_set_gauge(self, metrics):
|
||
|
|
metrics.set_gauge("test.gauge", 42.0)
|
||
|
|
assert metrics.get_gauge("test.gauge") == 42.0
|
||
|
|
|
||
|
|
def test_adjust_gauge_positive(self, metrics):
|
||
|
|
metrics.set_gauge("test.gauge", 10.0)
|
||
|
|
metrics.adjust_gauge("test.gauge", 5.0)
|
||
|
|
assert metrics.get_gauge("test.gauge") == 15.0
|
||
|
|
|
||
|
|
def test_adjust_gauge_negative(self, metrics):
|
||
|
|
metrics.set_gauge("test.gauge", 10.0)
|
||
|
|
metrics.adjust_gauge("test.gauge", -3.0)
|
||
|
|
assert metrics.get_gauge("test.gauge") == 7.0
|
||
|
|
|
||
|
|
def test_snapshot_contains_counters(self, metrics):
|
||
|
|
metrics.increment("event.count", 10)
|
||
|
|
metrics.set_gauge("memory.size", 1024.0)
|
||
|
|
snapshot = metrics.snapshot()
|
||
|
|
assert isinstance(snapshot, MetricsSnapshot)
|
||
|
|
assert snapshot.counters["event.count"] == 10
|
||
|
|
assert snapshot.gauges["memory.size"] == 1024.0
|
||
|
|
assert snapshot.timestamp > 0
|
||
|
|
|
||
|
|
def test_reset_clears_all(self, metrics):
|
||
|
|
metrics.increment("counter1", 100)
|
||
|
|
metrics.set_gauge("gauge1", 50.0)
|
||
|
|
metrics.reset()
|
||
|
|
assert metrics.get_counter("counter1") == 0
|
||
|
|
assert metrics.get_gauge("gauge1") == 0.0
|
||
|
|
|
||
|
|
def test_record_event_received(self, metrics):
|
||
|
|
metrics.record_event_received(1)
|
||
|
|
metrics.record_event_received(4)
|
||
|
|
assert metrics.get_counter("event.received.total") == 2
|
||
|
|
assert metrics.get_counter("event.received.kind_1") == 1
|
||
|
|
assert metrics.get_counter("event.received.kind_4") == 1
|
||
|
|
|
||
|
|
def test_record_event_rejected(self, metrics):
|
||
|
|
metrics.record_event_rejected("duplicate")
|
||
|
|
metrics.record_event_rejected("rate_limit")
|
||
|
|
assert metrics.get_counter("event.rejected.total") == 2
|
||
|
|
assert metrics.get_counter("event.rejected.duplicate") == 1
|
||
|
|
|
||
|
|
def test_record_event_accepted(self, metrics):
|
||
|
|
metrics.record_event_accepted()
|
||
|
|
metrics.record_event_accepted()
|
||
|
|
assert metrics.get_counter("event.accepted.total") == 2
|
||
|
|
|
||
|
|
def test_record_relay_connect(self, metrics):
|
||
|
|
metrics.record_relay_connect("wss://relay.example.com")
|
||
|
|
assert metrics.get_counter("relay.connect.total") == 1
|
||
|
|
|
||
|
|
def test_record_relay_disconnect(self, metrics):
|
||
|
|
metrics.record_relay_disconnect("wss://relay.example.com")
|
||
|
|
assert metrics.get_counter("relay.disconnect.total") == 1
|
||
|
|
|
||
|
|
def test_record_relay_send_success(self, metrics):
|
||
|
|
metrics.record_relay_send_success("wss://relay.example.com")
|
||
|
|
assert metrics.get_counter("relay.send.success.total") == 1
|
||
|
|
|
||
|
|
def test_record_relay_send_failure(self, metrics):
|
||
|
|
metrics.record_relay_send_failure("wss://relay.example.com")
|
||
|
|
assert metrics.get_counter("relay.send.failure.total") == 1
|
||
|
|
|
||
|
|
def test_record_circuit_breaker_state(self, metrics):
|
||
|
|
metrics.record_circuit_breaker_state("wss://relay.example.com", "closed")
|
||
|
|
assert metrics.get_gauge("circuit_breaker.wss_relay_example_com.state") == 0
|
||
|
|
metrics.record_circuit_breaker_state("wss://relay.example.com", "open")
|
||
|
|
assert metrics.get_gauge("circuit_breaker.wss_relay_example_com.state") == 2
|
||
|
|
|
||
|
|
def test_record_rate_limit_hit_sender(self, metrics):
|
||
|
|
metrics.record_rate_limit_hit(sender_pubkey="abc123", global_hit=False)
|
||
|
|
assert metrics.get_counter("rate_limit.sender.hit") == 1
|
||
|
|
|
||
|
|
def test_record_rate_limit_hit_global(self, metrics):
|
||
|
|
metrics.record_rate_limit_hit(global_hit=True)
|
||
|
|
assert metrics.get_counter("rate_limit.global.hit") == 1
|
||
|
|
|
||
|
|
def test_record_decrypt_success_nip04(self, metrics):
|
||
|
|
metrics.record_decrypt_success("nip04")
|
||
|
|
assert metrics.get_counter("decrypt.nip04.success") == 1
|
||
|
|
|
||
|
|
def test_record_decrypt_success_nip17(self, metrics):
|
||
|
|
metrics.record_decrypt_success("nip17")
|
||
|
|
assert metrics.get_counter("decrypt.nip17.success") == 1
|
||
|
|
|
||
|
|
def test_record_decrypt_failure(self, metrics):
|
||
|
|
metrics.record_decrypt_failure("nip04")
|
||
|
|
assert metrics.get_counter("decrypt.nip04.failure") == 1
|
||
|
|
|
||
|
|
def test_record_seen_tracker_size(self, metrics):
|
||
|
|
metrics.record_seen_tracker_size(500)
|
||
|
|
assert metrics.get_gauge("memory.seen_tracker_size") == 500
|
||
|
|
|
||
|
|
def test_record_inflight_size(self, metrics):
|
||
|
|
metrics.record_inflight_size(10)
|
||
|
|
assert metrics.get_gauge("memory.inflight_size") == 10
|
||
|
|
|
||
|
|
def test_safe_label_replaces_special_chars(self):
|
||
|
|
result = NostrMetrics._safe_label("wss://relay.example.com/path/")
|
||
|
|
assert "://" not in result
|
||
|
|
assert "." not in result
|
||
|
|
assert "/" not in result
|
||
|
|
|
||
|
|
def test_snapshot_is_independent(self, metrics):
|
||
|
|
metrics.increment("counter_x", 5)
|
||
|
|
snap = metrics.snapshot()
|
||
|
|
metrics.increment("counter_x", 10)
|
||
|
|
assert snap.counters["counter_x"] == 5
|
||
|
|
assert metrics.get_counter("counter_x") == 15
|
||
|
|
|
||
|
|
def test_get_nonexistent_counter(self, metrics):
|
||
|
|
assert metrics.get_counter("nonexistent") == 0
|
||
|
|
|
||
|
|
def test_get_nonexistent_gauge(self, metrics):
|
||
|
|
assert metrics.get_gauge("nonexistent") == 0.0
|