package middleware import ( "log/slog" "maps" "net/http" "net/http/httptest" "testing" "github.com/go-chi/chi/v5" "github.com/prometheus/client_golang/prometheus" "sneak.berlin/go/pixa/internal/config" ) // TestMetricsLabelsRequestsByRoute checks that the metrics middleware labels // each request with the pattern of the route that answers it, never with its // path: two paths one route answers share that route's pattern, and requests // no route answers, a 404 or a 405, share "unmatched". With a label per path, // any client could add series to the registry without limit. The middleware // registers with a registry of its own here, as another test in this package // registers with Prometheus' default registry, which can be done only once. func TestMetricsLabelsRequestsByRoute(t *testing.T) { t.Parallel() registry := prometheus.NewRegistry() mw := &Middleware{ log: slog.Default(), config: &config.Config{}, metricsRegistry: registry, } // The image route is in a subrouter, as in pixa's router. router := chi.NewRouter() router.Use(mw.Metrics()) router.Route("/v1", func(r chi.Router) { r.Get("/image/*", func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusOK) }) }) for _, req := range []struct{ method, path string }{ {http.MethodGet, "/v1/image/a.example.com/one.jpg/1x1.png"}, {http.MethodGet, "/v1/image/b.example.com/two.jpg/1x1.png"}, {http.MethodGet, "/no-such-route"}, {http.MethodGet, "/v1/no-such-route"}, {http.MethodPost, "/v1/image/a.example.com/one.jpg/1x1.png"}, } { router.ServeHTTP(httptest.NewRecorder(), httptest.NewRequestWithContext( t.Context(), req.method, req.path, nil)) } got := requestCountsByHandler(t, registry) t.Logf("requests counted by handler label: %v", got) want := map[string]uint64{"/v1/image/*": 2, "unmatched": 3} if !maps.Equal(got, want) { t.Errorf("requests counted by handler label = %v, want %v", got, want) } } // requestCountsByHandler returns how many requests the request duration // histogram in registry counted under each value of its handler label. func requestCountsByHandler( t *testing.T, registry *prometheus.Registry, ) map[string]uint64 { t.Helper() families, err := registry.Gather() if err != nil { t.Fatalf("registry.Gather() error = %v", err) } counts := map[string]uint64{} for _, family := range families { if family.GetName() != "http_request_duration_seconds" { continue } for _, metric := range family.GetMetric() { for _, label := range metric.GetLabel() { if label.GetName() == "handler" { counts[label.GetValue()] += metric.GetHistogram().GetSampleCount() } } } } return counts }