package telemetry import ( "bytes" "context" "testing" "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.uber.org/zap" ) func TestNewProcessBuildsWithoutExporters(t *testing.T) { runtime, err := newProcess(context.Background(), ProcessConfig{ ServiceName: "galaxy-authsession-test", TracesExporter: processExporterNone, MetricsExporter: processExporterNone, }, zap.NewNop(), ioDiscard{}, ioDiscard{}) require.NoError(t, err) assert.NotNil(t, runtime.TracerProvider()) assert.NotNil(t, runtime.MeterProvider()) require.NoError(t, runtime.Shutdown(context.Background())) require.NoError(t, runtime.Shutdown(context.Background())) } func TestNewProcessBuildsWithStdoutExporters(t *testing.T) { traceBuffer := &bytes.Buffer{} metricBuffer := &bytes.Buffer{} runtime, err := newProcess(context.Background(), ProcessConfig{ ServiceName: "galaxy-authsession-test", TracesExporter: processExporterNone, MetricsExporter: processExporterNone, StdoutTracesEnabled: true, StdoutMetricsEnabled: true, }, zap.NewNop(), traceBuffer, metricBuffer) require.NoError(t, err) ctx, span := runtime.TracerProvider().Tracer("test").Start(context.Background(), "public-request") runtime.RecordSendEmailCode(ctx, SendEmailCodeOutcomeSent, "") span.End() require.NoError(t, runtime.Shutdown(context.Background())) assert.NotEmpty(t, traceBuffer.String()) assert.NotEmpty(t, metricBuffer.String()) } func TestNewPreservesBusinessMetrics(t *testing.T) { reader := sdkmetric.NewManualReader() meterProvider := sdkmetric.NewMeterProvider(sdkmetric.WithReader(reader)) tracerProvider := sdktrace.NewTracerProvider() runtime, err := NewWithProviders(meterProvider, tracerProvider) require.NoError(t, err) runtime.RecordSendEmailCode(context.Background(), SendEmailCodeOutcomeSuppressed, SendEmailCodeReasonBlocked) runtime.RecordUserDirectoryOutcome(context.Background(), "ensure_user_by_email", "created") runtime.RecordSessionRevocations(context.Background(), "block_user", "user_blocked", 2) assertMetricCount(t, reader, "authsession.send_email_code.attempts", map[string]string{ "outcome": "suppressed", "reason": "blocked", }, 1) assertMetricCount(t, reader, "authsession.user_directory.outcomes", map[string]string{ "operation": "ensure_user_by_email", "outcome": "created", }, 1) assertMetricCount(t, reader, "authsession.sessions.revoked", map[string]string{ "operation": "block_user", "reason_bucket": "user_blocked", }, 2) } type ioDiscard struct{} func (ioDiscard) Write(p []byte) (int, error) { return len(p), nil } 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 }