Major refactoring to hexagonal (ports & adapters) architecture: - Add service layer (apikey_service, project_service) for business logic - Add webhook system with dispatcher and delivery tracking - Add command queue with priority-based processing - Add rate limiting with sliding window algorithm - Add audit logging for command execution - Add OpenTelemetry integration (traces, metrics, spans) - Add circuit breaker for fault tolerance - Add cached repository wrapper for performance - Add comprehensive validation package - Add Kubernetes client integration for pod management - Add database migrations (allowed_ips, audit_log, rate_limiting, queue, webhooks) - Add network policy and PodDisruptionBudget for k8s - Remove legacy executor and projects/registry packages - Untrack secrets.yaml (now managed via envault) - Add coverage.out to .gitignore - Add e2e test infrastructure with docker-compose - Add comprehensive documentation (API, architecture, operations, plans) - Add golangci-lint config and pre-commit hook Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
92 lines
2.1 KiB
Go
92 lines
2.1 KiB
Go
package handlers
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
)
|
|
|
|
func TestHealthHandler_Health(t *testing.T) {
|
|
h := NewHealthHandler("test-service", nil, nil)
|
|
|
|
req := httptest.NewRequest("GET", "/health", nil)
|
|
rec := httptest.NewRecorder()
|
|
|
|
h.Health(rec, req)
|
|
|
|
if rec.Code != http.StatusOK {
|
|
t.Errorf("Health() status = %d, want %d", rec.Code, http.StatusOK)
|
|
}
|
|
|
|
var resp map[string]any
|
|
if err := json.NewDecoder(rec.Body).Decode(&resp); err != nil {
|
|
t.Fatalf("failed to decode response: %v", err)
|
|
}
|
|
|
|
data, ok := resp["data"].(map[string]any)
|
|
if !ok {
|
|
t.Fatalf("response missing data field")
|
|
}
|
|
|
|
if data["status"] != "ok" {
|
|
t.Errorf("status = %q, want %q", data["status"], "ok")
|
|
}
|
|
if data["service"] != "test-service" {
|
|
t.Errorf("service = %q, want %q", data["service"], "test-service")
|
|
}
|
|
}
|
|
|
|
func TestHealthHandler_Ready_NoDependencies(t *testing.T) {
|
|
// Handler with no dependencies should always be ready
|
|
h := NewHealthHandler("test-service", nil, nil)
|
|
|
|
req := httptest.NewRequest("GET", "/ready", nil)
|
|
rec := httptest.NewRecorder()
|
|
|
|
h.Ready(rec, req)
|
|
|
|
if rec.Code != http.StatusOK {
|
|
t.Errorf("Ready() status = %d, want %d", rec.Code, http.StatusOK)
|
|
}
|
|
|
|
var resp map[string]any
|
|
if err := json.NewDecoder(rec.Body).Decode(&resp); err != nil {
|
|
t.Fatalf("failed to decode response: %v", err)
|
|
}
|
|
|
|
data, ok := resp["data"].(map[string]any)
|
|
if !ok {
|
|
t.Fatalf("response missing data field")
|
|
}
|
|
|
|
if data["status"] != "ready" {
|
|
t.Errorf("status = %q, want %q", data["status"], "ready")
|
|
}
|
|
}
|
|
|
|
func TestCheckResult_JSON(t *testing.T) {
|
|
result := CheckResult{
|
|
Healthy: true,
|
|
Message: "connected",
|
|
Latency: "1ms",
|
|
}
|
|
|
|
data, err := json.Marshal(result)
|
|
if err != nil {
|
|
t.Fatalf("failed to marshal: %v", err)
|
|
}
|
|
|
|
var decoded CheckResult
|
|
if err := json.Unmarshal(data, &decoded); err != nil {
|
|
t.Fatalf("failed to unmarshal: %v", err)
|
|
}
|
|
|
|
if decoded.Healthy != result.Healthy {
|
|
t.Errorf("Healthy = %v, want %v", decoded.Healthy, result.Healthy)
|
|
}
|
|
if decoded.Message != result.Message {
|
|
t.Errorf("Message = %q, want %q", decoded.Message, result.Message)
|
|
}
|
|
}
|