107 lines
3.7 KiB
Go
107 lines
3.7 KiB
Go
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)
|
|
}
|
|
}
|