The Prometheus monitoring system and time series database.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
prometheus/web/api/testhelpers/request.go

145 lines
4.1 KiB

// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// This file provides HTTP request builders for testing API endpoints.
package testhelpers
import (
"encoding/json"
"io"
"net/http"
"net/http/httptest"
"net/url"
"strings"
"testing"
)
// Response wraps an HTTP response with parsed JSON data.
// It supports method chaining for assertions.
//
// Example usage:
//
// testhelpers.GET(t, api, "/api/v1/query", "query", "up").
// ValidateOpenAPI().
// RequireSuccess().
// RequireEquals("$.data.resultType", "vector").
// RequireLenAtLeast("$.data.result", 1)
//
// testhelpers.POST(t, api, "/api/v1/query", "query", "up").
// ValidateOpenAPI().
// RequireSuccess().
// RequireArrayContains("$.data.result", expectedValue)
type Response struct {
StatusCode int
Body string
JSON map[string]any
t *testing.T
request *http.Request
requestBody []byte
responseHeader http.Header
}
// GET sends a GET request to the API and returns a Response with parsed JSON.
// queryParams should be pairs of key-value strings.
func GET(t *testing.T, api *APIWrapper, path string, queryParams ...string) *Response {
t.Helper()
if len(queryParams)%2 != 0 {
t.Fatal("queryParams must be key-value pairs")
}
// Build query string.
values := url.Values{}
for i := 0; i < len(queryParams); i += 2 {
values.Add(queryParams[i], queryParams[i+1])
}
fullPath := path
if len(values) > 0 {
fullPath = path + "?" + values.Encode()
}
req := httptest.NewRequest(http.MethodGet, fullPath, nil)
return executeRequest(t, api, req)
}
// POST sends a POST request to the API with the given body and returns a Response with parsed JSON.
// bodyParams should be pairs of key-value strings for form data.
func POST(t *testing.T, api *APIWrapper, path string, bodyParams ...string) *Response {
t.Helper()
if len(bodyParams)%2 != 0 {
t.Fatal("bodyParams must be key-value pairs")
}
// Build form data.
values := url.Values{}
for i := 0; i < len(bodyParams); i += 2 {
values.Add(bodyParams[i], bodyParams[i+1])
}
req := httptest.NewRequest(http.MethodPost, path, strings.NewReader(values.Encode()))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
return executeRequest(t, api, req)
}
// executeRequest executes an HTTP request and parses the response as JSON.
func executeRequest(t *testing.T, api *APIWrapper, req *http.Request) *Response {
t.Helper()
// Capture the request body for validation.
var requestBody []byte
if req.Body != nil {
var err error
requestBody, err = io.ReadAll(req.Body)
if err != nil {
t.Fatalf("failed to read request body: %v", err)
}
// Restore the body for the actual request.
req.Body = io.NopCloser(strings.NewReader(string(requestBody)))
}
recorder := httptest.NewRecorder()
api.Handler.ServeHTTP(recorder, req)
result := recorder.Result()
defer result.Body.Close()
bodyBytes, err := io.ReadAll(result.Body)
if err != nil {
t.Fatalf("failed to read response body: %v", err)
}
resp := &Response{
StatusCode: result.StatusCode,
Body: string(bodyBytes),
t: t,
request: req,
requestBody: requestBody,
responseHeader: result.Header,
}
// Try to parse as JSON.
if result.Header.Get("Content-Type") == "application/json" || strings.Contains(result.Header.Get("Content-Type"), "application/json") {
var jsonData map[string]any
if err := json.Unmarshal(bodyBytes, &jsonData); err != nil {
// If JSON parsing fails, leave JSON as nil.
// This allows tests to handle non-JSON responses.
resp.JSON = nil
} else {
resp.JSON = jsonData
}
}
return resp
}