package telemetry import ( "context" "testing" "time" "scrabble/pkg/version" ) // TestConfigValidate covers the supported and rejected exporter selections. func TestConfigValidate(t *testing.T) { for _, exp := range []string{ExporterNone, ExporterStdout, ExporterOTLP} { c := DefaultConfig("svc") c.TracesExporter = exp c.MetricsExporter = exp if err := c.Validate(); err != nil { t.Errorf("exporter %q must be valid: %v", exp, err) } } bad := DefaultConfig("svc") bad.TracesExporter = "prometheus" if err := bad.Validate(); err == nil { t.Error("unsupported exporter must be rejected") } if err := DefaultConfig("").Validate(); err == nil { t.Error("empty service name must be rejected") } } // TestNewNoneAndStdout builds the providers with the none and stdout exporters, // starts the runtime metrics and shuts them down. func TestNewNoneAndStdout(t *testing.T) { for _, exp := range []string{ExporterNone, ExporterStdout} { cfg := DefaultConfig("svc") cfg.TracesExporter = exp cfg.MetricsExporter = exp rt, err := New(context.Background(), cfg) if err != nil { t.Fatalf("New(%q): %v", exp, err) } if rt.TracerProvider() == nil || rt.MeterProvider() == nil { t.Errorf("New(%q): nil provider", exp) } if err := rt.StartRuntimeMetrics(); err != nil { t.Errorf("StartRuntimeMetrics(%q): %v", exp, err) } if err := rt.Shutdown(context.Background()); err != nil { t.Errorf("Shutdown(%q): %v", exp, err) } } } // TestNewOTLPConstructs verifies the otlp exporter selection builds providers // without a reachable collector (the gRPC exporters dial lazily). Shutdown is // best-effort: with no collector the final export may error, so it is bounded and // its result ignored. func TestNewOTLPConstructs(t *testing.T) { cfg := DefaultConfig("svc") cfg.TracesExporter = ExporterOTLP cfg.MetricsExporter = ExporterOTLP rt, err := New(context.Background(), cfg) if err != nil { t.Fatalf("New(otlp): %v", err) } if rt.TracerProvider() == nil || rt.MeterProvider() == nil { t.Error("New(otlp): nil provider") } ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() _ = rt.Shutdown(ctx) } // TestNilRuntime checks the nil-receiver fallbacks used before initialisation. func TestNilRuntime(t *testing.T) { var rt *Runtime if rt.TracerProvider() == nil { t.Error("nil runtime must fall back to the global tracer provider") } if rt.MeterProvider() == nil { t.Error("nil runtime must fall back to the global meter provider") } if err := rt.Shutdown(context.Background()); err != nil { t.Errorf("nil runtime Shutdown: %v", err) } } // TestServiceResource checks the resource carries service.name and the embedded // service.version (pkg/version, stamped at build time). func TestServiceResource(t *testing.T) { res, err := serviceResource(context.Background(), DefaultConfig("svc")) if err != nil { t.Fatalf("serviceResource: %v", err) } attrs := map[string]string{} for _, kv := range res.Attributes() { attrs[string(kv.Key)] = kv.Value.AsString() } if attrs["service.name"] != "svc" { t.Errorf("service.name = %q, want svc", attrs["service.name"]) } if attrs["service.version"] != version.Version { t.Errorf("service.version = %q, want %q", attrs["service.version"], version.Version) } }