rdev/internal/handlers/sdlc_orchestrator_test.go
jordan 56e3f83955 feat: add auth scopes, OpenAPI docs, SDLC guides, and code quality improvements
- Add auth.RequireScope() to all handler routes for proper authorization
- Add SDLC OpenAPI endpoint documentation (state, features, tasks, branches, merge, archive, orchestrator)
- Add SDLC documentation guides (getting-started, cli-reference, api-reference, command-catalog)
- Add artifact_test.go for SDLC artifact coverage
- Add CLAUDE.md rules: auth scopes requirement, error wrapping with %w
- Fix error wrapping to use %w instead of %v throughout codebase
- Improve CLI merge command with conflict detection and resolution
- Fix handler tests to include auth middleware for RequireScope
- Add cookbook tree runner scripts for automated testing

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 13:55:50 -07:00

188 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, service.SDLCServiceConfig{})
orchestrator := service.NewSDLCOrchestratorService(
sdlcSvc,
nil, // no agent registry for handler tests
nil, // no git committer for handler tests
repo,
service.SDLCOrchestratorConfig{},
)
handler := NewSDLCOrchestratorHandler(orchestrator, nil)
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())
}
}