vibe-proxy/backend/internal/logging/cpa_trace_test.go
2026-08-24 00:10:41 +02:00

115 lines
3.3 KiB
Go

package logging
import (
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/gin-gonic/gin"
)
func TestFormatCPATraceID(t *testing.T) {
selectedAt := time.Date(2026, time.July, 17, 21, 58, 49, 0, time.UTC)
got := FormatCPATraceID(selectedAt, "auth-index", "request1")
if want := "20260717215849-auth-index-request1"; got != want {
t.Fatalf("FormatCPATraceID() = %q, want %q", got, want)
}
for _, test := range []struct {
name string
selectedAt time.Time
authIndex string
requestID string
}{
{name: "zero time", authIndex: "auth-index", requestID: "request1"},
{name: "empty auth index", selectedAt: selectedAt, requestID: "request1"},
{name: "empty request ID", selectedAt: selectedAt, authIndex: "auth-index"},
} {
t.Run(test.name, func(t *testing.T) {
if gotEmpty := FormatCPATraceID(test.selectedAt, test.authIndex, test.requestID); gotEmpty != "" {
t.Fatalf("FormatCPATraceID() = %q, want empty", gotEmpty)
}
})
}
}
func TestCPATraceIDMiddlewareRequiresAuthIndexBeforeResponseCommit(t *testing.T) {
gin.SetMode(gin.TestMode)
engine := gin.New()
engine.Use(CPATraceIDMiddleware())
engine.GET("/selected", func(c *gin.Context) {
SetGinRequestID(c, "1234abcd")
SetGinCPATraceID(c, "auth-index")
c.Status(http.StatusOK)
})
engine.GET("/unselected", func(c *gin.Context) {
SetGinRequestID(c, "1234abcd")
SetGinCPATraceID(c, "")
c.Status(http.StatusOK)
})
engine.GET("/committed", func(c *gin.Context) {
SetGinRequestID(c, "1234abcd")
c.Writer.WriteHeaderNow()
SetGinCPATraceID(c, "auth-index")
})
t.Run("writes selected auth trace", func(t *testing.T) {
recorder := httptest.NewRecorder()
engine.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/selected", nil))
traceID := recorder.Header().Get(CPATraceIDHeader)
if len(traceID) != len("20060102150405-auth-index-1234abcd") {
t.Fatalf("trace ID = %q, unexpected length", traceID)
}
if got := traceID[15:]; got != "auth-index-1234abcd" {
t.Fatalf("trace suffix = %q, want %q", got, "auth-index-1234abcd")
}
})
t.Run("skips empty auth index", func(t *testing.T) {
recorder := httptest.NewRecorder()
engine.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/unselected", nil))
if got := recorder.Header().Get(CPATraceIDHeader); got != "" {
t.Fatalf("trace ID = %q, want empty", got)
}
})
t.Run("skips committed response", func(t *testing.T) {
recorder := httptest.NewRecorder()
engine.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/committed", nil))
if got := recorder.Header().Get(CPATraceIDHeader); got != "" {
t.Fatalf("trace ID = %q, want empty", got)
}
})
}
func TestCPATraceIDConcurrentSelectionAndResponseCommit(t *testing.T) {
gin.SetMode(gin.TestMode)
engine := gin.New()
engine.Use(CPATraceIDMiddleware())
engine.GET("/race", func(c *gin.Context) {
SetGinRequestID(c, "1234abcd")
traceCallback := GinCPATraceIDCallback(c)
start := make(chan struct{})
done := make(chan struct{})
go func() {
defer close(done)
<-start
traceCallback("auth-index")
}()
close(start)
_, _ = c.Writer.Write([]byte("\n"))
<-done
})
for range 100 {
recorder := httptest.NewRecorder()
engine.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/race", nil))
if recorder.Code != http.StatusOK {
t.Fatalf("status = %d, want %d", recorder.Code, http.StatusOK)
}
}
}