- Add internal/health package with health checker - Implement three endpoints: /health, /health/live, /health/ready - /health returns full JSON report with component status, latency, timestamp - /health/live for K8s liveness probe (service running) - /health/ready for K8s readiness probe (ready to accept traffic) - Temporal connectivity check via GetWorkflow call with timeout - Health check caching (30s interval) to prevent excessive checks - Graceful shutdown: health server stops on SIGINT/SIGTERM - Add --health flag to starter command to run health check - Worker runs health server on port 8081 alongside task queue worker - 10/10 unit tests passing - All verification criteria met Closes T1.8
111 lines
3.2 KiB
Go
111 lines
3.2 KiB
Go
package health
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
// TestNewChecker tests the creation of a new health checker
|
|
func TestNewChecker(t *testing.T) {
|
|
checker := NewChecker(nil)
|
|
assert.NotNil(t, checker)
|
|
assert.Equal(t, 30*time.Second, checker.checkInterval)
|
|
}
|
|
|
|
// TestCheckWithNilClient tests health check with nil client
|
|
func TestCheckWithNilClient(t *testing.T) {
|
|
checker := NewChecker(nil)
|
|
report := checker.Check(context.Background())
|
|
|
|
assert.NotNil(t, report)
|
|
assert.Equal(t, StatusUnhealthy, report.Status)
|
|
assert.Len(t, report.Components, 1)
|
|
assert.Equal(t, StatusUnhealthy, report.Components["temporal"].Status)
|
|
assert.Equal(t, "Temporal client not initialized", report.Components["temporal"].Error)
|
|
}
|
|
|
|
// TestIsHealthy tests the IsHealthy method
|
|
func TestIsHealthy(t *testing.T) {
|
|
checker := NewChecker(nil)
|
|
assert.False(t, checker.IsHealthy(context.Background()))
|
|
}
|
|
|
|
// TestGetReport tests the GetReport method
|
|
func TestGetReport(t *testing.T) {
|
|
checker := NewChecker(nil)
|
|
report := checker.GetReport(context.Background())
|
|
assert.NotNil(t, report)
|
|
assert.Equal(t, StatusUnhealthy, report.Status)
|
|
}
|
|
|
|
// TestHealthReportJSON tests JSON serialization
|
|
func TestHealthReportJSON(t *testing.T) {
|
|
checker := NewChecker(nil)
|
|
report := checker.Check(context.Background())
|
|
|
|
jsonData, err := report.ToJSON()
|
|
assert.NoError(t, err)
|
|
assert.NotEmpty(t, jsonData)
|
|
assert.Contains(t, string(jsonData), "unhealthy")
|
|
assert.Contains(t, string(jsonData), "temporal")
|
|
}
|
|
|
|
// TestHealthReportTimestamp tests that timestamp is set
|
|
func TestHealthReportTimestamp(t *testing.T) {
|
|
checker := NewChecker(nil)
|
|
before := time.Now()
|
|
report := checker.Check(context.Background())
|
|
after := time.Now()
|
|
|
|
assert.True(t, report.Timestamp.After(before) || report.Timestamp.Equal(before))
|
|
assert.True(t, report.Timestamp.Before(after) || report.Timestamp.Equal(after))
|
|
}
|
|
|
|
// TestStatusString tests Status string representation
|
|
func TestStatusString(t *testing.T) {
|
|
assert.Equal(t, "healthy", StatusHealthy.String())
|
|
assert.Equal(t, "unhealthy", StatusUnhealthy.String())
|
|
assert.Equal(t, "unknown", StatusUnknown.String())
|
|
}
|
|
|
|
// TestComponentHealthLatency tests that latency is recorded
|
|
func TestComponentHealthLatency(t *testing.T) {
|
|
checker := NewChecker(nil)
|
|
report := checker.Check(context.Background())
|
|
|
|
assert.NotNil(t, report.Components["temporal"])
|
|
assert.GreaterOrEqual(t, report.Components["temporal"].Latency, int64(0))
|
|
}
|
|
|
|
// TestHealthCheckCaching tests that recent checks are cached
|
|
func TestHealthCheckCaching(t *testing.T) {
|
|
checker := NewChecker(nil)
|
|
|
|
// First check
|
|
report1 := checker.Check(context.Background())
|
|
time1 := report1.Timestamp
|
|
|
|
// Second check immediately (should be cached)
|
|
time.Sleep(100 * time.Millisecond)
|
|
report2 := checker.Check(context.Background())
|
|
time2 := report2.Timestamp
|
|
|
|
// Timestamps should be the same or very close (cached)
|
|
assert.Equal(t, time1, time2, "second check should use cached result")
|
|
}
|
|
|
|
// TestComponentHealthDefaults tests default component health values
|
|
func TestComponentHealthDefaults(t *testing.T) {
|
|
comp := ComponentHealth{
|
|
Name: "test",
|
|
Status: StatusHealthy,
|
|
}
|
|
|
|
assert.Equal(t, "test", comp.Name)
|
|
assert.Equal(t, StatusHealthy, comp.Status)
|
|
assert.Empty(t, comp.Error)
|
|
}
|