owpengram-server/internal/observability/metrics/registry_test.go
2026-09-01 12:06:31 +03:00

182 lines
6.6 KiB
Go

package metrics
import (
"errors"
"net/http/httptest"
"strings"
"testing"
"time"
"telesrv/internal/mtprotoedge"
"telesrv/internal/rpc"
)
var (
_ mtprotoedge.Metrics = (*Registry)(nil)
_ mtprotoedge.RPCDatabaseMetrics = (*Registry)(nil)
_ mtprotoedge.RPCResultMetrics = (*Registry)(nil)
_ mtprotoedge.LogicalOutboxMetrics = (*Registry)(nil)
_ mtprotoedge.ConnectionIntakeMetrics = (*Registry)(nil)
_ rpc.Metrics = (*Registry)(nil)
)
func TestRegistryExportsRPCDatabaseWork(t *testing.T) {
registry := New()
registry.RPCDatabase("messages.getDialogs", 17, 25*time.Millisecond, 0)
recorder := httptest.NewRecorder()
registry.ServeHTTP(recorder, httptest.NewRequest("GET", "/metrics", nil))
body := recorder.Body.String()
for _, want := range []string{
`telesrv_rpc_db_queries_total{method="messages.getDialogs"} 17`,
`telesrv_rpc_db_time_seconds_sum{method="messages.getDialogs"} 0.025`,
`telesrv_rpc_db_time_seconds_count{method="messages.getDialogs"} 1`,
} {
if !strings.Contains(body, want) {
t.Fatalf("missing %q from:\n%s", want, body)
}
}
}
func TestRegistryExportsPresenceLastSeenBatchWork(t *testing.T) {
registry := New()
registry.PresenceLastSeenBatch(37, 25*time.Millisecond, nil)
registry.PresenceLastSeenBatch(12, 50*time.Millisecond, errors.New("temporary"))
registry.PresenceLastSeenSubmitted()
registry.PresenceLastSeenSubmitted()
registry.PresenceLastSeenPending(9)
registry.PresenceLastSeenPending(-4)
registry.PresenceLastSeenOverflow()
registry.PresenceLastSeenDrainDropped(3)
recorder := httptest.NewRecorder()
registry.ServeHTTP(recorder, httptest.NewRequest("GET", "/metrics", nil))
body := recorder.Body.String()
for _, want := range []string{
`telesrv_presence_last_seen_batches_total{outcome="ok"} 1`,
`telesrv_presence_last_seen_batches_total{outcome="error"} 1`,
`telesrv_presence_last_seen_updates_total{outcome="ok"} 37`,
`telesrv_presence_last_seen_updates_total{outcome="error"} 12`,
`telesrv_presence_last_seen_submitted_total 2`,
`telesrv_presence_last_seen_pending 5`,
`telesrv_presence_last_seen_overflow_total 1`,
`telesrv_presence_last_seen_drain_dropped_total 3`,
} {
if !strings.Contains(body, want) {
t.Fatalf("missing %q from:\n%s", want, body)
}
}
}
func TestRegistryExportsBootstrapReadyBatchWork(t *testing.T) {
registry := New()
registry.BootstrapReadyPending(7)
registry.BootstrapReadyBatch(7, 2, 25*time.Millisecond, nil)
registry.BootstrapReadyPending(-7)
registry.BootstrapReadyBatch(3, 0, 50*time.Millisecond, errors.New("temporary"))
recorder := httptest.NewRecorder()
registry.ServeHTTP(recorder, httptest.NewRequest("GET", "/metrics", nil))
body := recorder.Body.String()
for _, want := range []string{
`telesrv_bootstrap_ready_batches_total{outcome="ok"} 1`,
`telesrv_bootstrap_ready_batches_total{outcome="error"} 1`,
`telesrv_bootstrap_ready_selectors_total{outcome="matched"} 2`,
`telesrv_bootstrap_ready_selectors_total{outcome="miss"} 5`,
`telesrv_bootstrap_ready_selectors_total{outcome="error"} 3`,
`telesrv_bootstrap_ready_pending 0`,
} {
if !strings.Contains(body, want) {
t.Fatalf("missing %q from:\n%s", want, body)
}
}
}
func TestRegistryExportsActiveChannelIDsReadModelWork(t *testing.T) {
registry := New()
registry.ActiveChannelIDsCache("hit")
registry.ActiveChannelIDsCache("miss")
registry.ActiveChannelIDsCache("served")
registry.ActiveChannelIDsPending(7)
registry.ActiveChannelIDsBatch(7, 19, 25*time.Millisecond, nil)
registry.ActiveChannelIDsPending(-7)
registry.ActiveChannelIDsBatch(3, 0, 50*time.Millisecond, errors.New("temporary"))
recorder := httptest.NewRecorder()
registry.ServeHTTP(recorder, httptest.NewRequest("GET", "/metrics", nil))
body := recorder.Body.String()
for _, want := range []string{
`telesrv_active_channel_ids_cache_total{outcome="hit"} 1`,
`telesrv_active_channel_ids_cache_total{outcome="miss"} 1`,
`telesrv_active_channel_ids_cache_total{outcome="served"} 1`,
`telesrv_active_channel_ids_batches_total{outcome="ok"} 1`,
`telesrv_active_channel_ids_batches_total{outcome="error"} 1`,
`telesrv_active_channel_ids_selectors_total{outcome="ok"} 7`,
`telesrv_active_channel_ids_selectors_total{outcome="error"} 3`,
`telesrv_active_channel_ids_rows_total 19`,
`telesrv_active_channel_ids_pending 0`,
} {
if !strings.Contains(body, want) {
t.Fatalf("missing %q from:\n%s", want, body)
}
}
}
func TestRegistryExportsBoundedAggregateMetrics(t *testing.T) {
registry := New()
registry.maxSeries = 2
registry.RPCHandled("help.getConfig", 5*time.Millisecond, nil)
registry.RPCHandled("users.getUsers", time.Second, errors.New("secret auth_key_id=deadbeef session=123"))
recorder := httptest.NewRecorder()
registry.ServeHTTP(recorder, httptest.NewRequest("GET", "/metrics", nil))
body := recorder.Body.String()
if recorder.Code != 200 {
t.Fatalf("status = %d, want 200", recorder.Code)
}
if !strings.Contains(body, "telesrv_mtproto_rpc_handled_total") || !strings.Contains(body, "telesrv_mtproto_rpc_duration_seconds_bucket") {
t.Fatalf("expected RPC counter and histogram, got:\n%s", body)
}
if strings.Contains(body, "deadbeef") || strings.Contains(body, "session=123") {
t.Fatalf("raw error identity leaked into metrics:\n%s", body)
}
if got := registry.series.Load(); got != registry.maxSeries {
t.Fatalf("resident dynamic series = %d, want cap %d", got, registry.maxSeries)
}
if got := registry.dropped.Load(); got == 0 {
t.Fatal("series overflow was not reported")
}
if !strings.Contains(body, "telesrv_metrics_dropped_observations_total 2") {
t.Fatalf("overflow counter missing from:\n%s", body)
}
}
func TestRegistrySanitizesAndBoundsProviderSamples(t *testing.T) {
registry := New()
registry.AddGaugeProvider(func() []GaugeSample {
return []GaugeSample{{
Name: "9 invalid metric",
Labels: []Label{{Name: "bad:label", Value: "quoted\"\nvalue"}},
Value: 3,
}}
})
recorder := httptest.NewRecorder()
registry.ServeHTTP(recorder, httptest.NewRequest("GET", "/metrics", nil))
body := recorder.Body.String()
if !strings.Contains(body, `__invalid_metric{bad_label="quoted\"\nvalue"} 3`) {
t.Fatalf("provider sample was not safely sanitized:\n%s", body)
}
}
func TestErrorOutcomeHasFixedCardinality(t *testing.T) {
tests := []struct {
err error
want string
}{
{nil, "ok"},
{errors.New("FLOOD_WAIT_1 for phone 123"), "flood_wait"},
{errors.New("global capacity exceeded for auth key"), "edge_overload"},
{errors.New("arbitrary user-controlled failure"), "error"},
}
for _, test := range tests {
if got := errorOutcome(test.err); got != test.want {
t.Errorf("errorOutcome(%v) = %q, want %q", test.err, got, test.want)
}
}
}