package metricquery import ( "context" "encoding/json" "net/http" "net/http/httptest" "strings" "testing" "time" "github.com/itworx/pulse/internal/auth" "github.com/itworx/pulse/internal/metriccatalog" "github.com/itworx/pulse/internal/prometheus" "github.com/itworx/pulse/internal/queryplan" ) func handlerService(t *testing.T) *Service { return testService(t, &fakeSource{result: prometheus.QueryResult{Status: "success", Data: []byte(`{"resultType":"matrix","result":[]}`)}}, NewCache(8, 1<<20)) } func authenticatedRequest(method, path, body string) *http.Request { request := httptest.NewRequest(method, path, strings.NewReader(body)) return request.WithContext(auth.WithPrincipal(context.Background(), auth.Principal{Subject: "viewer", Role: auth.RoleViewer})) } func operatorRequest(method, path, body string) *http.Request { request := httptest.NewRequest(method, path, strings.NewReader(body)) return request.WithContext(auth.WithPrincipal(context.Background(), auth.Principal{Subject: "operator", Role: auth.RoleOperator})) } func TestHandlerRequiresAuthAndMapsUnavailableService(t *testing.T) { response := httptest.NewRecorder() Handler{Service: handlerService(t)}.ServeHTTP(response, httptest.NewRequest(http.MethodPost, "/api/v1/metrics/query-range", strings.NewReader(`{}`))) if response.Code != http.StatusUnauthorized { t.Fatalf("status=%d", response.Code) } response = httptest.NewRecorder() Handler{}.ServeHTTP(response, authenticatedRequest(http.MethodPost, "/api/v1/metrics/query-range", `{}`)) if response.Code != http.StatusServiceUnavailable { t.Fatalf("status=%d", response.Code) } } func TestHandlerRejectsMalformedAndOversizedBodies(t *testing.T) { service := handlerService(t) response := httptest.NewRecorder() Handler{Service: service}.ServeHTTP(response, authenticatedRequest(http.MethodPost, "/api/v1/metrics/query-range", `{"metric":`)) if response.Code != http.StatusBadRequest { t.Fatalf("malformed=%d", response.Code) } response = httptest.NewRecorder() oversized := `{"padding":"` + strings.Repeat("x", maxRequestBody) + `"}` Handler{Service: service}.ServeHTTP(response, authenticatedRequest(http.MethodPost, "/api/v1/metrics/query-range", oversized)) if response.Code != http.StatusRequestEntityTooLarge { t.Fatalf("oversized=%d", response.Code) } } func TestHandlerPermissionedInspector(t *testing.T) { service := handlerService(t) payload, _ := json.Marshal(testRequest()) response := httptest.NewRecorder() Handler{Service: service}.ServeHTTP(response, authenticatedRequest(http.MethodPost, "/api/v1/metrics/inspect", string(payload))) if response.Code != http.StatusForbidden { t.Fatalf("viewer status=%d body=%s", response.Code, response.Body.String()) } response = httptest.NewRecorder() Handler{Service: service}.ServeHTTP(response, operatorRequest(http.MethodPost, "/api/v1/metrics/inspect", string(payload))) if response.Code != http.StatusOK { t.Fatalf("operator status=%d body=%s", response.Code, response.Body.String()) } var decoded struct { Inspector Inspector `json:"inspector"` } if err := json.Unmarshal(response.Body.Bytes(), &decoded); err != nil { t.Fatal(err) } if decoded.Inspector.GeneratedQuery == "" || decoded.Inspector.Cost.Series < 1 || decoded.Inspector.Limits.MaxPoints < 1 { t.Fatalf("inspector=%+v", decoded.Inspector) } } func TestHandlerReturnsBoundedQueryResponse(t *testing.T) { service := handlerService(t) request := testRequest() payload, _ := json.Marshal(request) response := httptest.NewRecorder() Handler{Service: service}.ServeHTTP(response, authenticatedRequest(http.MethodPost, "/api/v1/metrics/query-range", string(payload))) if response.Code != http.StatusOK { t.Fatalf("status=%d body=%s", response.Code, response.Body.String()) } var decoded Response if err := json.Unmarshal(response.Body.Bytes(), &decoded); err != nil { t.Fatal(err) } if decoded.Provenance.Source != "prometheus" || decoded.Freshness != "fresh" { t.Fatalf("response=%+v", decoded) } } func TestHandlerMapsSourceErrorsWithoutLeakingDetails(t *testing.T) { service := testService(t, &fakeSource{err: context.DeadlineExceeded}, nil) payload, _ := json.Marshal(testRequest()) response := httptest.NewRecorder() Handler{Service: service}.ServeHTTP(response, authenticatedRequest(http.MethodPost, "/api/v1/metrics/query-range", string(payload))) if response.Code != http.StatusServiceUnavailable || strings.Contains(response.Body.String(), "DeadlineExceeded") { t.Fatalf("status=%d body=%s", response.Code, response.Body.String()) } } func TestHandlerInstantRoute(t *testing.T) { registry, err := metriccatalog.DefaultRegistry() if err != nil { t.Fatal(err) } source := &fakeSource{result: prometheus.QueryResult{Status: "success", Data: []byte(`{}`)}} service := NewService(queryplan.NewPlanner(registry, queryplan.Limits{}), source, nil) payload := `{"metric":"host.cpu.utilization","scope":{"serverId":"server-1"},"at":"` + time.Date(2026, 8, 1, 12, 0, 0, 0, time.UTC).Format(time.RFC3339) + `"}` response := httptest.NewRecorder() Handler{Service: service}.ServeHTTP(response, authenticatedRequest(http.MethodPost, "/api/v1/metrics/query", payload)) if response.Code != http.StatusOK || source.instantCalls != 1 { t.Fatalf("status=%d calls=%d body=%s", response.Code, source.instantCalls, response.Body.String()) } }