check / check (push) Successful in 2m18s
backend/script/test runs go test -timeout 30s -race -cover and, if that fails, runs it again with -v and fails. The root script/test drops its one 30-second timeout around both halves: from a cold Go build cache, compiling the tests with -race used it all up on four cores. Each half keeps its own limit. The builder stage of the Dockerfile gains gcc and musl-dev for the race detector; make build still sets CGO_ENABLED=0. New tests: the health check's answer, a valid report's answer, a report file's exact lines, and the flush at the 10 MiB threshold. The handlers TestImport stub is gone. Model: opus-5-5
121 lines
3.2 KiB
Go
121 lines
3.2 KiB
Go
package handlers_test
|
|
|
|
import (
|
|
"encoding/json"
|
|
"maps"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"slices"
|
|
"testing"
|
|
"time"
|
|
|
|
"sneak.berlin/go/netwatch/internal/globals"
|
|
"sneak.berlin/go/netwatch/internal/handlers"
|
|
"sneak.berlin/go/netwatch/internal/healthcheck"
|
|
"sneak.berlin/go/netwatch/internal/logger"
|
|
|
|
"go.uber.org/fx/fxtest"
|
|
)
|
|
|
|
// newStartedHandlers builds Handlers with a real health check for the
|
|
// server named in g, and starts them, which records the time the
|
|
// uptime counts from.
|
|
func newStartedHandlers(t *testing.T, g *globals.Globals) *handlers.Handlers {
|
|
t.Helper()
|
|
|
|
lc := fxtest.NewLifecycle(t)
|
|
|
|
log, err := logger.New(lc, logger.Params{Globals: g})
|
|
if err != nil {
|
|
t.Fatalf("logger: %v", err)
|
|
}
|
|
|
|
hc, err := healthcheck.New(lc,
|
|
healthcheck.Params{Globals: g, Logger: log})
|
|
if err != nil {
|
|
t.Fatalf("health check: %v", err)
|
|
}
|
|
|
|
h, err := handlers.New(lc,
|
|
handlers.Params{Globals: g, Healthcheck: hc, Logger: log})
|
|
if err != nil {
|
|
t.Fatalf("handlers: %v", err)
|
|
}
|
|
|
|
lc.RequireStart()
|
|
t.Cleanup(lc.RequireStop)
|
|
|
|
return h
|
|
}
|
|
|
|
// TestHandleHealthCheck checks the health check's answer: 200, a JSON
|
|
// content type, and a JSON object with exactly the fields of
|
|
// healthcheck.Response, carrying this server's name and version and
|
|
// an uptime counted from its start.
|
|
func TestHandleHealthCheck(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
g := &globals.Globals{Appname: "netwatch-server", Version: "v1.2.3"}
|
|
h := newStartedHandlers(t, g)
|
|
|
|
rec := httptest.NewRecorder()
|
|
req := httptest.NewRequestWithContext(t.Context(),
|
|
http.MethodGet, "/.well-known/healthcheck", http.NoBody)
|
|
|
|
h.HandleHealthCheck().ServeHTTP(rec, req)
|
|
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("status = %d, want %d", rec.Code, http.StatusOK)
|
|
}
|
|
|
|
contentType := rec.Header().Get("Content-Type")
|
|
if contentType != "application/json; charset=utf-8" {
|
|
t.Errorf("Content-Type = %q, want %q",
|
|
contentType, "application/json; charset=utf-8")
|
|
}
|
|
|
|
var body map[string]any
|
|
|
|
err := json.Unmarshal(rec.Body.Bytes(), &body)
|
|
if err != nil {
|
|
t.Fatalf("body not a JSON object: %v (%q)", err, rec.Body.String())
|
|
}
|
|
|
|
fields := []string{
|
|
"appname", "now", "status", "uptimeHuman", "uptimeSeconds", "version",
|
|
}
|
|
if got := slices.Sorted(maps.Keys(body)); !slices.Equal(got, fields) {
|
|
t.Fatalf("fields = %v, want %v", got, fields)
|
|
}
|
|
|
|
for field, want := range map[string]string{
|
|
"appname": g.Appname, "status": "ok", "version": g.Version,
|
|
} {
|
|
if body[field] != want {
|
|
t.Errorf("%s = %v, want %q", field, body[field], want)
|
|
}
|
|
}
|
|
|
|
now, _ := body["now"].(string)
|
|
|
|
at, err := time.Parse(time.RFC3339Nano, now)
|
|
if err != nil || time.Since(at).Abs() > time.Minute {
|
|
t.Errorf("now = %q, want the current time in RFC 3339 (%v)", now, err)
|
|
}
|
|
|
|
// Started just now, so the uptime is well under a minute.
|
|
human, _ := body["uptimeHuman"].(string)
|
|
|
|
uptime, err := time.ParseDuration(human)
|
|
if err != nil || uptime > time.Minute {
|
|
t.Errorf("uptimeHuman = %q, want a duration under a minute (%v)",
|
|
human, err)
|
|
}
|
|
|
|
seconds, ok := body["uptimeSeconds"].(float64)
|
|
if !ok || seconds < 0 || seconds > time.Minute.Seconds() {
|
|
t.Errorf("uptimeSeconds = %v, want a number of seconds under a minute",
|
|
body["uptimeSeconds"])
|
|
}
|
|
}
|