package observability import ( "strings" "testing" "time" ) func TestMetricsNormalizesOperationsAndExportsBoundedLabels(t *testing.T) { m := NewMetrics() m.ObserveAPI("queue", 201, 10*time.Millisecond) m.ObserveAPI("/crafted/path/with-secret", 500, time.Second) var output strings.Builder if err := m.WritePrometheus(&output); err != nil { t.Fatal(err) } text := output.String() if !strings.Contains(text, `operation="queue",status="2xx"`) || !strings.Contains(text, `operation="other",status="5xx"`) { t.Fatalf("metrics output = %s", text) } if !strings.Contains(text, "# TYPE cosmic_clash_api_latency_seconds histogram") || !strings.Contains(text, `cosmic_clash_api_latency_seconds_bucket{operation="queue",status="2xx",le="0.25"} 1`) || !strings.Contains(text, `cosmic_clash_api_latency_seconds_bucket{operation="queue",status="2xx",le="+Inf"} 1`) { t.Fatalf("latency histogram missing expected buckets: %s", text) } if strings.Contains(text, "crafted") || strings.Contains(text, "secret") { t.Fatalf("unbounded operation label leaked: %s", text) } } func TestMetricsHistogramUsesCumulativeBoundarySemantics(t *testing.T) { m := NewMetrics() m.ObserveAPI("queue", 200, 250*time.Millisecond) var output strings.Builder if err := m.WritePrometheus(&output); err != nil { t.Fatal(err) } text := output.String() if !strings.Contains(text, `le="0.25"} 1`) || !strings.Contains(text, `le="0.5"} 1`) { t.Fatalf("boundary observation was not cumulative: %s", text) } if strings.Contains(text, `le="0.1"} 1`) { t.Fatalf("250ms observation entered an earlier bucket: %s", text) } } func TestMetricsServerConflictsAreCountedByKindAndBounded(t *testing.T) { m := NewMetrics() m.ObserveServerConflict("register") m.ObserveServerConflict("register") m.ObserveServerConflict("result") m.ObserveServerConflict("crafted-unknown-kind") var output strings.Builder if err := m.WritePrometheus(&output); err != nil { t.Fatal(err) } text := output.String() if !strings.Contains(text, "# TYPE cosmic_clash_api_server_conflicts_total counter") { t.Fatalf("missing conflict counter TYPE line: %s", text) } if !strings.Contains(text, `cosmic_clash_api_server_conflicts_total{kind="register"} 2`) { t.Fatalf("register conflicts not counted correctly: %s", text) } if !strings.Contains(text, `cosmic_clash_api_server_conflicts_total{kind="result"} 1`) { t.Fatalf("result conflicts not counted correctly: %s", text) } if !strings.Contains(text, `cosmic_clash_api_server_conflicts_total{kind="other"} 1`) { t.Fatalf("unknown kind was not folded into the bounded 'other' label: %s", text) } if strings.Contains(text, "crafted-unknown-kind") { t.Fatalf("unbounded conflict kind label leaked: %s", text) } } func TestMetricsServerConflictAbsentWhenUnobserved(t *testing.T) { m := NewMetrics() m.ObserveAPI("queue", 200, time.Millisecond) var output strings.Builder if err := m.WritePrometheus(&output); err != nil { t.Fatal(err) } if strings.Contains(output.String(), "cosmic_clash_api_server_conflicts_total") { t.Fatalf("conflict counter should be omitted entirely until first observed: %s", output.String()) } } func TestMetricsServerConflictNilReceiverIsANoop(t *testing.T) { var m *Metrics m.ObserveServerConflict("register") // must not panic }