Add comprehensive test coverage improvements
Improved overall test coverage from 37.9% to 51.0% (+13.1 percentage points) New test files: - internal/observability/metrics_test.go (18 test functions) - internal/observability/tracing_test.go (11 test functions) - internal/observability/provider_wrapper_test.go (12 test functions) - internal/conversation/sql_store_test.go (16 test functions) - internal/conversation/redis_store_test.go (15 test functions) Test helper utilities: - internal/observability/testing.go - internal/conversation/testing.go Coverage improvements by package: - internal/conversation: 0% → 66.0% (+66.0%) - internal/observability: 0% → 34.5% (+34.5%) Test infrastructure: - Added miniredis/v2 for Redis store testing - Added prometheus/testutil for metrics testing Total: ~2,000 lines of test code, 72 new test functions Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
120
internal/observability/testing.go
Normal file
120
internal/observability/testing.go
Normal file
@@ -0,0 +1,120 @@
|
||||
package observability
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/testutil"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/sdk/resource"
|
||||
sdktrace "go.opentelemetry.io/otel/sdk/trace"
|
||||
"go.opentelemetry.io/otel/sdk/trace/tracetest"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
|
||||
)
|
||||
|
||||
// NewTestRegistry creates a new isolated Prometheus registry for testing
|
||||
func NewTestRegistry() *prometheus.Registry {
|
||||
return prometheus.NewRegistry()
|
||||
}
|
||||
|
||||
// NewTestTracer creates a no-op tracer for testing
|
||||
func NewTestTracer() (*sdktrace.TracerProvider, *tracetest.InMemoryExporter) {
|
||||
exporter := tracetest.NewInMemoryExporter()
|
||||
res := resource.NewSchemaless(
|
||||
semconv.ServiceNameKey.String("test-service"),
|
||||
)
|
||||
tp := sdktrace.NewTracerProvider(
|
||||
sdktrace.WithSyncer(exporter),
|
||||
sdktrace.WithResource(res),
|
||||
)
|
||||
otel.SetTracerProvider(tp)
|
||||
return tp, exporter
|
||||
}
|
||||
|
||||
// GetMetricValue extracts a metric value from a registry
|
||||
func GetMetricValue(registry *prometheus.Registry, metricName string) (float64, error) {
|
||||
metrics, err := registry.Gather()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
for _, mf := range metrics {
|
||||
if mf.GetName() == metricName {
|
||||
if len(mf.GetMetric()) > 0 {
|
||||
m := mf.GetMetric()[0]
|
||||
if m.GetCounter() != nil {
|
||||
return m.GetCounter().GetValue(), nil
|
||||
}
|
||||
if m.GetGauge() != nil {
|
||||
return m.GetGauge().GetValue(), nil
|
||||
}
|
||||
if m.GetHistogram() != nil {
|
||||
return float64(m.GetHistogram().GetSampleCount()), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// CountMetricsWithName counts how many metrics match the given name
|
||||
func CountMetricsWithName(registry *prometheus.Registry, metricName string) (int, error) {
|
||||
metrics, err := registry.Gather()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
for _, mf := range metrics {
|
||||
if mf.GetName() == metricName {
|
||||
return len(mf.GetMetric()), nil
|
||||
}
|
||||
}
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// GetCounterValue is a helper to get counter values using testutil
|
||||
func GetCounterValue(counter prometheus.Counter) float64 {
|
||||
return testutil.ToFloat64(counter)
|
||||
}
|
||||
|
||||
// NewNoOpTracerProvider creates a tracer provider that discards all spans
|
||||
func NewNoOpTracerProvider() *sdktrace.TracerProvider {
|
||||
return sdktrace.NewTracerProvider(
|
||||
sdktrace.WithSpanProcessor(sdktrace.NewSimpleSpanProcessor(&noOpExporter{})),
|
||||
)
|
||||
}
|
||||
|
||||
// noOpExporter is an exporter that discards all spans
|
||||
type noOpExporter struct{}
|
||||
|
||||
func (e *noOpExporter) ExportSpans(context.Context, []sdktrace.ReadOnlySpan) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *noOpExporter) Shutdown(context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ShutdownTracer is a helper to safely shutdown a tracer provider
|
||||
func ShutdownTracer(tp *sdktrace.TracerProvider) error {
|
||||
if tp != nil {
|
||||
return tp.Shutdown(context.Background())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewTestExporter creates a test exporter that writes to the provided writer
|
||||
type TestExporter struct {
|
||||
writer io.Writer
|
||||
}
|
||||
|
||||
func (e *TestExporter) ExportSpans(ctx context.Context, spans []sdktrace.ReadOnlySpan) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *TestExporter) Shutdown(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user