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>
121 lines
2.8 KiB
Go
121 lines
2.8 KiB
Go
package api
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestDocsEndpointSchemeDetection(t *testing.T) {
|
|
// Create a simple app with docs enabled
|
|
app := New("test-api")
|
|
spec := NewOpenAPISpec("Test API", "1.0.0")
|
|
app.EnableDocs(spec)
|
|
|
|
tests := []struct {
|
|
name string
|
|
xForwardedProto string
|
|
expectedScheme string
|
|
}{
|
|
{
|
|
name: "no header defaults to http",
|
|
xForwardedProto: "",
|
|
expectedScheme: "http",
|
|
},
|
|
{
|
|
name: "X-Forwarded-Proto https",
|
|
xForwardedProto: "https",
|
|
expectedScheme: "https",
|
|
},
|
|
{
|
|
name: "X-Forwarded-Proto http",
|
|
xForwardedProto: "http",
|
|
expectedScheme: "http",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
req := httptest.NewRequest(http.MethodGet, "/docs", nil)
|
|
if tt.xForwardedProto != "" {
|
|
req.Header.Set("X-Forwarded-Proto", tt.xForwardedProto)
|
|
}
|
|
rec := httptest.NewRecorder()
|
|
|
|
app.ServeHTTP(rec, req)
|
|
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("expected status 200, got %d", rec.Code)
|
|
}
|
|
|
|
body := rec.Body.String()
|
|
expectedURL := tt.expectedScheme + "://"
|
|
if !strings.Contains(body, expectedURL) {
|
|
t.Errorf("expected body to contain %q scheme URL", expectedURL)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestOpenAPISpec(t *testing.T) {
|
|
spec := NewOpenAPISpec("Test API", "1.0.0").
|
|
WithDescription("Test description").
|
|
WithServer("https://api.example.com", "Production").
|
|
WithTag("test", "Test operations")
|
|
|
|
// Add a path
|
|
spec.AddPath("/test", "get", Op("Get test", "Gets a test", "test"))
|
|
|
|
// Generate JSON
|
|
jsonBytes, err := spec.JSON()
|
|
if err != nil {
|
|
t.Fatalf("failed to generate JSON: %v", err)
|
|
}
|
|
|
|
json := string(jsonBytes)
|
|
|
|
// Verify content
|
|
if !strings.Contains(json, `"title": "Test API"`) {
|
|
t.Error("expected title in JSON")
|
|
}
|
|
if !strings.Contains(json, `"version": "1.0.0"`) {
|
|
t.Error("expected version in JSON")
|
|
}
|
|
if !strings.Contains(json, `"description": "Test description"`) {
|
|
t.Error("expected description in JSON")
|
|
}
|
|
if !strings.Contains(json, `"url": "https://api.example.com"`) {
|
|
t.Error("expected server URL in JSON")
|
|
}
|
|
if !strings.Contains(json, `"/test"`) {
|
|
t.Error("expected path in JSON")
|
|
}
|
|
}
|
|
|
|
func TestOpenAPIJSONEndpoint(t *testing.T) {
|
|
app := New("test-api")
|
|
spec := NewOpenAPISpec("Test API", "1.0.0")
|
|
app.EnableDocs(spec)
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/openapi.json", nil)
|
|
rec := httptest.NewRecorder()
|
|
|
|
app.ServeHTTP(rec, req)
|
|
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("expected status 200, got %d", rec.Code)
|
|
}
|
|
|
|
contentType := rec.Header().Get("Content-Type")
|
|
if contentType != "application/json" {
|
|
t.Errorf("expected Content-Type application/json, got %s", contentType)
|
|
}
|
|
|
|
// Check CORS header
|
|
cors := rec.Header().Get("Access-Control-Allow-Origin")
|
|
if cors != "*" {
|
|
t.Errorf("expected CORS header *, got %s", cors)
|
|
}
|
|
}
|