package cli import ( "bytes" "context" "io" "net" "net/http" "strings" "testing" "time" ) func TestMetricsCmdRegistered(t *testing.T) { found := false for _, c := range rootCmd.Commands() { if c.Name() == "metrics" { found = true break } } if !found { t.Fatal("metricsCmd not registered on root") } } func TestMetricsAddrFlagDefault(t *testing.T) { f := metricsCmd.Flags().Lookup("addr") if f == nil { t.Fatal("--addr flag not registered on metricsCmd") } if f.DefValue != ":9100" { t.Errorf("--addr default = %q, want %q", f.DefValue, ":9100") } } func TestMetricsEndpoints(t *testing.T) { _, cleanup := initTestEnv(t) defer cleanup() resetRootFlags(t) // Pick a free port by briefly listening then closing. ln, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { t.Fatalf("listen probe: %v", err) } addr := ln.Addr().String() _ = ln.Close() metricsAddr = addr ctx, cancel := context.WithCancel(context.Background()) defer cancel() cmd := metricsCmd var out bytes.Buffer cmd.SetOut(&out) cmd.SetErr(&out) cmd.SetContext(ctx) errCh := make(chan error, 1) go func() { errCh <- cmd.RunE(cmd, nil) }() deadline := time.Now().Add(5 * time.Second) var resp *http.Response for time.Now().Before(deadline) { resp, err = http.Get("http://" + addr + "/healthz") if err == nil { break } time.Sleep(20 * time.Millisecond) } if err != nil { t.Fatalf("GET /healthz: %v", err) } if resp.StatusCode != http.StatusOK { t.Errorf("/healthz status = %d, want 200", resp.StatusCode) } body, _ := io.ReadAll(resp.Body) resp.Body.Close() if !strings.HasPrefix(string(body), "ok") { t.Errorf("/healthz body = %q, want \"ok\"", string(body)) } resp2, err := http.Get("http://" + addr + "/metrics") if err != nil { t.Fatalf("GET /metrics: %v", err) } defer resp2.Body.Close() if resp2.StatusCode != http.StatusOK { t.Errorf("/metrics status = %d, want 200", resp2.StatusCode) } mbody, _ := io.ReadAll(resp2.Body) ms := string(mbody) for _, name := range []string{ "txns_applied_total", "txns_drifted_total", "drifts_remediated_total", "peers_total", "nodes_total", "allocs_total", } { if !strings.Contains(ms, name) { t.Errorf("/metrics missing %q\n---\n%s", name, ms) } } cancel() select { case <-errCh: case <-time.After(3 * time.Second): t.Fatal("metrics command did not stop after cancel") } }