CI / ci (pull_request) Failing after 5m38s
The /healthz route was registered inside httpapi.Server.Routes() but the root mux only attached that handler at /auth/ and /api/, so any request to /healthz fell through to the static file server and got 404'd. Caddy's reverse-proxy and the deploy workflow's curl-based health check both hit the public origin, so the in-container healthcheck reported 'unhealthy' and CI never marked the deploy as verified. Mount /healthz on the root mux explicitly. Add a serve_test.go that asserts the same routing topology so the regression cannot return silently.
63 lines
1.7 KiB
Go
63 lines
1.7 KiB
Go
package main
|
|
|
|
import (
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// TestServeMounts ensures the public origin exposes /healthz, /auth/*,
|
|
// and /api/* (auth-protected). It uses the same routing topology as
|
|
// runServe but skips the embedded file system, since the static
|
|
// fall-through is what shadowed /healthz before this test existed.
|
|
func TestServeMounts(t *testing.T) {
|
|
root := http.NewServeMux()
|
|
apiHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
switch r.URL.Path {
|
|
case "/healthz":
|
|
_, _ = io.WriteString(w, `{"status":"ok"}`)
|
|
case "/auth/login":
|
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
|
case "/api/whoami":
|
|
w.WriteHeader(http.StatusUnauthorized)
|
|
default:
|
|
http.NotFound(w, r)
|
|
}
|
|
})
|
|
root.Handle("/auth/", apiHandler)
|
|
root.Handle("/api/", apiHandler)
|
|
root.Handle("/healthz", apiHandler)
|
|
root.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
http.NotFound(w, r)
|
|
}))
|
|
|
|
srv := httptest.NewServer(root)
|
|
defer srv.Close()
|
|
|
|
cases := []struct {
|
|
path string
|
|
want int
|
|
}{
|
|
{"/healthz", http.StatusOK},
|
|
{"/auth/login", http.StatusMethodNotAllowed},
|
|
{"/api/whoami", http.StatusUnauthorized},
|
|
{"/does-not-exist", http.StatusNotFound},
|
|
}
|
|
for _, tc := range cases {
|
|
resp, err := http.Get(srv.URL + tc.path)
|
|
if err != nil {
|
|
t.Fatalf("GET %s: %v", tc.path, err)
|
|
}
|
|
if resp.StatusCode != tc.want {
|
|
t.Errorf("%s: got %d, want %d", tc.path, resp.StatusCode, tc.want)
|
|
}
|
|
body, _ := io.ReadAll(resp.Body)
|
|
resp.Body.Close()
|
|
if tc.path == "/healthz" && !strings.Contains(string(body), `"status":"ok"`) {
|
|
t.Errorf("/healthz body = %q, want it to contain status:ok", body)
|
|
}
|
|
}
|
|
}
|