rdev/internal/handlers/infrastructure_domain_test.go
jordan bc47e426b0 feat: Add CI pipeline proxy, DNS alias management, and worker executor system
- Add ListPipelines/GetPipeline to CIProvider port with Woodpecker adapter
- Add DNS alias endpoints: GET/POST/DELETE /projects/{id}/domains
- Implement worker executor daemon, build executor, and git operations
- Add build service, worker service, and build audit tracking
- Add worker registry with PostgreSQL adapter and migration
- Add multi-provider code agent interface (Claude Code + OpenCode)
- Add create-and-build combo endpoint
- Update landing-page cookbook to reflect all gaps closed
- Fix tech debt: unified validation, auth scopes, error wrapping, slog patterns

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 21:05:28 -07:00

196 lines
5.6 KiB
Go

package handlers
import (
"bytes"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"github.com/orchard9/rdev/internal/domain"
)
func TestInfrastructureHandler_RestartDeploy(t *testing.T) {
t.Run("success", func(t *testing.T) {
_, _, _, _, router := setupInfraHandler()
req := httptest.NewRequest("POST", "/projects/myapp/deploy/restart", nil)
rec := httptest.NewRecorder()
router.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Errorf("status = %d, want %d", rec.Code, http.StatusOK)
}
})
}
func TestInfrastructureHandler_ScaleDeploy(t *testing.T) {
t.Run("valid scale", func(t *testing.T) {
_, _, _, _, router := setupInfraHandler()
body, _ := json.Marshal(ScaleRequest{Replicas: 3})
req := httptest.NewRequest("POST", "/projects/myapp/deploy/scale", bytes.NewReader(body))
rec := httptest.NewRecorder()
router.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Errorf("status = %d, want %d", rec.Code, http.StatusOK)
}
})
t.Run("invalid replicas too high", func(t *testing.T) {
_, _, _, _, router := setupInfraHandler()
body, _ := json.Marshal(ScaleRequest{Replicas: 11})
req := httptest.NewRequest("POST", "/projects/myapp/deploy/scale", bytes.NewReader(body))
rec := httptest.NewRecorder()
router.ServeHTTP(rec, req)
if rec.Code != http.StatusBadRequest {
t.Errorf("status = %d, want %d", rec.Code, http.StatusBadRequest)
}
})
t.Run("invalid replicas negative", func(t *testing.T) {
_, _, _, _, router := setupInfraHandler()
body, _ := json.Marshal(ScaleRequest{Replicas: -1})
req := httptest.NewRequest("POST", "/projects/myapp/deploy/scale", bytes.NewReader(body))
rec := httptest.NewRecorder()
router.ServeHTTP(rec, req)
if rec.Code != http.StatusBadRequest {
t.Errorf("status = %d, want %d", rec.Code, http.StatusBadRequest)
}
})
}
func TestInfrastructureHandler_GetDeployLogs(t *testing.T) {
t.Run("success", func(t *testing.T) {
_, _, _, deployer, router := setupInfraHandler()
deployer.logs = "line1\nline2\nline3\n"
req := httptest.NewRequest("GET", "/projects/myapp/deploy/logs", nil)
rec := httptest.NewRecorder()
router.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Errorf("status = %d, want %d", rec.Code, http.StatusOK)
}
})
}
func TestInfrastructureHandler_AddDomain(t *testing.T) {
t.Run("subdomain", func(t *testing.T) {
_, _, dns, _, router := setupInfraHandler()
body, _ := json.Marshal(AddDomainRequest{Domain: "myapp.threesix.ai"})
req := httptest.NewRequest("POST", "/projects/myapp/domain", bytes.NewReader(body))
rec := httptest.NewRecorder()
router.ServeHTTP(rec, req)
if rec.Code != http.StatusCreated {
t.Errorf("status = %d, want %d", rec.Code, http.StatusCreated)
}
if len(dns.records) != 1 {
t.Errorf("DNS records = %d, want 1", len(dns.records))
}
})
t.Run("external domain", func(t *testing.T) {
_, _, dns, _, router := setupInfraHandler()
body, _ := json.Marshal(AddDomainRequest{Domain: "myapp.example.com"})
req := httptest.NewRequest("POST", "/projects/myapp/domain", bytes.NewReader(body))
rec := httptest.NewRecorder()
router.ServeHTTP(rec, req)
if rec.Code != http.StatusCreated {
t.Errorf("status = %d, want %d", rec.Code, http.StatusCreated)
}
// External domain should NOT create DNS record
if len(dns.records) != 0 {
t.Errorf("DNS records = %d, want 0 (external domain)", len(dns.records))
}
})
t.Run("missing domain", func(t *testing.T) {
_, _, _, _, router := setupInfraHandler()
body, _ := json.Marshal(AddDomainRequest{})
req := httptest.NewRequest("POST", "/projects/myapp/domain", bytes.NewReader(body))
rec := httptest.NewRecorder()
router.ServeHTTP(rec, req)
if rec.Code != http.StatusBadRequest {
t.Errorf("status = %d, want %d", rec.Code, http.StatusBadRequest)
}
})
}
func TestInfrastructureHandler_RemoveDomain(t *testing.T) {
t.Run("subdomain", func(t *testing.T) {
_, _, dns, _, router := setupInfraHandler()
dns.records["myapp"] = &domain.DNSRecord{ID: "rec-myapp", Name: "myapp"}
req := httptest.NewRequest("DELETE", "/projects/myapp/domain?domain=myapp.threesix.ai", nil)
rec := httptest.NewRecorder()
router.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Errorf("status = %d, want %d", rec.Code, http.StatusOK)
}
})
t.Run("missing domain param", func(t *testing.T) {
_, _, _, _, router := setupInfraHandler()
req := httptest.NewRequest("DELETE", "/projects/myapp/domain", nil)
rec := httptest.NewRecorder()
router.ServeHTTP(rec, req)
if rec.Code != http.StatusBadRequest {
t.Errorf("status = %d, want %d", rec.Code, http.StatusBadRequest)
}
})
}
func TestIsSubdomain(t *testing.T) {
tests := []struct {
domain, base string
want bool
}{
{"myapp.threesix.ai", "threesix.ai", true},
{"deep.sub.threesix.ai", "threesix.ai", true},
{"threesix.ai", "threesix.ai", false},
{"myapp.example.com", "threesix.ai", false},
{"", "threesix.ai", false},
}
for _, tt := range tests {
t.Run(tt.domain, func(t *testing.T) {
got := isSubdomain(tt.domain, tt.base)
if got != tt.want {
t.Errorf("isSubdomain(%q, %q) = %v, want %v", tt.domain, tt.base, got, tt.want)
}
})
}
}
func TestGetSubdomain(t *testing.T) {
tests := []struct {
domain, base, want string
}{
{"myapp.threesix.ai", "threesix.ai", "myapp"},
{"deep.sub.threesix.ai", "threesix.ai", "deep.sub"},
{"threesix.ai", "threesix.ai", "threesix.ai"},
}
for _, tt := range tests {
t.Run(tt.domain, func(t *testing.T) {
got := getSubdomain(tt.domain, tt.base)
if got != tt.want {
t.Errorf("getSubdomain(%q, %q) = %q, want %q", tt.domain, tt.base, got, tt.want)
}
})
}
}