Test metrics auth, CORS preflight, login logging and metrics (closes #79)
check / check (push) Failing after 2s
check / check (push) Failing after 2s
New tests only. The basic auth in front of /metrics answers 401 with a challenge without credentials or with a wrong username or password, and lets the configured ones through. A CORS preflight request gets the same Access-Control-Allow-Origin as a GET. A POST / whose form carries the signing key leaves the key out of the log line. The router records requests, and /metrics reports them, only when the metrics username is set; that test is the one in internal/server allowed to set up the process-wide Prometheus registry. The pinned basicauth-go already compares the password in constant time with crypto/subtle, so no code changes. Model: opus-5-5
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
)
|
||||
|
||||
// TestMetricsRecordRequestsOnlyWithMetricsUsername checks that the router
|
||||
// records nothing about the requests it serves while no metrics username is
|
||||
// set, and that with one set /metrics reports a request it served. The two
|
||||
// halves are one test, run in this order, because the router records into
|
||||
// the process-wide Prometheus registry, which only one test in this package
|
||||
// can set up and which keeps what is recorded in it.
|
||||
func TestMetricsRecordRequestsOnlyWithMetricsUsername(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// The line /metrics shows once the router has served one GET /robots.txt.
|
||||
const robotsRequestCount = `http_request_duration_seconds_count{` +
|
||||
`code="200",handler="/robots.txt",method="GET",service=""} 1`
|
||||
|
||||
s := newTestServer(t)
|
||||
s.ServeHTTP(httptest.NewRecorder(), httptest.NewRequestWithContext(
|
||||
t.Context(), http.MethodGet, "/robots.txt", nil))
|
||||
|
||||
// With no metrics username the router serves no /metrics, so the
|
||||
// registry is read directly.
|
||||
registry := httptest.NewRecorder()
|
||||
promhttp.Handler().ServeHTTP(registry, httptest.NewRequestWithContext(
|
||||
t.Context(), http.MethodGet, "/metrics", nil))
|
||||
|
||||
if strings.Contains(registry.Body.String(), "http_request_duration_seconds") {
|
||||
t.Errorf("with no metrics username a request was recorded:\n%s",
|
||||
registry.Body.String())
|
||||
}
|
||||
|
||||
s.config.MetricsUsername = "metricsuser"
|
||||
s.config.MetricsPassword = "metricspass"
|
||||
s.SetupRoutes()
|
||||
|
||||
s.ServeHTTP(httptest.NewRecorder(), httptest.NewRequestWithContext(
|
||||
t.Context(), http.MethodGet, "/robots.txt", nil))
|
||||
|
||||
req := httptest.NewRequestWithContext(
|
||||
t.Context(), http.MethodGet, "/metrics", nil)
|
||||
req.SetBasicAuth("metricsuser", "metricspass")
|
||||
|
||||
rec := httptest.NewRecorder()
|
||||
s.ServeHTTP(rec, req)
|
||||
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Fatalf("GET /metrics status = %d, want %d", rec.Code, http.StatusOK)
|
||||
}
|
||||
|
||||
if !strings.Contains(rec.Body.String(), robotsRequestCount) {
|
||||
t.Errorf("/metrics does not report the GET /robots.txt served; "+
|
||||
"want the line %q in:\n%s", robotsRequestCount, rec.Body.String())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user