mirror of
https://github.com/Dvorinka/Devour.git
synced 2026-06-03 20:13:03 +00:00
updage
This commit is contained in:
@@ -0,0 +1,754 @@
|
||||
package quality
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestNewNarrativeGenerator(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
targetScore int
|
||||
expected int
|
||||
}{
|
||||
{"default target", 0, 95},
|
||||
{"custom target", 85, 85},
|
||||
{"negative target", -10, 95},
|
||||
{"zero target", 0, 95},
|
||||
{"high target", 100, 100},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
gen := NewNarrativeGenerator(tt.targetScore)
|
||||
if gen.targetScore != tt.expected {
|
||||
t.Errorf("NewNarrativeGenerator() targetScore = %v, want %v", gen.targetScore, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNarrativeGenerator_determinePhase(t *testing.T) {
|
||||
gen := NewNarrativeGenerator(95)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
findings []Finding
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "no open issues",
|
||||
findings: []Finding{{Status: StatusFixed}},
|
||||
expected: "maintenance",
|
||||
},
|
||||
{
|
||||
name: "critical phase with T4",
|
||||
findings: []Finding{
|
||||
{Status: StatusOpen, Severity: SeverityT4},
|
||||
},
|
||||
expected: "critical",
|
||||
},
|
||||
{
|
||||
name: "debt reduction with many T3",
|
||||
findings: []Finding{
|
||||
{Status: StatusOpen, Severity: SeverityT3},
|
||||
{Status: StatusOpen, Severity: SeverityT3},
|
||||
{Status: StatusOpen, Severity: SeverityT3},
|
||||
{Status: StatusOpen, Severity: SeverityT3},
|
||||
{Status: StatusOpen, Severity: SeverityT3},
|
||||
{Status: StatusOpen, Severity: SeverityT3},
|
||||
},
|
||||
expected: "debt_reduction",
|
||||
},
|
||||
{
|
||||
name: "debt reduction with many open issues",
|
||||
findings: func() []Finding {
|
||||
var f []Finding
|
||||
for i := 0; i < 25; i++ {
|
||||
f = append(f, Finding{Status: StatusOpen, Severity: SeverityT2})
|
||||
}
|
||||
return f
|
||||
}(),
|
||||
expected: "debt_reduction",
|
||||
},
|
||||
{
|
||||
name: "cleanup phase",
|
||||
findings: func() []Finding {
|
||||
var f []Finding
|
||||
for i := 0; i < 10; i++ {
|
||||
f = append(f, Finding{Status: StatusOpen, Severity: SeverityT2})
|
||||
}
|
||||
return f
|
||||
}(),
|
||||
expected: "cleanup",
|
||||
},
|
||||
{
|
||||
name: "polish phase",
|
||||
findings: []Finding{
|
||||
{Status: StatusOpen, Severity: SeverityT2},
|
||||
{Status: StatusOpen, Severity: SeverityT2},
|
||||
},
|
||||
expected: "polish",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
scorecard := &Scorecard{TotalScore: 100}
|
||||
phase := gen.determinePhase(tt.findings, scorecard)
|
||||
if phase != tt.expected {
|
||||
t.Errorf("determinePhase() = %v, want %v", phase, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNarrativeGenerator_generateHeadline(t *testing.T) {
|
||||
gen := NewNarrativeGenerator(95)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
phase string
|
||||
scorecard *Scorecard
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "maintenance phase",
|
||||
phase: "maintenance",
|
||||
scorecard: &Scorecard{StrictScore: 50},
|
||||
expected: "Codebase is healthy! Focus on preventing new debt.",
|
||||
},
|
||||
{
|
||||
name: "critical phase",
|
||||
phase: "critical",
|
||||
scorecard: &Scorecard{StrictScore: 150},
|
||||
expected: "Critical issues detected (150 strict score). Address T4 findings first.",
|
||||
},
|
||||
{
|
||||
name: "debt reduction phase",
|
||||
phase: "debt_reduction",
|
||||
scorecard: &Scorecard{TotalScore: 200},
|
||||
expected: "Significant technical debt (200 open issues). Systematic cleanup recommended.",
|
||||
},
|
||||
{
|
||||
name: "cleanup phase",
|
||||
phase: "cleanup",
|
||||
scorecard: &Scorecard{TotalScore: 15},
|
||||
expected: "Minor issues detected (15 open). Quick wins available.",
|
||||
},
|
||||
{
|
||||
name: "polish phase",
|
||||
phase: "polish",
|
||||
scorecard: &Scorecard{TotalScore: 3},
|
||||
expected: "Codebase in good shape (3 open issues).",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
headline := gen.generateHeadline(tt.phase, tt.scorecard)
|
||||
if headline != tt.expected {
|
||||
t.Errorf("generateHeadline() = %v, want %v", headline, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNarrativeGenerator_classifyDimension(t *testing.T) {
|
||||
gen := NewNarrativeGenerator(95)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
finding Finding
|
||||
expected Dimension
|
||||
}{
|
||||
{
|
||||
name: "complexity",
|
||||
finding: Finding{Type: "complexity"},
|
||||
expected: DimensionCodeQuality,
|
||||
},
|
||||
{
|
||||
name: "complexity_ast",
|
||||
finding: Finding{Type: "complexity_ast"},
|
||||
expected: DimensionCodeQuality,
|
||||
},
|
||||
{
|
||||
name: "duplication",
|
||||
finding: Finding{Type: "duplication"},
|
||||
expected: DimensionDuplication,
|
||||
},
|
||||
{
|
||||
name: "dead_code",
|
||||
finding: Finding{Type: "dead_code"},
|
||||
expected: DimensionFileHealth,
|
||||
},
|
||||
{
|
||||
name: "security",
|
||||
finding: Finding{Type: "security"},
|
||||
expected: DimensionSecurity,
|
||||
},
|
||||
{
|
||||
name: "naming",
|
||||
finding: Finding{Type: "naming"},
|
||||
expected: DimensionNamingQuality,
|
||||
},
|
||||
{
|
||||
name: "import_cycle",
|
||||
finding: Finding{Type: "import_cycle"},
|
||||
expected: DimensionAbstractionFit,
|
||||
},
|
||||
{
|
||||
name: "unknown type",
|
||||
finding: Finding{Type: "unknown"},
|
||||
expected: DimensionCodeQuality,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
dimension := gen.classifyDimension(tt.finding)
|
||||
if dimension != tt.expected {
|
||||
t.Errorf("classifyDimension() = %v, want %v", dimension, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNarrativeGenerator_generateActions(t *testing.T) {
|
||||
gen := NewNarrativeGenerator(95)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
findings []Finding
|
||||
phase string
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
name: "mixed severities",
|
||||
findings: []Finding{
|
||||
{Status: StatusOpen, Severity: SeverityT4},
|
||||
{Status: StatusOpen, Severity: SeverityT3},
|
||||
{Status: StatusOpen, Severity: SeverityT2},
|
||||
{Status: StatusOpen, Severity: SeverityT1},
|
||||
},
|
||||
phase: "critical",
|
||||
expected: []string{
|
||||
"Address 1 T4 (major refactor) issues - these require architectural changes",
|
||||
"Review 1 T3 (needs judgment) issues - decide if they need fixing",
|
||||
"Run auto-fixer for 1 T1 (auto-fixable) issues",
|
||||
"Quick manual fixes available for 1 T2 issues",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no open issues",
|
||||
findings: []Finding{{Status: StatusFixed}},
|
||||
phase: "maintenance",
|
||||
expected: []string{"No immediate actions required - maintain code quality"},
|
||||
},
|
||||
{
|
||||
name: "only T1 issues",
|
||||
findings: []Finding{
|
||||
{Status: StatusOpen, Severity: SeverityT1},
|
||||
{Status: StatusOpen, Severity: SeverityT1},
|
||||
},
|
||||
phase: "polish",
|
||||
expected: []string{
|
||||
"Run auto-fixer for 2 T1 (auto-fixable) issues",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
actions := gen.generateActions(tt.findings, tt.phase)
|
||||
if len(actions) != len(tt.expected) {
|
||||
t.Errorf("generateActions() length = %v, want %v", len(actions), len(tt.expected))
|
||||
}
|
||||
for i, action := range actions {
|
||||
if i < len(tt.expected) && action != tt.expected[i] {
|
||||
t.Errorf("generateActions()[%d] = %v, want %v", i, action, tt.expected[i])
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNarrativeGenerator_generateStrategy(t *testing.T) {
|
||||
gen := NewNarrativeGenerator(95)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
findings []Finding
|
||||
expected string
|
||||
parallel bool
|
||||
}{
|
||||
{
|
||||
name: "high auto-fixable coverage",
|
||||
findings: []Finding{
|
||||
{Status: StatusOpen, Severity: SeverityT1},
|
||||
{Status: StatusOpen, Severity: SeverityT1},
|
||||
{Status: StatusOpen, Severity: SeverityT2},
|
||||
},
|
||||
expected: "Use auto-fixers first, then address remaining issues manually",
|
||||
parallel: false,
|
||||
},
|
||||
{
|
||||
name: "some auto-fixable",
|
||||
findings: []Finding{
|
||||
{Status: StatusOpen, Severity: SeverityT1},
|
||||
{Status: StatusOpen, Severity: SeverityT3},
|
||||
{Status: StatusOpen, Severity: SeverityT4},
|
||||
},
|
||||
expected: "Start with auto-fixers for quick wins, then prioritize by impact",
|
||||
parallel: false,
|
||||
},
|
||||
{
|
||||
name: "no auto-fixable",
|
||||
findings: []Finding{
|
||||
{Status: StatusOpen, Severity: SeverityT3},
|
||||
{Status: StatusOpen, Severity: SeverityT4},
|
||||
},
|
||||
expected: "Prioritize by severity and impact, starting with T4 issues",
|
||||
parallel: false,
|
||||
},
|
||||
{
|
||||
name: "no findings",
|
||||
findings: []Finding{},
|
||||
expected: "Prioritize by severity and impact, starting with T4 issues",
|
||||
parallel: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
dimensions := &NarrativeDimensions{}
|
||||
strategy := gen.generateStrategy(tt.findings, dimensions)
|
||||
|
||||
if strategy.FixerLeverage.Recommendation != tt.expected {
|
||||
t.Errorf("generateStrategy() recommendation = %v, want %v", strategy.FixerLeverage.Recommendation, tt.expected)
|
||||
}
|
||||
|
||||
if strategy.CanParallelize != tt.parallel {
|
||||
t.Errorf("generateStrategy() CanParallelize = %v, want %v", strategy.CanParallelize, tt.parallel)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNarrativeGenerator_generateHint(t *testing.T) {
|
||||
gen := NewNarrativeGenerator(95)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
findings []Finding
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "has T1 issues",
|
||||
findings: []Finding{
|
||||
{Status: StatusOpen, Severity: SeverityT1},
|
||||
{Status: StatusOpen, Severity: SeverityT2},
|
||||
},
|
||||
expected: "T1 issues can be auto-fixed with 'devour quality fix'",
|
||||
},
|
||||
{
|
||||
name: "has T4 issues but no T1",
|
||||
findings: []Finding{
|
||||
{Status: StatusOpen, Severity: SeverityT4},
|
||||
{Status: StatusOpen, Severity: SeverityT3},
|
||||
},
|
||||
expected: "T4 issues require planning - consider creating a dedicated branch",
|
||||
},
|
||||
{
|
||||
name: "no T1 or T4 issues",
|
||||
findings: []Finding{
|
||||
{Status: StatusOpen, Severity: SeverityT2},
|
||||
{Status: StatusOpen, Severity: SeverityT3},
|
||||
},
|
||||
expected: "Focus on one category at a time for best results",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
hint := gen.generateHint(tt.findings)
|
||||
if hint != tt.expected {
|
||||
t.Errorf("generateHint() = %v, want %v", hint, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNarrativeGenerator_generateTools(t *testing.T) {
|
||||
gen := NewNarrativeGenerator(95)
|
||||
|
||||
findings := []Finding{
|
||||
{Status: StatusOpen, Severity: SeverityT1, Type: "dead_code"},
|
||||
{Status: StatusOpen, Severity: SeverityT2, Type: "naming"},
|
||||
}
|
||||
|
||||
tools := gen.generateTools(findings)
|
||||
|
||||
if tools.Plan.Command != "devour quality plan" {
|
||||
t.Errorf("generateTools() Plan.Command = %v, want %v", tools.Plan.Command, "devour quality plan")
|
||||
}
|
||||
|
||||
if !tools.Badge.Generated {
|
||||
t.Error("generateTools() Badge.Generated should be true")
|
||||
}
|
||||
|
||||
if len(tools.Fixers) != 1 {
|
||||
t.Errorf("generateTools() Fixers length = %v, want 1", len(tools.Fixers))
|
||||
}
|
||||
}
|
||||
|
||||
func TestNarrativeGenerator_analyzeDebt(t *testing.T) {
|
||||
gen := NewNarrativeGenerator(95)
|
||||
|
||||
findings := []Finding{
|
||||
{Status: StatusOpen, Severity: SeverityT4, Type: "security", Score: 10},
|
||||
{Status: StatusWontfix, Severity: SeverityT2, Type: "naming", Score: 5},
|
||||
{Status: StatusOpen, Severity: SeverityT3, Type: "complexity", Score: 8},
|
||||
}
|
||||
|
||||
scorecard := &Scorecard{StrictScore: 150}
|
||||
|
||||
debt := gen.analyzeDebt(findings, scorecard)
|
||||
|
||||
if debt.WontfixCount != 1 {
|
||||
t.Errorf("analyzeDebt() WontfixCount = %v, want 1", debt.WontfixCount)
|
||||
}
|
||||
|
||||
if debt.OverallGap != 150.0 {
|
||||
t.Errorf("analyzeDebt() OverallGap = %v, want 150.0", debt.OverallGap)
|
||||
}
|
||||
|
||||
if debt.WorstDimension != "Security" {
|
||||
t.Errorf("analyzeDebt() WorstDimension = %v, want Security", debt.WorstDimension)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNarrativeGenerator_calculateStrictTarget(t *testing.T) {
|
||||
gen := NewNarrativeGenerator(100)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
scorecard *Scorecard
|
||||
expected string
|
||||
hasWarning bool
|
||||
}{
|
||||
{
|
||||
name: "at target",
|
||||
scorecard: &Scorecard{StrictScore: 100},
|
||||
expected: "at_target",
|
||||
hasWarning: false,
|
||||
},
|
||||
{
|
||||
name: "near target",
|
||||
scorecard: &Scorecard{StrictScore: 85},
|
||||
expected: "near_target",
|
||||
hasWarning: false,
|
||||
},
|
||||
{
|
||||
name: "in progress",
|
||||
scorecard: &Scorecard{StrictScore: 60},
|
||||
expected: "in_progress",
|
||||
hasWarning: true,
|
||||
},
|
||||
{
|
||||
name: "needs work",
|
||||
scorecard: &Scorecard{StrictScore: 30},
|
||||
expected: "needs_work",
|
||||
hasWarning: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
target := gen.calculateStrictTarget(tt.scorecard)
|
||||
|
||||
if target.State != tt.expected {
|
||||
t.Errorf("calculateStrictTarget() State = %v, want %v", target.State, tt.expected)
|
||||
}
|
||||
|
||||
if (target.Warning != nil) != tt.hasWarning {
|
||||
t.Errorf("calculateStrictTarget() Warning presence = %v, want %v", target.Warning != nil, tt.hasWarning)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNarrativeGenerator_generateReminders(t *testing.T) {
|
||||
gen := NewNarrativeGenerator(95)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
findings []Finding
|
||||
history []StateSnapshot
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
name: "auto-fixable available",
|
||||
findings: []Finding{
|
||||
{Status: StatusOpen, Severity: SeverityT1},
|
||||
{Status: StatusOpen, Severity: SeverityT1},
|
||||
},
|
||||
history: []StateSnapshot{},
|
||||
expected: []string{
|
||||
"2 auto-fixable issues available - use 'devour quality fix'",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no progress",
|
||||
findings: []Finding{{Status: StatusOpen, Severity: SeverityT2}},
|
||||
history: []StateSnapshot{{Findings: 1, Timestamp: time.Now()}},
|
||||
expected: []string{
|
||||
"No progress since last scan - consider tackling a specific category",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no reminders",
|
||||
findings: []Finding{{Status: StatusOpen, Severity: SeverityT3}},
|
||||
history: []StateSnapshot{},
|
||||
expected: []string{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
reminders := gen.generateReminders(tt.findings, tt.history)
|
||||
if len(reminders) != len(tt.expected) {
|
||||
t.Errorf("generateReminders() length = %v, want %v", len(reminders), len(tt.expected))
|
||||
}
|
||||
for i, reminder := range reminders {
|
||||
if i < len(tt.expected) && reminder != tt.expected[i] {
|
||||
t.Errorf("generateReminders()[%d] = %v, want %v", i, reminder, tt.expected[i])
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNarrativeGenerator_identifyRisks(t *testing.T) {
|
||||
gen := NewNarrativeGenerator(95)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
findings []Finding
|
||||
history []StateSnapshot
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
name: "high T4 count",
|
||||
findings: func() []Finding {
|
||||
var f []Finding
|
||||
for i := 0; i < 5; i++ {
|
||||
f = append(f, Finding{Status: StatusOpen, Severity: SeverityT4})
|
||||
}
|
||||
return f
|
||||
}(),
|
||||
history: []StateSnapshot{},
|
||||
expected: []string{
|
||||
"High number of T4 issues (5) indicates architectural debt",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "upward trend",
|
||||
findings: func() []Finding {
|
||||
var f []Finding
|
||||
for i := 0; i < 25; i++ {
|
||||
f = append(f, Finding{Status: StatusOpen, Severity: SeverityT2})
|
||||
}
|
||||
return f
|
||||
}(),
|
||||
history: []StateSnapshot{
|
||||
{Findings: 10, Timestamp: time.Now().Add(-3 * time.Hour)},
|
||||
{Findings: 12, Timestamp: time.Now().Add(-2 * time.Hour)},
|
||||
{Findings: 15, Timestamp: time.Now().Add(-1 * time.Hour)},
|
||||
},
|
||||
expected: []string{
|
||||
"Finding count is trending upward - debt is accumulating",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no risks",
|
||||
findings: []Finding{{Status: StatusOpen, Severity: SeverityT2}},
|
||||
history: []StateSnapshot{},
|
||||
expected: []string{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
risks := gen.identifyRisks(tt.findings, tt.history)
|
||||
if len(risks) != len(tt.expected) {
|
||||
t.Errorf("identifyRisks() length = %v, want %v", len(risks), len(tt.expected))
|
||||
}
|
||||
for i, risk := range risks {
|
||||
if i < len(tt.expected) && risk != tt.expected[i] {
|
||||
t.Errorf("identifyRisks()[%d] = %v, want %v", i, risk, tt.expected[i])
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNarrativeGenerator_generateMilestone(t *testing.T) {
|
||||
gen := NewNarrativeGenerator(95)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
phase string
|
||||
scorecard *Scorecard
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "maintenance",
|
||||
phase: "maintenance",
|
||||
scorecard: &Scorecard{},
|
||||
expected: "Maintain current quality level",
|
||||
},
|
||||
{
|
||||
name: "critical",
|
||||
phase: "critical",
|
||||
scorecard: &Scorecard{},
|
||||
expected: "Reduce T4 issues to zero",
|
||||
},
|
||||
{
|
||||
name: "debt reduction",
|
||||
phase: "debt_reduction",
|
||||
scorecard: &Scorecard{},
|
||||
expected: "Reduce strict score below 95",
|
||||
},
|
||||
{
|
||||
name: "cleanup",
|
||||
phase: "cleanup",
|
||||
scorecard: &Scorecard{},
|
||||
expected: "Clear all T1 and T2 issues",
|
||||
},
|
||||
{
|
||||
name: "polish",
|
||||
phase: "polish",
|
||||
scorecard: &Scorecard{},
|
||||
expected: "Continue quality improvement",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
milestone := gen.generateMilestone(tt.phase, tt.scorecard)
|
||||
if milestone != tt.expected {
|
||||
t.Errorf("generateMilestone() = %v, want %v", milestone, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNarrativeGenerator_explainWhyNow(t *testing.T) {
|
||||
gen := NewNarrativeGenerator(95)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
phase string
|
||||
findings []Finding
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "has T4 issues",
|
||||
findings: []Finding{
|
||||
{Status: StatusOpen, Severity: SeverityT4},
|
||||
},
|
||||
expected: "T4 issues compound over time - addressing them early prevents architectural decay",
|
||||
},
|
||||
{
|
||||
name: "many T1 issues",
|
||||
findings: func() []Finding {
|
||||
var f []Finding
|
||||
for i := 0; i < 6; i++ {
|
||||
f = append(f, Finding{Status: StatusOpen, Severity: SeverityT1})
|
||||
}
|
||||
return f
|
||||
}(),
|
||||
expected: "Quick wins available - auto-fixers can clear low-hanging fruit in minutes",
|
||||
},
|
||||
{
|
||||
name: "few T1 issues",
|
||||
findings: []Finding{
|
||||
{Status: StatusOpen, Severity: SeverityT1},
|
||||
{Status: StatusOpen, Severity: SeverityT2},
|
||||
},
|
||||
expected: "Consistent small improvements compound into significant quality gains",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
whyNow := gen.explainWhyNow(tt.phase, tt.findings)
|
||||
if whyNow != tt.expected {
|
||||
t.Errorf("explainWhyNow() = %v, want %v", whyNow, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNarrativeGenerator_Generate(t *testing.T) {
|
||||
gen := NewNarrativeGenerator(95)
|
||||
|
||||
findings := []Finding{
|
||||
{Status: StatusOpen, Severity: SeverityT2, Type: "naming", Score: 5},
|
||||
{Status: StatusOpen, Severity: SeverityT1, Type: "dead_code", Score: 3},
|
||||
}
|
||||
|
||||
scorecard := &Scorecard{
|
||||
TotalScore: 8,
|
||||
StrictScore: 15,
|
||||
TargetScore: 95,
|
||||
LastScan: time.Now(),
|
||||
}
|
||||
|
||||
history := []StateSnapshot{
|
||||
{Findings: 10, Timestamp: time.Now().Add(-1 * time.Hour)},
|
||||
}
|
||||
|
||||
narrative := gen.Generate(findings, scorecard, history)
|
||||
|
||||
if narrative.Phase == "" {
|
||||
t.Error("Generate() Phase should not be empty")
|
||||
}
|
||||
|
||||
if narrative.Headline == "" {
|
||||
t.Error("Generate() Headline should not be empty")
|
||||
}
|
||||
|
||||
if narrative.Dimensions == nil {
|
||||
t.Error("Generate() Dimensions should not be nil")
|
||||
}
|
||||
|
||||
if len(narrative.Actions) == 0 {
|
||||
t.Error("Generate() Actions should not be empty")
|
||||
}
|
||||
|
||||
if narrative.Strategy == nil {
|
||||
t.Error("Generate() Strategy should not be nil")
|
||||
}
|
||||
|
||||
if narrative.Tools == nil {
|
||||
t.Error("Generate() Tools should not be nil")
|
||||
}
|
||||
|
||||
if narrative.Debt == nil {
|
||||
t.Error("Generate() Debt should not be nil")
|
||||
}
|
||||
|
||||
if narrative.Milestone == "" {
|
||||
t.Error("Generate() Milestone should not be empty")
|
||||
}
|
||||
|
||||
if narrative.WhyNow == "" {
|
||||
t.Error("Generate() WhyNow should not be empty")
|
||||
}
|
||||
|
||||
if narrative.StrictTarget == nil {
|
||||
t.Error("Generate() StrictTarget should not be nil")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user