- 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>
199 lines
4.6 KiB
Go
199 lines
4.6 KiB
Go
package sdlc
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
)
|
|
|
|
func setupTestSDLC(t *testing.T) string {
|
|
t.Helper()
|
|
root := t.TempDir()
|
|
if err := Init(root, "test-project"); err != nil {
|
|
t.Fatalf("init failed: %v", err)
|
|
}
|
|
return root
|
|
}
|
|
|
|
func TestCreateBranch(t *testing.T) {
|
|
root := setupTestSDLC(t)
|
|
cfg := DefaultConfig("test")
|
|
|
|
// Create a feature and transition to planned phase (branch creation requires planned or later)
|
|
f, err := CreateFeature(root, "auth-flow", "Auth Flow")
|
|
if err != nil {
|
|
t.Fatalf("create feature: %v", err)
|
|
}
|
|
f.Phase = PhasePlanned
|
|
if err := f.Save(root); err != nil {
|
|
t.Fatalf("save feature: %v", err)
|
|
}
|
|
|
|
manifest, err := CreateBranch(root, "auth-flow", cfg)
|
|
if err != nil {
|
|
t.Fatalf("create branch: %v", err)
|
|
}
|
|
|
|
if manifest.Name != "feature/auth-flow" {
|
|
t.Errorf("Name = %q, want feature/auth-flow", manifest.Name)
|
|
}
|
|
if manifest.Feature != "auth-flow" {
|
|
t.Errorf("Feature = %q, want auth-flow", manifest.Feature)
|
|
}
|
|
if manifest.BaseBranch != "main" {
|
|
t.Errorf("BaseBranch = %q, want main", manifest.BaseBranch)
|
|
}
|
|
|
|
// Verify feature was updated with branch reference
|
|
f, err = LoadFeature(root, "auth-flow")
|
|
if err != nil {
|
|
t.Fatalf("load feature: %v", err)
|
|
}
|
|
if f.Branch != "feature/auth-flow" {
|
|
t.Errorf("Feature.Branch = %q, want feature/auth-flow", f.Branch)
|
|
}
|
|
}
|
|
|
|
func TestCreateBranch_AlreadyExists(t *testing.T) {
|
|
root := setupTestSDLC(t)
|
|
cfg := DefaultConfig("test")
|
|
|
|
f, err := CreateFeature(root, "auth-flow", "Auth Flow")
|
|
if err != nil {
|
|
t.Fatalf("create feature: %v", err)
|
|
}
|
|
f.Phase = PhasePlanned
|
|
if err := f.Save(root); err != nil {
|
|
t.Fatalf("save feature: %v", err)
|
|
}
|
|
|
|
if _, err := CreateBranch(root, "auth-flow", cfg); err != nil {
|
|
t.Fatalf("create branch: %v", err)
|
|
}
|
|
|
|
_, err = CreateBranch(root, "auth-flow", cfg)
|
|
if err != ErrBranchExists {
|
|
t.Errorf("err = %v, want ErrBranchExists", err)
|
|
}
|
|
}
|
|
|
|
func TestCreateBranch_FeatureNotFound(t *testing.T) {
|
|
root := setupTestSDLC(t)
|
|
cfg := DefaultConfig("test")
|
|
|
|
_, err := CreateBranch(root, "nonexistent", cfg)
|
|
if err != ErrFeatureNotFound {
|
|
t.Errorf("err = %v, want ErrFeatureNotFound", err)
|
|
}
|
|
}
|
|
|
|
func TestLoadBranch(t *testing.T) {
|
|
root := setupTestSDLC(t)
|
|
cfg := DefaultConfig("test")
|
|
|
|
f, err := CreateFeature(root, "auth-flow", "Auth Flow")
|
|
if err != nil {
|
|
t.Fatalf("create feature: %v", err)
|
|
}
|
|
f.Phase = PhasePlanned
|
|
if err := f.Save(root); err != nil {
|
|
t.Fatalf("save feature: %v", err)
|
|
}
|
|
|
|
_, err = CreateBranch(root, "auth-flow", cfg)
|
|
if err != nil {
|
|
t.Fatalf("create branch: %v", err)
|
|
}
|
|
|
|
manifest, err := LoadBranch(root, "feature/auth-flow")
|
|
if err != nil {
|
|
t.Fatalf("load branch: %v", err)
|
|
}
|
|
if manifest.Name != "feature/auth-flow" {
|
|
t.Errorf("Name = %q, want feature/auth-flow", manifest.Name)
|
|
}
|
|
}
|
|
|
|
func TestLoadBranch_NotFound(t *testing.T) {
|
|
root := setupTestSDLC(t)
|
|
|
|
_, err := LoadBranch(root, "nonexistent")
|
|
if err != ErrBranchNotFound {
|
|
t.Errorf("err = %v, want ErrBranchNotFound", err)
|
|
}
|
|
}
|
|
|
|
func TestSaveBranch(t *testing.T) {
|
|
root := setupTestSDLC(t)
|
|
|
|
manifest := &BranchManifest{
|
|
Name: "feature/test",
|
|
Feature: "test",
|
|
BaseBranch: "main",
|
|
}
|
|
|
|
if err := SaveBranch(root, manifest); err != nil {
|
|
t.Fatalf("save branch: %v", err)
|
|
}
|
|
|
|
// Verify file exists
|
|
path := filepath.Join(root, SDLCDir, BranchesDir, "feature/test.yaml")
|
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
|
t.Error("branch manifest file not created")
|
|
}
|
|
}
|
|
|
|
func TestMergeChecklist_NotReady(t *testing.T) {
|
|
root := setupTestSDLC(t)
|
|
|
|
_, err := CreateFeature(root, "auth-flow", "Auth Flow")
|
|
if err != nil {
|
|
t.Fatalf("create feature: %v", err)
|
|
}
|
|
|
|
unmet, err := MergeChecklist(root, "auth-flow")
|
|
if err != nil {
|
|
t.Fatalf("merge checklist: %v", err)
|
|
}
|
|
|
|
if len(unmet) == 0 {
|
|
t.Error("expected unmet gates, got none")
|
|
}
|
|
}
|
|
|
|
func TestMergeChecklist_Ready(t *testing.T) {
|
|
root := setupTestSDLC(t)
|
|
|
|
f, err := CreateFeature(root, "auth-flow", "Auth Flow")
|
|
if err != nil {
|
|
t.Fatalf("create feature: %v", err)
|
|
}
|
|
|
|
// Set up all gates as passed and transition to merge phase
|
|
f.Phase = PhaseMerge
|
|
f.GetArtifact(ArtifactReview).MarkPassed()
|
|
f.GetArtifact(ArtifactAudit).MarkPassed()
|
|
f.GetArtifact(ArtifactQAResults).MarkPassed()
|
|
if err := f.Save(root); err != nil {
|
|
t.Fatalf("save feature: %v", err)
|
|
}
|
|
|
|
unmet, err := MergeChecklist(root, "auth-flow")
|
|
if err != nil {
|
|
t.Fatalf("merge checklist: %v", err)
|
|
}
|
|
|
|
if len(unmet) != 0 {
|
|
t.Errorf("expected 0 unmet gates, got %d: %v", len(unmet), unmet)
|
|
}
|
|
}
|
|
|
|
func TestMergeChecklist_FeatureNotFound(t *testing.T) {
|
|
root := setupTestSDLC(t)
|
|
|
|
_, err := MergeChecklist(root, "nonexistent")
|
|
if err != ErrFeatureNotFound {
|
|
t.Errorf("err = %v, want ErrFeatureNotFound", err)
|
|
}
|
|
}
|