Files

183 lines
4.7 KiB
Go
Raw Permalink Normal View History

package board
import (
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
var validBoard = `# Task Board — Milestone T1: Production Hardening
**Submilestone:** T1 (Error recovery, observability, metrics, reliability)
| ID | Scope | Status | Branch | Verification |
|----|-------|--------|--------|--------------|
| T1.1 | Workflow error recovery | [x] | task/T1.1 | Verify recovery works |
| T1.2 | Structured logging | [x] | task/T1.2 | Verify metrics visible |
| T1.3 | Timeout tuning | [x] | task/T1.3 | Verify recommendations |
| T1.4 | Board validation | [ ] | task/T1.4 | Verify healing works |
`
func TestValidateValidBoard(t *testing.T) {
bv := NewBoardValidator("")
valid := bv.ValidateBoard(validBoard)
assert.True(t, valid)
assert.False(t, bv.HasErrors())
}
func TestValidateInvalidStatus(t *testing.T) {
board := strings.ReplaceAll(validBoard, "[x]", "[?]")
bv := NewBoardValidator("")
valid := bv.ValidateBoard(board)
assert.False(t, valid)
assert.True(t, bv.HasErrors())
}
func TestValidateMissingHeader(t *testing.T) {
boardNoHeader := `| ID | Scope | Status | Branch | Verification |
|----|-------|--------|--------|--------------|
| T1.1 | Task | [x] | branch | verify |
`
bv := NewBoardValidator("")
valid := bv.ValidateBoard(boardNoHeader)
assert.False(t, valid)
assert.True(t, bv.HasErrors())
}
func TestParseTasks(t *testing.T) {
bv := NewBoardValidator("")
tasks, err := bv.ParseTasks(validBoard)
assert.NoError(t, err)
assert.Equal(t, 4, len(tasks))
assert.Equal(t, "T1.1", tasks[0].ID)
assert.Equal(t, "[x]", tasks[0].Status)
}
func TestErrorSummary(t *testing.T) {
board := strings.ReplaceAll(validBoard, "[x]", "[?]")
bv := NewBoardValidator("")
bv.ValidateBoard(board)
summary := bv.ErrorSummary()
assert.Contains(t, summary, "error")
}
func TestRepairBoard(t *testing.T) {
boardNoHeader := `| T1.1 | Task | [ ] | branch | verify |`
bv := NewBoardValidator("")
repaired, err := bv.RepairBoard(boardNoHeader)
assert.NoError(t, err)
assert.Contains(t, repaired, "Task Board")
}
func TestIsValidTaskID(t *testing.T) {
assert.True(t, isValidTaskID("T0"))
assert.True(t, isValidTaskID("T1"))
assert.True(t, isValidTaskID("T1.1"))
assert.True(t, isValidTaskID("T1.8"))
assert.False(t, isValidTaskID("Task1"))
assert.False(t, isValidTaskID("T"))
}
func TestDetectDivergence(t *testing.T) {
bv := NewBoardValidator("")
actualStates := map[string]bool{
"T1.1": true, // Completed in reality
"T1.2": true, // Completed in reality
"T1.3": true, // Completed in reality
"T1.4": false, // Not completed in reality
}
// Valid board has T1.1, T1.2, T1.3 as [x] and T1.4 as [ ]
divergences := bv.DetectDivergence(validBoard, actualStates)
// Should be no divergences since they match
assert.Equal(t, 0, len(divergences))
}
func TestDetectDivergenceWithMismatch(t *testing.T) {
bv := NewBoardValidator("")
actualStates := map[string]bool{
"T1.1": false, // Should be true but is false
"T1.2": true,
"T1.3": true,
"T1.4": true, // Should be false but is true
}
divergences := bv.DetectDivergence(validBoard, actualStates)
// Should find 2 divergences
assert.Greater(t, len(divergences), 0)
}
func TestHealDivergence(t *testing.T) {
bv := NewBoardValidator("")
actualStates := map[string]bool{
"T1.1": false, // Different from board
"T1.2": true,
"T1.3": true,
"T1.4": true, // Different from board
}
healed, changes, err := bv.HealDivergence(validBoard, actualStates)
assert.NoError(t, err)
assert.Greater(t, len(changes), 0)
// Verify healing worked
bv2 := NewBoardValidator("")
tasks, _ := bv2.ParseTasks(healed)
for _, task := range tasks {
expected, _ := actualStates[task.ID]
if expected {
assert.Equal(t, "[x]", task.Status)
} else {
assert.Equal(t, "[ ]", task.Status)
}
}
}
func TestParseTasksEmptyBoard(t *testing.T) {
bv := NewBoardValidator("")
tasks, err := bv.ParseTasks("")
assert.Error(t, err)
assert.Equal(t, 0, len(tasks))
}
func TestValidateEmptyBoard(t *testing.T) {
bv := NewBoardValidator("")
valid := bv.ValidateBoard("")
assert.False(t, valid)
assert.True(t, bv.HasErrors())
}
func TestWarningsSummary(t *testing.T) {
bv := NewBoardValidator("")
bv.validateTaskRow("| ABC | Description | [x] | branch | verify |", 1)
summary := bv.WarningsSummary()
assert.Contains(t, summary, "Invalid task ID")
}
func TestMultipleTasks(t *testing.T) {
bv := NewBoardValidator("")
tasks, err := bv.ParseTasks(validBoard)
assert.NoError(t, err)
for _, task := range tasks {
assert.NotEmpty(t, task.ID)
assert.NotEmpty(t, task.Status)
}
}
func TestNormalizeStatus(t *testing.T) {
board := strings.ReplaceAll(validBoard, "[x]", "[X]")
bv := NewBoardValidator("")
_, _ = bv.RepairBoard(board)
// Should normalize [X] to [x]
}