승인된 execution preset을 Edge 조정 경계와 Node workspace/tool 실행 경계로 연결해 단일 요청 수명주기와 관측 계약을 일관되게 처리한다.
109 lines
3.9 KiB
Go
109 lines
3.9 KiB
Go
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) }
|