122 lines
3.7 KiB
Go
122 lines
3.7 KiB
Go
package metrics
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"sync/atomic"
|
|
"testing"
|
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
)
|
|
|
|
func TestHandlerServesMetricsAndIndependentProbes(t *testing.T) {
|
|
t.Parallel()
|
|
registry := prometheus.NewRegistry()
|
|
gauge := prometheus.NewGauge(prometheus.GaugeOpts{Name: "proxy_pool_test_inventory"})
|
|
gauge.Set(7)
|
|
registry.MustRegister(gauge)
|
|
readiness := &recordingReadiness{}
|
|
handler, err := NewHandler(Dependencies{Gatherer: registry, Readiness: readiness})
|
|
if err != nil {
|
|
t.Fatalf("NewHandler() error = %v", err)
|
|
}
|
|
|
|
server := httptest.NewServer(handler)
|
|
t.Cleanup(server.Close)
|
|
assertProbe(t, http.MethodGet, server.URL+"/livez", http.StatusOK, "live")
|
|
if readiness.calls.Load() != 0 {
|
|
t.Fatalf("livez readiness calls = %d, want 0", readiness.calls.Load())
|
|
}
|
|
assertProbe(t, http.MethodGet, server.URL+"/readyz", http.StatusOK, "ready")
|
|
if readiness.calls.Load() != 1 {
|
|
t.Fatalf("readyz readiness calls = %d, want 1", readiness.calls.Load())
|
|
}
|
|
|
|
response, err := http.Get(server.URL + "/metrics")
|
|
if err != nil {
|
|
t.Fatalf("GET /metrics: %v", err)
|
|
}
|
|
defer response.Body.Close()
|
|
body, err := io.ReadAll(response.Body)
|
|
if err != nil {
|
|
t.Fatalf("ReadAll(/metrics): %v", err)
|
|
}
|
|
if response.StatusCode != http.StatusOK || !strings.Contains(string(body), "proxy_pool_test_inventory 7") {
|
|
t.Fatalf("GET /metrics = %d %q", response.StatusCode, body)
|
|
}
|
|
}
|
|
|
|
func TestHandlerFailsReadyWithoutLeakingDependencyError(t *testing.T) {
|
|
t.Parallel()
|
|
const secret = "redis://user:secret@redis:6379"
|
|
handler, err := NewHandler(Dependencies{
|
|
Gatherer: prometheus.NewRegistry(),
|
|
Readiness: &recordingReadiness{err: errors.New(secret)},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("NewHandler() error = %v", err)
|
|
}
|
|
recorder := httptest.NewRecorder()
|
|
handler.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/readyz", nil))
|
|
if recorder.Code != http.StatusServiceUnavailable || strings.Contains(recorder.Body.String(), secret) {
|
|
t.Fatalf("GET /readyz = %d %q", recorder.Code, recorder.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestHandlerRejectsInvalidDependenciesMethodsAndPaths(t *testing.T) {
|
|
t.Parallel()
|
|
if _, err := NewHandler(Dependencies{}); !errors.Is(err, ErrInvalidDependencies) {
|
|
t.Fatalf("NewHandler(empty) error = %v", err)
|
|
}
|
|
handler, err := NewHandler(Dependencies{
|
|
Gatherer: prometheus.NewRegistry(), Readiness: &recordingReadiness{},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("NewHandler() error = %v", err)
|
|
}
|
|
recorder := httptest.NewRecorder()
|
|
handler.ServeHTTP(recorder, httptest.NewRequest(http.MethodPost, "/livez", nil))
|
|
if recorder.Code != http.StatusMethodNotAllowed || recorder.Header().Get("Allow") != http.MethodGet {
|
|
t.Fatalf("POST /livez = %d Allow=%q", recorder.Code, recorder.Header().Get("Allow"))
|
|
}
|
|
recorder = httptest.NewRecorder()
|
|
handler.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/missing", nil))
|
|
if recorder.Code != http.StatusNotFound {
|
|
t.Fatalf("GET /missing = %d", recorder.Code)
|
|
}
|
|
}
|
|
|
|
type recordingReadiness struct {
|
|
calls atomic.Int64
|
|
err error
|
|
}
|
|
|
|
func (readiness *recordingReadiness) Ready(context.Context) error {
|
|
readiness.calls.Add(1)
|
|
return readiness.err
|
|
}
|
|
|
|
func assertProbe(t *testing.T, method, target string, wantStatus int, wantBody string) {
|
|
t.Helper()
|
|
request, err := http.NewRequest(method, target, nil)
|
|
if err != nil {
|
|
t.Fatalf("NewRequest(): %v", err)
|
|
}
|
|
response, err := http.DefaultClient.Do(request)
|
|
if err != nil {
|
|
t.Fatalf("Do(%s): %v", target, err)
|
|
}
|
|
defer response.Body.Close()
|
|
body, err := io.ReadAll(response.Body)
|
|
if err != nil {
|
|
t.Fatalf("ReadAll(%s): %v", target, err)
|
|
}
|
|
if response.StatusCode != wantStatus || strings.TrimSpace(string(body)) != wantBody {
|
|
t.Fatalf("%s = %d %q", target, response.StatusCode, body)
|
|
}
|
|
}
|