spegel/pkg/httpx/mux_test.go
Philip Laine fdf96eee4b
Rename package mux to httpx and refactor http helpers
Signed-off-by: Philip Laine <philip.laine@gmail.com>
2025-05-23 13:42:22 +02:00

161 lines
5.2 KiB
Go

package httpx
import (
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/go-logr/logr"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/testutil"
"github.com/stretchr/testify/require"
)
func TestServeMux(t *testing.T) {
t.Parallel()
registerer := prometheus.NewRegistry()
RegisterMetrics(registerer)
m := NewServeMux(logr.Discard())
handlersCalled := []string{}
m.Handle("/exact", func(rw ResponseWriter, req *http.Request) {
handlersCalled = append(handlersCalled, "exact")
})
m.Handle("/prefix/", func(rw ResponseWriter, req *http.Request) {
handlersCalled = append(handlersCalled, "prefix")
})
paths := []string{"/prefix/", "/exact", "/exact/foo", "/prefix/bar"}
for _, path := range paths {
rw := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, "http://localhost"+path, nil)
m.ServeHTTP(rw, req)
}
expectedHandlersCalled := []string{"prefix", "exact", "prefix"}
require.Equal(t, expectedHandlersCalled, handlersCalled)
expectedMetrics := `
# HELP http_requests_inflight The number of inflight requests being handled at the same time.
# TYPE http_requests_inflight gauge
http_requests_inflight{handler="/exact"} 0
http_requests_inflight{handler="/prefix/*"} 0
`
err := testutil.CollectAndCompare(HttpRequestsInflight, strings.NewReader(expectedMetrics))
require.NoError(t, err)
expectedMetrics = `
# HELP http_response_size_bytes The size of the HTTP responses.
# TYPE http_response_size_bytes histogram
http_response_size_bytes_bucket{code="200",handler="/exact",method="GET",le="1024"} 1
http_response_size_bytes_bucket{code="200",handler="/exact",method="GET",le="5120"} 1
http_response_size_bytes_bucket{code="200",handler="/exact",method="GET",le="25600"} 1
http_response_size_bytes_bucket{code="200",handler="/exact",method="GET",le="128000"} 1
http_response_size_bytes_bucket{code="200",handler="/exact",method="GET",le="640000"} 1
http_response_size_bytes_bucket{code="200",handler="/exact",method="GET",le="3.2e+06"} 1
http_response_size_bytes_bucket{code="200",handler="/exact",method="GET",le="1.6e+07"} 1
http_response_size_bytes_bucket{code="200",handler="/exact",method="GET",le="8e+07"} 1
http_response_size_bytes_bucket{code="200",handler="/exact",method="GET",le="4e+08"} 1
http_response_size_bytes_bucket{code="200",handler="/exact",method="GET",le="2e+09"} 1
http_response_size_bytes_bucket{code="200",handler="/exact",method="GET",le="+Inf"} 1
http_response_size_bytes_sum{code="200",handler="/exact",method="GET"} 0
http_response_size_bytes_count{code="200",handler="/exact",method="GET"} 1
http_response_size_bytes_bucket{code="200",handler="/prefix/*",method="GET",le="1024"} 2
http_response_size_bytes_bucket{code="200",handler="/prefix/*",method="GET",le="5120"} 2
http_response_size_bytes_bucket{code="200",handler="/prefix/*",method="GET",le="25600"} 2
http_response_size_bytes_bucket{code="200",handler="/prefix/*",method="GET",le="128000"} 2
http_response_size_bytes_bucket{code="200",handler="/prefix/*",method="GET",le="640000"} 2
http_response_size_bytes_bucket{code="200",handler="/prefix/*",method="GET",le="3.2e+06"} 2
http_response_size_bytes_bucket{code="200",handler="/prefix/*",method="GET",le="1.6e+07"} 2
http_response_size_bytes_bucket{code="200",handler="/prefix/*",method="GET",le="8e+07"} 2
http_response_size_bytes_bucket{code="200",handler="/prefix/*",method="GET",le="4e+08"} 2
http_response_size_bytes_bucket{code="200",handler="/prefix/*",method="GET",le="2e+09"} 2
http_response_size_bytes_bucket{code="200",handler="/prefix/*",method="GET",le="+Inf"} 2
http_response_size_bytes_sum{code="200",handler="/prefix/*",method="GET"} 0
http_response_size_bytes_count{code="200",handler="/prefix/*",method="GET"} 2
`
err = testutil.CollectAndCompare(HttpResponseSizeHistogram, strings.NewReader(expectedMetrics))
require.NoError(t, err)
}
func TestGetClientIP(t *testing.T) {
t.Parallel()
tests := []struct {
name string
request *http.Request
expected string
}{
{
name: "x forwarded for single",
request: &http.Request{
Header: http.Header{
"X-Forwarded-For": []string{"localhost"},
},
},
expected: "localhost",
},
{
name: "x forwarded for multiple",
request: &http.Request{
Header: http.Header{
"X-Forwarded-For": []string{"localhost,127.0.0.1"},
},
},
expected: "localhost",
},
{
name: "remote address",
request: &http.Request{
RemoteAddr: "127.0.0.1:9090",
},
expected: "127.0.0.1",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
ip := GetClientIP(tt.request)
require.Equal(t, tt.expected, ip)
})
}
}
func TestMetricsFriendlyPath(t *testing.T) {
t.Parallel()
tests := []struct {
pattern string
expected string
}{
{
pattern: "/",
expected: "/*",
},
{
pattern: "/exact",
expected: "/exact",
},
{
pattern: "/prefix/",
expected: "/prefix/*",
},
{
pattern: "/chats/{id}/message/{index}",
expected: "/chats/{id}/message/{index}",
},
}
for _, method := range []string{"", "GET ", "HEAD "} {
for _, tt := range tests {
t.Run(tt.pattern, func(t *testing.T) {
t.Parallel()
metricsPath := metricsFriendlyPath(method + tt.pattern)
require.Equal(t, tt.expected, metricsPath)
})
}
}
}