Files
goplt/internal/health/registry_test.go
0x1d 5fdbb729bd
Some checks failed
CI / Test (pull_request) Failing after 17s
CI / Lint (pull_request) Failing after 18s
CI / Build (pull_request) Successful in 12s
CI / Format Check (pull_request) Successful in 2s
test: add comprehensive tests for all Epic 1 stories
Story 1.2: Database Layer
- Test database client creation, connection, ping, and close
- Test connection pooling configuration
- Tests skip if database is not available (short mode)

Story 1.3: Health Monitoring and Metrics
- Test health registry registration and checking
- Test database health checker
- Test liveness and readiness checks
- Test metrics creation, middleware, and handler
- Test Prometheus metrics endpoint

Story 1.4: Error Handling and Error Bus
- Test channel-based error bus creation
- Test error publishing with context
- Test nil error handling
- Test channel full scenario
- Test graceful shutdown
- Fix Close() method to handle multiple calls safely

Story 1.5: HTTP Server and Middleware
- Test server creation with all middleware
- Test request ID middleware
- Test logging middleware
- Test panic recovery middleware
- Test CORS middleware
- Test timeout middleware
- Test health and metrics endpoints
- Test server shutdown

Story 1.6: OpenTelemetry Tracing
- Test tracer initialization (enabled/disabled)
- Test development and production modes
- Test OTLP exporter configuration
- Test graceful shutdown
- Test no-op tracer provider

All tests follow Go testing best practices:
- Table-driven tests where appropriate
- Parallel execution
- Proper mocking of interfaces
- Skip tests requiring external dependencies in short mode
2025-11-05 21:05:36 +01:00

192 lines
4.1 KiB
Go

package health
import (
"context"
"errors"
"testing"
"time"
"git.dcentral.systems/toolz/goplt/pkg/health"
)
func TestNewRegistry(t *testing.T) {
t.Parallel()
registry := NewRegistry()
if registry == nil {
t.Fatal("Expected registry, got nil")
}
if registry.checkers == nil {
t.Error("Expected checkers map, got nil")
}
}
func TestRegistry_Register(t *testing.T) {
t.Parallel()
registry := NewRegistry()
mockChecker := &mockChecker{
checkFunc: func(ctx context.Context) error {
return nil
},
}
registry.Register("test", mockChecker)
// Verify checker is registered
registry.mu.RLock()
checker, ok := registry.checkers["test"]
registry.mu.RUnlock()
if !ok {
t.Error("Expected checker to be registered")
}
if checker != mockChecker {
t.Error("Registered checker does not match")
}
}
func TestRegistry_Check_AllHealthy(t *testing.T) {
t.Parallel()
registry := NewRegistry()
registry.Register("healthy1", &mockChecker{
checkFunc: func(ctx context.Context) error {
return nil
},
})
registry.Register("healthy2", &mockChecker{
checkFunc: func(ctx context.Context) error {
return nil
},
})
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
status := registry.Check(ctx)
if status.Status != health.StatusHealthy {
t.Errorf("Expected status healthy, got %s", status.Status)
}
if len(status.Components) != 2 {
t.Errorf("Expected 2 components, got %d", len(status.Components))
}
for _, component := range status.Components {
if component.Status != health.StatusHealthy {
t.Errorf("Expected component %s to be healthy, got %s", component.Name, component.Status)
}
}
}
func TestRegistry_Check_OneUnhealthy(t *testing.T) {
t.Parallel()
registry := NewRegistry()
registry.Register("healthy", &mockChecker{
checkFunc: func(ctx context.Context) error {
return nil
},
})
registry.Register("unhealthy", &mockChecker{
checkFunc: func(ctx context.Context) error {
return errors.New("component failed")
},
})
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
status := registry.Check(ctx)
if status.Status != health.StatusUnhealthy {
t.Errorf("Expected status unhealthy, got %s", status.Status)
}
if len(status.Components) != 2 {
t.Errorf("Expected 2 components, got %d", len(status.Components))
}
unhealthyFound := false
for _, component := range status.Components {
if component.Name == "unhealthy" {
unhealthyFound = true
if component.Status != health.StatusUnhealthy {
t.Errorf("Expected unhealthy component to be unhealthy, got %s", component.Status)
}
if component.Error == "" {
t.Error("Expected error message for unhealthy component")
}
}
}
if !unhealthyFound {
t.Error("Expected to find unhealthy component")
}
}
func TestRegistry_LivenessCheck(t *testing.T) {
t.Parallel()
registry := NewRegistry()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
status := registry.LivenessCheck(ctx)
if status.Status != health.StatusHealthy {
t.Errorf("Expected liveness check to be healthy, got %s", status.Status)
}
if len(status.Components) != 0 {
t.Errorf("Expected no components in liveness check, got %d", len(status.Components))
}
}
func TestRegistry_ReadinessCheck(t *testing.T) {
t.Parallel()
registry := NewRegistry()
registry.Register("test", &mockChecker{
checkFunc: func(ctx context.Context) error {
return nil
},
})
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
status := registry.ReadinessCheck(ctx)
if status.Status != health.StatusHealthy {
t.Errorf("Expected readiness check to be healthy, got %s", status.Status)
}
if len(status.Components) != 1 {
t.Errorf("Expected 1 component in readiness check, got %d", len(status.Components))
}
}
// mockChecker is a mock implementation of HealthChecker for testing.
type mockChecker struct {
checkFunc func(ctx context.Context) error
}
func (m *mockChecker) Check(ctx context.Context) error {
if m.checkFunc != nil {
return m.checkFunc(ctx)
}
return nil
}