package internalhttp import ( "context" "net/http" "net/http/httptest" "testing" "galaxy/authsession/internal/service/blockuser" "galaxy/authsession/internal/service/getsession" "galaxy/authsession/internal/service/listusersessions" "galaxy/authsession/internal/service/revokeallusersessions" "galaxy/authsession/internal/service/revokedevicesession" "galaxy/authsession/internal/service/shared" authtelemetry "galaxy/authsession/internal/telemetry" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/attribute" sdkmetric "go.opentelemetry.io/otel/sdk/metric" "go.opentelemetry.io/otel/sdk/metric/metricdata" sdktrace "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/sdk/trace/tracetest" ) func TestInternalHandlerEmitsTraceFieldsAndMetrics(t *testing.T) { t.Parallel() logger, buffer := newObservedLogger() telemetryRuntime, reader, recorder := newObservedInternalTelemetryRuntime(t) handler := mustNewHandler(t, DefaultConfig(), Dependencies{ Logger: logger, Telemetry: telemetryRuntime, GetSession: getSessionFunc(func(context.Context, getsession.Input) (getsession.Result, error) { return getsession.Result{Session: validSessionDTO()}, nil }), ListUserSessions: listUserSessionsFunc(func(context.Context, listusersessions.Input) (listusersessions.Result, error) { return listusersessions.Result{Sessions: []shared.Session{}}, nil }), RevokeDeviceSession: revokeDeviceSessionFunc(func(context.Context, revokedevicesession.Input) (revokedevicesession.Result, error) { return revokedevicesession.Result{}, nil }), RevokeAllUserSessions: revokeAllUserSessionsFunc(func(context.Context, revokeallusersessions.Input) (revokeallusersessions.Result, error) { return revokeallusersessions.Result{}, nil }), BlockUser: blockUserFunc(func(context.Context, blockuser.Input) (blockuser.Result, error) { return blockuser.Result{}, nil }), }) recorderHTTP := httptest.NewRecorder() request := httptest.NewRequest(http.MethodGet, "/api/v1/internal/sessions/device-session-123", nil) handler.ServeHTTP(recorderHTTP, request) require.Equal(t, http.StatusOK, recorderHTTP.Code) require.NotEmpty(t, recorder.Ended()) assert.Contains(t, buffer.String(), "otel_trace_id") assert.Contains(t, buffer.String(), "otel_span_id") assertMetricCount(t, reader, "authsession.internal_http.requests", map[string]string{ "route": "/api/v1/internal/sessions/:device_session_id", "method": http.MethodGet, "edge_outcome": "success", }, 1) } func newObservedInternalTelemetryRuntime(t *testing.T) (*authtelemetry.Runtime, *sdkmetric.ManualReader, *tracetest.SpanRecorder) { t.Helper() reader := sdkmetric.NewManualReader() meterProvider := sdkmetric.NewMeterProvider(sdkmetric.WithReader(reader)) recorder := tracetest.NewSpanRecorder() tracerProvider := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(recorder)) runtime, err := authtelemetry.NewWithProviders(meterProvider, tracerProvider) require.NoError(t, err) return runtime, reader, recorder } func assertMetricCount(t *testing.T, reader *sdkmetric.ManualReader, metricName string, wantAttrs map[string]string, wantValue int64) { t.Helper() var resourceMetrics metricdata.ResourceMetrics require.NoError(t, reader.Collect(context.Background(), &resourceMetrics)) for _, scopeMetrics := range resourceMetrics.ScopeMetrics { for _, metric := range scopeMetrics.Metrics { if metric.Name != metricName { continue } sum, ok := metric.Data.(metricdata.Sum[int64]) require.True(t, ok) for _, point := range sum.DataPoints { if hasMetricAttributes(point.Attributes.ToSlice(), wantAttrs) { assert.Equal(t, wantValue, point.Value) return } } } } require.Failf(t, "test failed", "metric %q with attrs %v not found", metricName, wantAttrs) } func hasMetricAttributes(values []attribute.KeyValue, want map[string]string) bool { if len(values) != len(want) { return false } for _, value := range values { if want[string(value.Key)] != value.Value.AsString() { return false } } return true }