rdev/internal/handlers/sdlc_orchestrator_test.go
jordan d69da6d627 feat: add structured logging infrastructure and SDLC extensions
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>
2026-02-04 22:56:04 -07:00

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