Add deploy-{name} CI steps to all component templates (app-astro,
app-react, service, worker) so each component deploys independently
via kubectl set image on merge to main. Replace the skeleton's
generic deploy step with a verify step that confirms deployments.
Add GET /templates/components endpoint for listing available component
templates with optional type filter. Simplify component API by merging
type+template into a single type field (e.g., "app-react" instead of
type="app" template="app-react").
Include ESLint configs and pnpm-workspace.yaml in app templates.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
87 lines
2.4 KiB
Go
87 lines
2.4 KiB
Go
package handlers
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"log/slog"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
)
|
|
|
|
func TestProjectManagementHandler_NilService(t *testing.T) {
|
|
h := NewProjectManagementHandler(nil, slog.Default())
|
|
r := chi.NewRouter()
|
|
h.Mount(r)
|
|
|
|
tests := []struct {
|
|
name string
|
|
method string
|
|
path string
|
|
body string
|
|
}{
|
|
{"create", "POST", "/project", `{"name":"test"}`},
|
|
{"list", "GET", "/project", ""},
|
|
{"status", "GET", "/project/test", ""},
|
|
{"delete", "DELETE", "/project/test", ""},
|
|
{"list templates", "GET", "/templates", ""},
|
|
{"list component templates", "GET", "/templates/components", ""},
|
|
{"get template", "GET", "/templates/default", ""},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
var req *http.Request
|
|
if tt.body != "" {
|
|
req = httptest.NewRequest(tt.method, tt.path, bytes.NewReader([]byte(tt.body)))
|
|
} else {
|
|
req = httptest.NewRequest(tt.method, tt.path, nil)
|
|
}
|
|
rec := httptest.NewRecorder()
|
|
r.ServeHTTP(rec, req)
|
|
|
|
if rec.Code != http.StatusInternalServerError {
|
|
t.Errorf("%s: status = %d, want %d", tt.name, rec.Code, http.StatusInternalServerError)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestProjectManagementHandler_CreateValidation(t *testing.T) {
|
|
// With nil service, the handler returns 500 before reaching validation.
|
|
// This tests that the nil check takes precedence.
|
|
h := NewProjectManagementHandler(nil, slog.Default())
|
|
r := chi.NewRouter()
|
|
h.Mount(r)
|
|
|
|
t.Run("nil service returns 500 even with missing name", func(t *testing.T) {
|
|
body, _ := json.Marshal(CreateRequest{Name: ""})
|
|
req := httptest.NewRequest("POST", "/project", bytes.NewReader(body))
|
|
rec := httptest.NewRecorder()
|
|
r.ServeHTTP(rec, req)
|
|
|
|
if rec.Code != http.StatusInternalServerError {
|
|
t.Errorf("status = %d, want %d", rec.Code, http.StatusInternalServerError)
|
|
}
|
|
})
|
|
|
|
t.Run("nil service returns 500 even with invalid json", func(t *testing.T) {
|
|
req := httptest.NewRequest("POST", "/project", bytes.NewReader([]byte("not json")))
|
|
rec := httptest.NewRecorder()
|
|
r.ServeHTTP(rec, req)
|
|
|
|
if rec.Code != http.StatusInternalServerError {
|
|
t.Errorf("status = %d, want %d", rec.Code, http.StatusInternalServerError)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestNewProjectManagementHandler_NilLogger(t *testing.T) {
|
|
h := NewProjectManagementHandler(nil, nil)
|
|
if h.logger == nil {
|
|
t.Error("logger should default to slog.Default() when nil")
|
|
}
|
|
}
|