Major changes: - Add internal/logging package with field constants, context propagation, sensitive data auto-redaction, and per-component log levels - Add worker timeout constants (TimeoutQuickOp, TimeoutHealthCheck, etc.) - Extend SDLC with callback handlers, generate endpoints, and executor - Add new cookbook trees for aeries and slackpath progression - Add skeleton templates for queue, realtime, and microservices - Add worker component template with async job processing - Refactor services and handlers to use new logging infrastructure - Split component.go into component_infra.go and component_listing.go Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
187 lines
6.0 KiB
Go
187 lines
6.0 KiB
Go
package handlers
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
"github.com/orchard9/rdev/internal/domain"
|
|
"github.com/orchard9/rdev/internal/sdlc"
|
|
"github.com/orchard9/rdev/internal/service"
|
|
)
|
|
|
|
func setupOrchestratorHandler(exec *testSDLCExecutor) (*SDLCOrchestratorHandler, *chi.Mux) {
|
|
repo := &testSDLCProjectRepo{
|
|
project: &domain.Project{ID: "test-project", PodName: "test-pod"},
|
|
}
|
|
sdlcSvc := service.NewSDLCService(exec, repo)
|
|
|
|
orchestrator := service.NewSDLCOrchestratorService(
|
|
sdlcSvc,
|
|
nil, // no agent registry for handler tests
|
|
nil, // no git committer for handler tests
|
|
repo,
|
|
)
|
|
|
|
handler := NewSDLCOrchestratorHandler(orchestrator)
|
|
r := chi.NewRouter()
|
|
r.Use(testAdminAuth)
|
|
handler.Mount(r)
|
|
return handler, r
|
|
}
|
|
|
|
func TestSDLCOrchestratorHandler_Execute(t *testing.T) {
|
|
exec := &testSDLCExecutor{
|
|
classification: &sdlc.Classification{
|
|
Action: sdlc.ActionIdle,
|
|
Feature: "auth-flow",
|
|
Message: "No action needed",
|
|
},
|
|
}
|
|
_, router := setupOrchestratorHandler(exec)
|
|
|
|
body, _ := json.Marshal(service.ExecuteRequest{Feature: "auth-flow"})
|
|
req := httptest.NewRequest(http.MethodPost, "/projects/test-project/sdlc/execute", bytes.NewReader(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusOK {
|
|
t.Errorf("expected status 200, got %d: %s", w.Code, w.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestSDLCOrchestratorHandler_Execute_MissingFeature(t *testing.T) {
|
|
exec := &testSDLCExecutor{}
|
|
_, router := setupOrchestratorHandler(exec)
|
|
|
|
body, _ := json.Marshal(service.ExecuteRequest{})
|
|
req := httptest.NewRequest(http.MethodPost, "/projects/test-project/sdlc/execute", bytes.NewReader(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("expected status 400, got %d: %s", w.Code, w.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestSDLCOrchestratorHandler_Execute_InvalidBody(t *testing.T) {
|
|
exec := &testSDLCExecutor{}
|
|
_, router := setupOrchestratorHandler(exec)
|
|
|
|
req := httptest.NewRequest(http.MethodPost, "/projects/test-project/sdlc/execute", bytes.NewReader([]byte("not json")))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("expected status 400, got %d: %s", w.Code, w.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestSDLCOrchestratorHandler_Execute_FeatureNotFound(t *testing.T) {
|
|
exec := &testSDLCExecutor{err: sdlc.ErrFeatureNotFound}
|
|
_, router := setupOrchestratorHandler(exec)
|
|
|
|
body, _ := json.Marshal(service.ExecuteRequest{Feature: "nonexistent"})
|
|
req := httptest.NewRequest(http.MethodPost, "/projects/test-project/sdlc/execute", bytes.NewReader(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusNotFound {
|
|
t.Errorf("expected status 404, got %d: %s", w.Code, w.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestSDLCOrchestratorHandler_Execute_Transition(t *testing.T) {
|
|
exec := &testSDLCExecutor{
|
|
classification: &sdlc.Classification{
|
|
Action: sdlc.ActionTransition,
|
|
Feature: "auth-flow",
|
|
TransitionTo: sdlc.PhaseSpecified,
|
|
},
|
|
}
|
|
_, router := setupOrchestratorHandler(exec)
|
|
|
|
body, _ := json.Marshal(service.ExecuteRequest{Feature: "auth-flow"})
|
|
req := httptest.NewRequest(http.MethodPost, "/projects/test-project/sdlc/execute", bytes.NewReader(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusOK {
|
|
t.Errorf("expected status 200, got %d: %s", w.Code, w.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestSDLCOrchestratorHandler_Resolve(t *testing.T) {
|
|
exec := &testSDLCExecutor{
|
|
classification: &sdlc.Classification{
|
|
Action: sdlc.ActionIdle,
|
|
Feature: "auth-flow",
|
|
},
|
|
}
|
|
_, router := setupOrchestratorHandler(exec)
|
|
|
|
body, _ := json.Marshal(service.ResolveRequest{Feature: "auth-flow"})
|
|
req := httptest.NewRequest(http.MethodPost, "/projects/test-project/sdlc/resolve", bytes.NewReader(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusOK {
|
|
t.Errorf("expected status 200, got %d: %s", w.Code, w.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestSDLCOrchestratorHandler_Resolve_MissingFeature(t *testing.T) {
|
|
exec := &testSDLCExecutor{}
|
|
_, router := setupOrchestratorHandler(exec)
|
|
|
|
body, _ := json.Marshal(service.ResolveRequest{})
|
|
req := httptest.NewRequest(http.MethodPost, "/projects/test-project/sdlc/resolve", bytes.NewReader(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("expected status 400, got %d: %s", w.Code, w.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestSDLCOrchestratorHandler_Commit(t *testing.T) {
|
|
exec := &testSDLCExecutor{}
|
|
_, router := setupOrchestratorHandler(exec)
|
|
|
|
body, _ := json.Marshal(service.CommitRequest{Feature: "auth-flow", Message: "implement auth"})
|
|
req := httptest.NewRequest(http.MethodPost, "/projects/test-project/sdlc/commit", bytes.NewReader(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
|
|
// Expected 500 because git committer is nil in test setup
|
|
if w.Code != http.StatusInternalServerError {
|
|
t.Errorf("expected status 500 (no git committer), got %d: %s", w.Code, w.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestSDLCOrchestratorHandler_Commit_MissingMessage(t *testing.T) {
|
|
exec := &testSDLCExecutor{}
|
|
_, router := setupOrchestratorHandler(exec)
|
|
|
|
body, _ := json.Marshal(service.CommitRequest{Feature: "auth-flow"})
|
|
req := httptest.NewRequest(http.MethodPost, "/projects/test-project/sdlc/commit", bytes.NewReader(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("expected status 400, got %d: %s", w.Code, w.Body.String())
|
|
}
|
|
}
|