package metricsquality import ( "net/http" "net/http/httptest" "strings" "testing" "time" "scm.bstein.dev/bstein/ananke/internal/metrics" ) // TestExporterEmitsCoreMetrics runs one orchestration or CLI step. // Signature: TestExporterEmitsCoreMetrics(t *testing.T). // Why: keeps exporter metric output stable after moving checks into the // top-level testing module. func TestExporterEmitsCoreMetrics(t *testing.T) { exporter := metrics.New() exporter.UpdateBudget(321) exporter.UpdateSample(metrics.Sample{ Name: "Pyrphoros", Target: "pyrphoros@localhost", OnBattery: true, LowBattery: false, RuntimeSecond: 412, BatteryCharge: 81.5, LoadPercent: 27.4, PowerNominalW: 510, ThresholdSec: 354, Trigger: true, BreachCount: 2, Status: "OB", UpdatedAt: time.Unix(1710000000, 0).UTC(), }) exporter.MarkShutdown("ups-threshold") req := httptest.NewRequest(http.MethodGet, "/metrics", nil) rr := httptest.NewRecorder() exporter.Handler("/metrics").ServeHTTP(rr, req) body := rr.Body.String() mustContain := []string{ "ananke_shutdown_budget_seconds 321", "ananke_shutdown_triggers_total 1", "ananke_ups_on_battery{source=\"Pyrphoros\",target=\"pyrphoros@localhost\"", "ananke_ups_runtime_seconds{source=\"Pyrphoros\",target=\"pyrphoros@localhost\"", "ananke_ups_battery_charge_percent{source=\"Pyrphoros\",target=\"pyrphoros@localhost\"", "ananke_ups_load_percent{source=\"Pyrphoros\",target=\"pyrphoros@localhost\"", "ananke_ups_power_nominal_watts{source=\"Pyrphoros\",target=\"pyrphoros@localhost\"", "ananke_ups_threshold_seconds{source=\"Pyrphoros\",target=\"pyrphoros@localhost\"", } for _, fragment := range mustContain { if !strings.Contains(body, fragment) { t.Fatalf("missing metric fragment %q in output:\n%s", fragment, body) } } } // TestExporterHealthzAndEscaping runs one orchestration or CLI step. // Signature: TestExporterHealthzAndEscaping(t *testing.T). // Why: covers exporter health routing and label escaping from the top-level // testing module so HTTP behavior stays explicit after migration. func TestExporterHealthzAndEscaping(t *testing.T) { exporter := metrics.New() exporter.UpdateSample(metrics.Sample{ Name: `Sta"tera`, Target: `statera\host`, Status: `O"B`, LastError: "x", }) handler := exporter.Handler("/custom") healthReq := httptest.NewRequest(http.MethodGet, "/healthz", nil) healthRR := httptest.NewRecorder() handler.ServeHTTP(healthRR, healthReq) if healthRR.Code != http.StatusOK || strings.TrimSpace(healthRR.Body.String()) != "ok" { t.Fatalf("unexpected health response: code=%d body=%q", healthRR.Code, healthRR.Body.String()) } metricsReq := httptest.NewRequest(http.MethodGet, "/custom", nil) metricsRR := httptest.NewRecorder() handler.ServeHTTP(metricsRR, metricsReq) body := metricsRR.Body.String() if !strings.Contains(body, `source="Sta\\\"tera"`) { t.Fatalf("expected escaped source label, got:\n%s", body) } if !strings.Contains(body, `target="statera\\\\host"`) { t.Fatalf("expected escaped target label, got:\n%s", body) } if !strings.Contains(body, "ananke_ups_error") { t.Fatalf("expected error metric line in output") } } // TestExporterHelperContracts runs one orchestration or CLI step. // Signature: TestExporterHelperContracts(t *testing.T). // Why: preserves direct helper coverage for metric formatting utilities after // removing same-package root tests. func TestExporterHelperContracts(t *testing.T) { if metrics.TestHookBoolNum(true) != 1 || metrics.TestHookBoolNum(false) != 0 { t.Fatalf("unexpected boolNum values") } if got := metrics.TestHookSafeLabelValue(`a"b\c`); got != `a\"b\\c` { t.Fatalf("unexpected escaped string: %q", got) } }