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) } }) } }