github.com/letsencrypt/boulder@v0.20251208.0/metrics/measured_http/http_test.go (about)

     1  package measured_http
     2  
     3  import (
     4  	"net/http"
     5  	"net/http/httptest"
     6  	"net/url"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/jmhodges/clock"
    11  	"github.com/prometheus/client_golang/prometheus"
    12  	io_prometheus_client "github.com/prometheus/client_model/go"
    13  )
    14  
    15  type sleepyHandler struct {
    16  	clk clock.FakeClock
    17  }
    18  
    19  func (h sleepyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    20  	h.clk.Sleep(999 * time.Second)
    21  	w.WriteHeader(302)
    22  }
    23  
    24  func collect(m prometheus.Collector) *io_prometheus_client.Metric {
    25  	ch := make(chan prometheus.Metric, 10)
    26  	m.Collect(ch)
    27  	result := <-ch
    28  	var iom = new(io_prometheus_client.Metric)
    29  	_ = result.Write(iom)
    30  	return iom
    31  }
    32  
    33  func TestMeasuring(t *testing.T) {
    34  	clk := clock.NewFake()
    35  
    36  	// Create a local histogram stat with the same labels as the real one, but
    37  	// don't register it; we will collect its data here in the test to verify it.
    38  	stat := prometheus.NewHistogramVec(
    39  		prometheus.HistogramOpts{
    40  			Name: "fake",
    41  			Help: "fake",
    42  		},
    43  		[]string{"endpoint", "method", "code"})
    44  
    45  	inFlightRequestsGauge := prometheus.NewGaugeVec(
    46  		prometheus.GaugeOpts{
    47  			Name: "in_flight_requests",
    48  			Help: "Tracks the number of WFE requests currently in flight, labeled by endpoint.",
    49  		},
    50  		[]string{"endpoint"},
    51  	)
    52  
    53  	mux := http.NewServeMux()
    54  	mux.Handle("/foo", sleepyHandler{clk})
    55  	mh := MeasuredHandler{
    56  		serveMux:              mux,
    57  		clk:                   clk,
    58  		stat:                  stat,
    59  		inFlightRequestsGauge: inFlightRequestsGauge,
    60  	}
    61  	mh.ServeHTTP(httptest.NewRecorder(), &http.Request{
    62  		URL:    &url.URL{Path: "/foo"},
    63  		Method: "GET",
    64  	})
    65  	iom := collect(stat)
    66  
    67  	hist := iom.Histogram
    68  	if *hist.SampleCount != 1 {
    69  		t.Errorf("SampleCount = %d (expected 1)", *hist.SampleCount)
    70  	}
    71  	if *hist.SampleSum != 999 {
    72  		t.Errorf("SampleSum = %g (expected 999)", *hist.SampleSum)
    73  	}
    74  
    75  	expectedLabels := map[string]string{
    76  		"endpoint": "/foo",
    77  		"method":   "GET",
    78  		"code":     "302",
    79  	}
    80  	for _, labelPair := range iom.Label {
    81  		if expectedLabels[*labelPair.Name] == "" {
    82  			t.Errorf("Unexpected label %s", *labelPair.Name)
    83  		} else if expectedLabels[*labelPair.Name] != *labelPair.Value {
    84  			t.Errorf("labels[%q] = %q (expected %q)", *labelPair.Name, *labelPair.Value,
    85  				expectedLabels[*labelPair.Name])
    86  		}
    87  		delete(expectedLabels, *labelPair.Name)
    88  	}
    89  	if len(expectedLabels) != 0 {
    90  		t.Errorf("Some labels were expected, but not observed: %v", expectedLabels)
    91  	}
    92  }
    93  
    94  // Make an HTTP request with an unknown method and ensure we use the appropriate
    95  // label value.
    96  func TestUnknownMethod(t *testing.T) {
    97  	clk := clock.NewFake()
    98  
    99  	// Create a local histogram stat with the same labels as the real one, but
   100  	// don't register it; we will collect its data here in the test to verify it.
   101  	stat := prometheus.NewHistogramVec(
   102  		prometheus.HistogramOpts{
   103  			Name: "fake",
   104  			Help: "fake",
   105  		},
   106  		[]string{"endpoint", "method", "code"})
   107  	inFlightRequestsGauge := prometheus.NewGaugeVec(
   108  		prometheus.GaugeOpts{
   109  			Name: "in_flight_requests",
   110  			Help: "Tracks the number of WFE requests currently in flight, labeled by endpoint.",
   111  		},
   112  		[]string{"endpoint"},
   113  	)
   114  
   115  	mux := http.NewServeMux()
   116  	mux.Handle("/foo", sleepyHandler{clk})
   117  	mh := MeasuredHandler{
   118  		serveMux:              mux,
   119  		clk:                   clk,
   120  		stat:                  stat,
   121  		inFlightRequestsGauge: inFlightRequestsGauge,
   122  	}
   123  	mh.ServeHTTP(httptest.NewRecorder(), &http.Request{
   124  		URL:    &url.URL{Path: "/foo"},
   125  		Method: "POKE",
   126  	})
   127  	iom := collect(stat)
   128  
   129  	expectedLabels := map[string]string{
   130  		"endpoint": "/foo",
   131  		"method":   "unknown",
   132  		"code":     "302",
   133  	}
   134  	for _, labelPair := range iom.Label {
   135  		if expectedLabels[*labelPair.Name] == "" {
   136  			t.Errorf("Unexpected label %s", *labelPair.Name)
   137  		} else if expectedLabels[*labelPair.Name] != *labelPair.Value {
   138  			t.Errorf("labels[%q] = %q (expected %q)", *labelPair.Name, *labelPair.Value,
   139  				expectedLabels[*labelPair.Name])
   140  		}
   141  		delete(expectedLabels, *labelPair.Name)
   142  	}
   143  	if len(expectedLabels) != 0 {
   144  		t.Errorf("Some labels were expected, but not observed: %v", expectedLabels)
   145  	}
   146  }
   147  
   148  func TestWrite(t *testing.T) {
   149  	clk := clock.NewFake()
   150  
   151  	// Create a local histogram stat with the same labels as the real one, but
   152  	// don't register it; we will collect its data here in the test to verify it.
   153  	stat := prometheus.NewHistogramVec(
   154  		prometheus.HistogramOpts{
   155  			Name: "fake",
   156  			Help: "fake",
   157  		},
   158  		[]string{"endpoint", "method", "code"})
   159  
   160  	inFlightRequestsGauge := prometheus.NewGaugeVec(
   161  		prometheus.GaugeOpts{
   162  			Name: "in_flight_requests",
   163  			Help: "Tracks the number of WFE requests currently in flight, labeled by endpoint.",
   164  		},
   165  		[]string{"endpoint"})
   166  
   167  	mux := http.NewServeMux()
   168  	mux.HandleFunc("/foo", func(w http.ResponseWriter, r *http.Request) {
   169  		w.Write([]byte{})
   170  	})
   171  	mh := MeasuredHandler{
   172  		serveMux:              mux,
   173  		clk:                   clk,
   174  		stat:                  stat,
   175  		inFlightRequestsGauge: inFlightRequestsGauge,
   176  	}
   177  	mh.ServeHTTP(httptest.NewRecorder(), &http.Request{
   178  		URL:    &url.URL{Path: "/foo"},
   179  		Method: "GET",
   180  	})
   181  	iom := collect(stat)
   182  
   183  	stat = prometheus.NewHistogramVec(
   184  		prometheus.HistogramOpts{
   185  			Name: "fake",
   186  			Help: "fake",
   187  		},
   188  		[]string{"endpoint", "method", "code"})
   189  	mh.stat = stat
   190  	mh.inFlightRequestsGauge = inFlightRequestsGauge
   191  	expectedLabels := map[string]string{
   192  		"endpoint": "/foo",
   193  		"method":   "GET",
   194  		"code":     "200",
   195  	}
   196  	for _, labelPair := range iom.Label {
   197  		if expectedLabels[*labelPair.Name] == "" {
   198  			t.Errorf("Unexpected label %s", *labelPair.Name)
   199  		} else if expectedLabels[*labelPair.Name] != *labelPair.Value {
   200  			t.Errorf("labels[%q] = %q (expected %q)", *labelPair.Name, *labelPair.Value,
   201  				expectedLabels[*labelPair.Name])
   202  		}
   203  		delete(expectedLabels, *labelPair.Name)
   204  	}
   205  	if len(expectedLabels) != 0 {
   206  		t.Errorf("Some labels were expected, but not observed: %v", expectedLabels)
   207  	}
   208  
   209  	mux.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
   210  		w.WriteHeader(202)
   211  		w.Write([]byte{})
   212  	})
   213  	mh.ServeHTTP(httptest.NewRecorder(), &http.Request{
   214  		URL:    &url.URL{Path: "/bar"},
   215  		Method: "GET",
   216  	})
   217  	iom = collect(stat)
   218  
   219  	expectedLabels = map[string]string{
   220  		"endpoint": "/bar",
   221  		"method":   "GET",
   222  		"code":     "202",
   223  	}
   224  	for _, labelPair := range iom.Label {
   225  		if expectedLabels[*labelPair.Name] == "" {
   226  			t.Errorf("Unexpected label %s", *labelPair.Name)
   227  		} else if expectedLabels[*labelPair.Name] != *labelPair.Value {
   228  			t.Errorf("labels[%q] = %q (expected %q)", *labelPair.Name, *labelPair.Value,
   229  				expectedLabels[*labelPair.Name])
   230  		}
   231  		delete(expectedLabels, *labelPair.Name)
   232  	}
   233  	if len(expectedLabels) != 0 {
   234  		t.Errorf("Some labels were expected, but not observed: %v", expectedLabels)
   235  	}
   236  }