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