package service import ( "errors" "fmt" "strings" "testing" "time" "github.com/prometheus/client_golang/prometheus" "go.uber.org/zap" "go.uber.org/zap/zaptest/observer" ) func TestSingleRequestMetrics(t *testing.T) { if first, second := defaultSingleRequestCollectorSet(), defaultSingleRequestCollectorSet(); first != second { t.Fatal("default collector set was registered more than once") } registry := prometheus.NewRegistry() core, logs := observer.New(zap.InfoLevel) collector := newSingleRequestObservability(registry, zap.New(core)) clock := newSingleRequestManualClock(time.Unix(0, 0)) accumulator := newSingleRequestTimingAccumulator(clock, collector) accumulator.onRequest() accumulator.onStageEnter(singleRequestStagePlan) clock.Advance(2 * time.Second) accumulator.onStageExit(singleRequestStagePlan, singleRequestOutcomeSuccess, "") accumulator.onTerminal(singleRequestOutcomeSuccess, "", true) accumulator.onTerminal(singleRequestOutcomeError, singleRequestErrorClassProvider, false) terminalLabels := map[string]string{ "event_class": "terminal", "stage": "none", "operation": "terminal", "outcome": "success", "error_class": "none", } if got := metricValue(t, registry, singleRequestLifecycleMetric, terminalLabels); got != 1 { t.Fatalf("terminal metric = %v, want 1", got) } stageLabels := map[string]string{ "event_class": "stage", "stage": "plan", "operation": "plan", "outcome": "success", "error_class": "none", } if got := metricValue(t, registry, singleRequestLifecycleMetric, stageLabels); got != 1 { t.Fatalf("stage metric = %v, want 1", got) } families, err := registry.Gather() if err != nil { t.Fatalf("gather metrics: %v", err) } for _, family := range families { for _, metric := range family.Metric { for _, label := range metric.Label { if label.GetName() == "correlation" || strings.Contains(label.GetValue(), "sr-") { t.Fatalf("unbounded correlation label: %s=%q", label.GetName(), label.GetValue()) } } } } allowed := map[string]bool{ "correlation": true, "event_class": true, "stage": true, "operation": true, "outcome": true, "error_class": true, "duration_ms": true, "tool_count": true, "has_result": true, } entries := logs.All() if len(entries) != 3 { t.Fatalf("observation logs = %d, want 3", len(entries)) } for _, entry := range entries { if entry.Message != singleRequestObservationLogKey { t.Fatalf("log message = %q", entry.Message) } if len(entry.Context) != len(allowed) { t.Fatalf("log field count = %d, want %d", len(entry.Context), len(allowed)) } for _, field := range entry.Context { if !allowed[field.Key] { t.Fatalf("unexpected log key %q", field.Key) } } } } func TestSingleRequestMetricsRejectSecretSentinelAndIsolatesObserverFailures(t *testing.T) { registry := prometheus.NewRegistry() core, logs := observer.New(zap.InfoLevel) collector := newSingleRequestObservability(registry, zap.New(core)) if err := collector.Emit(singleRequestDTO{ EventClass: singleRequestEventClassTerminal, Operation: singleRequestOperationTerminal, Outcome: singleRequestOutcomeError, ErrorClass: singleRequestErrorClassProvider, Correlation: "SECRET_PATH_COMMAND_BEARER", DurationMS: 1, }); err != nil { t.Fatalf("Emit: %v", err) } for _, entry := range logs.All() { if strings.Contains(strings.ToLower(fmt.Sprint(entry.ContextMap()["correlation"])), "secret") { t.Fatalf("secret sentinel leaked: %+v", entry) } } failing := singleRequestObserverFunc(func(singleRequestDTO) error { return errors.New("observer failure") }) accumulator := newSingleRequestTimingAccumulator(nil, failing) accumulator.onTerminal(singleRequestOutcomeSuccess, "", true) if got := accumulator.observer.failureCount(); got != 1 { t.Fatalf("isolated failures = %d, want 1", got) } } type singleRequestObserverFunc func(singleRequestDTO) error func (fn singleRequestObserverFunc) Emit(dto singleRequestDTO) error { return fn(dto) }