package securityevents import ( "context" "net/http" "net/http/httptest" "strings" "testing" "time" ) type metricsSnapshot struct { mode string last time.Time } func (m metricsSnapshot) SecurityEventMetrics(context.Context, string, string) (string, *time.Time, error) { return m.mode, &m.last, nil } func TestMetricsExposeBoundedOutcomesAndState(t *testing.T) { metrics := &Metrics{} metrics.ObserveReceipt("accepted", 20*time.Millisecond, 2) metrics.ObserveReceipt("duplicate", 10*time.Millisecond, 0) metrics.ObserveWatermarkRejection() metrics.ObserveHeartbeat("accepted") metrics.ObserveIntrospection("failed") metrics.ObserveJWKSRefreshFailure("ssf") recorder := httptest.NewRecorder() metrics.Handler(metricsSnapshot{mode: "push_healthy", last: time.Now().Add(-time.Minute)}, "issuer", "audience", true). ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/metrics", nil)) if recorder.Code != http.StatusOK { t.Fatalf("metrics status=%d", recorder.Code) } for _, expected := range []string{ `easyai_gateway_ssf_receipts_total{outcome="accepted"} 1`, `easyai_gateway_ssf_receipts_total{outcome="duplicate"} 1`, `easyai_gateway_ssf_sessions_deleted_total 2`, `easyai_gateway_ssf_watermark_rejections_total 1`, `easyai_gateway_ssf_processing_duration_seconds_bucket{le="0.05"} 2`, `easyai_gateway_ssf_mode{mode="push_healthy"} 1`, `easyai_gateway_oidc_introspection_total{outcome="failed"} 1`, `easyai_gateway_jwks_refresh_failures_total{outcome="ssf"} 1`, } { if !strings.Contains(recorder.Body.String(), expected) { t.Fatalf("missing metric %q in:\n%s", expected, recorder.Body.String()) } } }