main's tree, with next merged on 26 October, as one commit onto prod, on sneak's order. Model: opus-5-5 Co-authored-by: clawbot <clawbot@noreply.example.org>
This commit was merged in pull request #198.
This commit is contained in:
1 parent
0c36d79131
commit
863a1d549d
104 files changed
+9647
-2949
No files matched your search
@@ -0,0 +1,133 @@
|
||||
package handlers_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"maps"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"slices"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"sneak.berlin/go/netwatch/internal/config"
|
||||
"sneak.berlin/go/netwatch/internal/globals"
|
||||
"sneak.berlin/go/netwatch/internal/handlers"
|
||||
"sneak.berlin/go/netwatch/internal/healthcheck"
|
||||
"sneak.berlin/go/netwatch/internal/logger"
|
||||
"sneak.berlin/go/netwatch/internal/reportbuf"
|
||||
|
||||
"go.uber.org/fx/fxtest"
|
||||
)
|
||||
|
||||
// newStartedHandlers builds Handlers with a real health check for the
|
||||
// server named in g, and a report buffer in a temporary directory, 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)
|
||||
}
|
||||
|
||||
buf, err := reportbuf.New(lc, reportbuf.Params{
|
||||
Config: &config.Config{DataDir: t.TempDir(), DataDirMaxBytes: 1 << 20},
|
||||
Logger: log,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("report buffer: %v", err)
|
||||
}
|
||||
|
||||
hc, err := healthcheck.New(lc,
|
||||
healthcheck.Params{Buffer: buf, Globals: g, Logger: log})
|
||||
if err != nil {
|
||||
t.Fatalf("health check: %v", err)
|
||||
}
|
||||
|
||||
h, err := handlers.New(lc, handlers.Params{
|
||||
Buffer: buf, 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 at start, when
|
||||
// no write of a report file has failed: 200, a JSON content type, and a
|
||||
// JSON object with exactly the fields of healthcheck.HealthcheckResponse,
|
||||
// carrying "status":"ok", this server's name and version and an uptime
|
||||
// counted from its start. The answer while writes fail is checked in
|
||||
// internal/reportbuf, whose tests can make a write fail.
|
||||
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", "uptime_human", "uptime_seconds", "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["uptime_human"].(string)
|
||||
|
||||
uptime, err := time.ParseDuration(human)
|
||||
if err != nil || uptime > time.Minute {
|
||||
t.Errorf("uptime_human = %q, want a duration under a minute (%v)",
|
||||
human, err)
|
||||
}
|
||||
|
||||
seconds, ok := body["uptime_seconds"].(float64)
|
||||
if !ok || seconds < 0 || seconds > time.Minute.Seconds() {
|
||||
t.Errorf("uptime_seconds = %v, want a number of seconds under a minute",
|
||||
body["uptime_seconds"])
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user