package metrics import ( "io" "net/http/httptest" "strings" "sync" "testing" ) // 回归:Register 必须幂等。 // 线上事故:client_golang 在 init() 里已把 GoCollector / ProcessCollector // 注册进默认 registry,我们又 MustRegister 到默认 registry → 进程启动即 // panic: duplicate metrics collector registration attempted。 // 这个 bug 编译期和普通单测都发现不了(只有真的调用 Register 才炸), // 所以这里必须实际调用两次。 func TestRegisterIsIdempotent(t *testing.T) { defer func() { if r := recover(); r != nil { t.Fatalf("Register 触发 panic(重复注册):%v", r) } }() Register("test") Register("test") // 第二次必须安全返回 } // 抓取端点必须能输出我们自己定义的指标 func TestHandlerExposesCustomMetrics(t *testing.T) { Register("test") ObserveHTTP("GET", "/api/v1/deployments", 200, 0.01) ObserveDBSave(0.002, true) ObserveAlertEvaluation(0.3, true) ObserveLokiQuery(true) DBRecordsTotal.WithLabelValues("deployments").Set(3) rec := httptest.NewRecorder() req := httptest.NewRequest("GET", "/metrics", nil) Handler().ServeHTTP(rec, req) body, _ := io.ReadAll(rec.Body) out := string(body) for _, want := range []string{ "lokistack_http_requests_total", "lokistack_db_save_duration_seconds", "lokistack_alert_evaluations_total", "lokistack_loki_queries_total", "lokistack_db_records", "go_goroutines", // GoCollector 生效 } { if !strings.Contains(out, want) { t.Fatalf("抓取结果缺少指标 %q", want) } } } // 并发调用不能 panic(sync.Once 保护) func TestRegisterConcurrent(t *testing.T) { var wg sync.WaitGroup for i := 0; i < 20; i++ { wg.Add(1) go func() { defer wg.Done() Register("test") }() } wg.Wait() }