mirror of
https://github.com/PerpetualSoftware/pad.git
synced 2026-09-11 21:39:01 +00:00
0a1dbc8c73
newPadServer (internal/mcp), testStoreOAuth (internal/oauth), and newMetricsTestServer (internal/server) were left on the slow per-call migration path after IDEA-1914/#788 wired testServer and store's white-box testStore onto storetest.NewSQLite. Switch all three, and add minimal TestMains to internal/mcp and internal/oauth to release storetest's process-wide template DB, matching internal/server's existing TestMain.
104 lines
3.5 KiB
Go
104 lines
3.5 KiB
Go
package server
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/PerpetualSoftware/pad/internal/metrics"
|
|
"github.com/PerpetualSoftware/pad/internal/store/storetest"
|
|
)
|
|
|
|
// newMetricsTestServer builds a Server with metrics enabled and the given
|
|
// static token, via storetest.NewSQLite (IDEA-1914) — see testServer in
|
|
// server_test.go for why this is fast (once-per-binary migration chain).
|
|
func newMetricsTestServer(t *testing.T, token string) *Server {
|
|
t.Helper()
|
|
s := storetest.NewSQLite(t)
|
|
srv := New(s)
|
|
srv.SetMetrics(metrics.New())
|
|
srv.SetMetricsToken(token)
|
|
// storetest.NewSQLite already registers t.Cleanup(store.Close); since
|
|
// t.Cleanup runs LIFO, registering Stop() here afterward still drains
|
|
// background goroutines BEFORE the store closes — see testServer in
|
|
// server_test.go for the BUG-842 race details.
|
|
t.Cleanup(func() {
|
|
srv.Stop()
|
|
})
|
|
return srv
|
|
}
|
|
|
|
func doMetricsRequest(srv *Server, remoteAddr, authHeader string) *httptest.ResponseRecorder {
|
|
req := httptest.NewRequest("GET", "/metrics", nil)
|
|
req.RemoteAddr = remoteAddr
|
|
if authHeader != "" {
|
|
req.Header.Set("Authorization", authHeader)
|
|
}
|
|
rr := httptest.NewRecorder()
|
|
srv.ServeHTTP(rr, req)
|
|
return rr
|
|
}
|
|
|
|
// TestMetricsAuth_TokenUnset_LoopbackOnly: no PAD_METRICS_TOKEN → loopback
|
|
// callers succeed, everyone else gets 403. Safe default for self-hosters
|
|
// running Prometheus on the same box.
|
|
func TestMetricsAuth_TokenUnset_LoopbackOnly(t *testing.T) {
|
|
srv := newMetricsTestServer(t, "")
|
|
|
|
rr := doMetricsRequest(srv, "127.0.0.1:12345", "")
|
|
if rr.Code != http.StatusOK {
|
|
t.Fatalf("loopback /metrics: expected 200, got %d: %s", rr.Code, rr.Body.String())
|
|
}
|
|
|
|
rr = doMetricsRequest(srv, "203.0.113.5:12345", "")
|
|
if rr.Code != http.StatusForbidden {
|
|
t.Fatalf("non-loopback /metrics: expected 403 (no token configured), got %d: %s", rr.Code, rr.Body.String())
|
|
}
|
|
}
|
|
|
|
// TestMetricsAuth_TokenSet_RequiresBearer: with PAD_METRICS_TOKEN set,
|
|
// every scrape — even loopback — must present the correct Bearer token.
|
|
func TestMetricsAuth_TokenSet_RequiresBearer(t *testing.T) {
|
|
srv := newMetricsTestServer(t, "super-secret-token")
|
|
|
|
// Missing header → 401
|
|
rr := doMetricsRequest(srv, "127.0.0.1:1", "")
|
|
if rr.Code != http.StatusUnauthorized {
|
|
t.Fatalf("no auth header: expected 401, got %d", rr.Code)
|
|
}
|
|
|
|
// Wrong token → 401
|
|
rr = doMetricsRequest(srv, "127.0.0.1:1", "Bearer wrong-token")
|
|
if rr.Code != http.StatusUnauthorized {
|
|
t.Fatalf("wrong token: expected 401, got %d", rr.Code)
|
|
}
|
|
|
|
// Non-Bearer scheme → 401
|
|
rr = doMetricsRequest(srv, "127.0.0.1:1", "Basic dXNlcjpwYXNz")
|
|
if rr.Code != http.StatusUnauthorized {
|
|
t.Fatalf("basic auth: expected 401, got %d", rr.Code)
|
|
}
|
|
|
|
// Correct token — from anywhere — succeeds.
|
|
rr = doMetricsRequest(srv, "203.0.113.5:1", "Bearer super-secret-token")
|
|
if rr.Code != http.StatusOK {
|
|
t.Fatalf("valid token: expected 200, got %d: %s", rr.Code, rr.Body.String())
|
|
}
|
|
|
|
// WWW-Authenticate header is set for failed attempts.
|
|
rr = doMetricsRequest(srv, "127.0.0.1:1", "Bearer wrong")
|
|
if got := rr.Header().Get("WWW-Authenticate"); got == "" {
|
|
t.Error("expected WWW-Authenticate header on 401")
|
|
}
|
|
}
|
|
|
|
// TestMetricsAuth_NoMetricsMeans404 sanity-checks that /metrics is
|
|
// simply not routed when metrics are disabled (SetMetrics never called).
|
|
func TestMetricsAuth_NoMetricsMeans404(t *testing.T) {
|
|
srv := testServer(t) // no SetMetrics
|
|
rr := doMetricsRequest(srv, "127.0.0.1:1", "")
|
|
if rr.Code != http.StatusNotFound {
|
|
t.Fatalf("metrics disabled: expected 404, got %d", rr.Code)
|
|
}
|
|
}
|