- 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>
150 lines
3.8 KiB
Go
150 lines
3.8 KiB
Go
package sdlc
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestNewArtifact(t *testing.T) {
|
|
tests := []struct {
|
|
artType ArtifactType
|
|
wantPath string
|
|
}{
|
|
{ArtifactSpec, "spec.md"},
|
|
{ArtifactDesign, "design.md"},
|
|
{ArtifactTasks, "tasks.md"},
|
|
{ArtifactQAPlan, "qa-plan.md"},
|
|
{ArtifactReview, "review.md"},
|
|
{ArtifactAudit, "audit.md"},
|
|
{ArtifactQAResults, "qa-results.md"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(string(tt.artType), func(t *testing.T) {
|
|
art := NewArtifact(tt.artType)
|
|
if art.Status != StatusPending {
|
|
t.Errorf("NewArtifact() status = %v, want %v", art.Status, StatusPending)
|
|
}
|
|
if art.Path != tt.wantPath {
|
|
t.Errorf("NewArtifact() path = %v, want %v", art.Path, tt.wantPath)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestArtifact_Approve(t *testing.T) {
|
|
art := NewArtifact(ArtifactSpec)
|
|
art.Approve("user@example.com")
|
|
|
|
if art.Status != StatusApproved {
|
|
t.Errorf("Approve() status = %v, want %v", art.Status, StatusApproved)
|
|
}
|
|
if art.ApprovedBy != "user@example.com" {
|
|
t.Errorf("Approve() approvedBy = %v, want %v", art.ApprovedBy, "user@example.com")
|
|
}
|
|
if art.ApprovedAt == nil {
|
|
t.Error("Approve() approvedAt should not be nil")
|
|
}
|
|
if art.RejectedBy != "" {
|
|
t.Errorf("Approve() should clear rejectedBy, got %v", art.RejectedBy)
|
|
}
|
|
if art.RejectedAt != nil {
|
|
t.Error("Approve() should clear rejectedAt")
|
|
}
|
|
}
|
|
|
|
func TestArtifact_Reject(t *testing.T) {
|
|
art := NewArtifact(ArtifactSpec)
|
|
art.Reject("reviewer@example.com")
|
|
|
|
if art.Status != StatusRejected {
|
|
t.Errorf("Reject() status = %v, want %v", art.Status, StatusRejected)
|
|
}
|
|
if art.RejectedBy != "reviewer@example.com" {
|
|
t.Errorf("Reject() rejectedBy = %v, want %v", art.RejectedBy, "reviewer@example.com")
|
|
}
|
|
if art.RejectedAt == nil {
|
|
t.Error("Reject() rejectedAt should not be nil")
|
|
}
|
|
}
|
|
|
|
func TestArtifact_ApproveAfterReject(t *testing.T) {
|
|
art := NewArtifact(ArtifactSpec)
|
|
|
|
// First reject
|
|
art.Reject("reviewer@example.com")
|
|
if art.Status != StatusRejected {
|
|
t.Fatalf("expected rejected status after Reject()")
|
|
}
|
|
|
|
// Then approve
|
|
art.Approve("approver@example.com")
|
|
if art.Status != StatusApproved {
|
|
t.Errorf("Approve() after Reject() status = %v, want %v", art.Status, StatusApproved)
|
|
}
|
|
if art.RejectedBy != "" {
|
|
t.Errorf("Approve() should clear rejectedBy, got %v", art.RejectedBy)
|
|
}
|
|
if art.RejectedAt != nil {
|
|
t.Error("Approve() should clear rejectedAt")
|
|
}
|
|
}
|
|
|
|
func TestArtifact_MarkDraft(t *testing.T) {
|
|
art := NewArtifact(ArtifactSpec)
|
|
art.MarkDraft()
|
|
|
|
if art.Status != StatusDraft {
|
|
t.Errorf("MarkDraft() status = %v, want %v", art.Status, StatusDraft)
|
|
}
|
|
}
|
|
|
|
func TestArtifact_MarkPassed(t *testing.T) {
|
|
art := NewArtifact(ArtifactQAResults)
|
|
art.MarkPassed()
|
|
|
|
if art.Status != StatusPassed {
|
|
t.Errorf("MarkPassed() status = %v, want %v", art.Status, StatusPassed)
|
|
}
|
|
}
|
|
|
|
func TestArtifact_MarkFailed(t *testing.T) {
|
|
art := NewArtifact(ArtifactQAResults)
|
|
art.MarkFailed()
|
|
|
|
if art.Status != StatusFailed {
|
|
t.Errorf("MarkFailed() status = %v, want %v", art.Status, StatusFailed)
|
|
}
|
|
}
|
|
|
|
func TestArtifact_MarkNeedsFix(t *testing.T) {
|
|
art := NewArtifact(ArtifactReview)
|
|
art.MarkNeedsFix()
|
|
|
|
if art.Status != StatusNeedsFix {
|
|
t.Errorf("MarkNeedsFix() status = %v, want %v", art.Status, StatusNeedsFix)
|
|
}
|
|
}
|
|
|
|
func TestArtifact_ApprovedAtTimestamp(t *testing.T) {
|
|
art := NewArtifact(ArtifactSpec)
|
|
before := time.Now().UTC()
|
|
art.Approve("user@example.com")
|
|
after := time.Now().UTC()
|
|
|
|
if art.ApprovedAt.Before(before) || art.ApprovedAt.After(after) {
|
|
t.Errorf("ApprovedAt %v not in expected range [%v, %v]", art.ApprovedAt, before, after)
|
|
}
|
|
}
|
|
|
|
func TestArtifact_RejectedAtTimestamp(t *testing.T) {
|
|
art := NewArtifact(ArtifactSpec)
|
|
before := time.Now().UTC()
|
|
art.Reject("user@example.com")
|
|
after := time.Now().UTC()
|
|
|
|
if art.RejectedAt.Before(before) || art.RejectedAt.After(after) {
|
|
t.Errorf("RejectedAt %v not in expected range [%v, %v]", art.RejectedAt, before, after)
|
|
}
|
|
}
|