cli/pkg/cli/tracker_test.go
Claude 6a8bd92189
Some checks failed
Deploy / build (push) Failing after 4s
Security Scan / security (push) Successful in 15s
feat: add pkg/cli with TUI components (#14, #15)
Move pkg/cli from core/go to core/cli. Includes Frame AppShell,
Stream, TaskTracker, Tree, Rich Table. Update imports to v0.0.1
tagged deps and fix openpgp import path for go-crypt split.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 20:42:00 +00:00

188 lines
3.9 KiB
Go

package cli
import (
"bytes"
"sync"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestTaskTracker_Good(t *testing.T) {
t.Run("add and complete tasks", func(t *testing.T) {
tr := NewTaskTracker()
tr.out = &bytes.Buffer{} // non-TTY
t1 := tr.Add("repo-a")
t2 := tr.Add("repo-b")
t1.Update("pulling...")
t2.Update("pulling...")
t1.Done("up to date")
t2.Done("3 commits behind")
out := tr.String()
assert.Contains(t, out, "repo-a")
assert.Contains(t, out, "repo-b")
assert.Contains(t, out, "up to date")
assert.Contains(t, out, "3 commits behind")
})
t.Run("task states", func(t *testing.T) {
tr := NewTaskTracker()
tr.out = &bytes.Buffer{}
task := tr.Add("test")
// Pending
_, _, state := task.snapshot()
assert.Equal(t, taskPending, state)
// Running
task.Update("working")
_, status, state := task.snapshot()
assert.Equal(t, taskRunning, state)
assert.Equal(t, "working", status)
// Done
task.Done("finished")
_, status, state = task.snapshot()
assert.Equal(t, taskDone, state)
assert.Equal(t, "finished", status)
})
t.Run("task fail", func(t *testing.T) {
tr := NewTaskTracker()
tr.out = &bytes.Buffer{}
task := tr.Add("bad-repo")
task.Fail("connection refused")
_, status, state := task.snapshot()
assert.Equal(t, taskFailed, state)
assert.Equal(t, "connection refused", status)
})
t.Run("concurrent updates", func(t *testing.T) {
tr := NewTaskTracker()
tr.out = &bytes.Buffer{}
var wg sync.WaitGroup
for i := range 10 {
task := tr.Add("task-" + string(rune('a'+i)))
wg.Add(1)
go func(t *TrackedTask) {
defer wg.Done()
t.Update("running")
time.Sleep(5 * time.Millisecond)
t.Done("ok")
}(task)
}
wg.Wait()
assert.True(t, tr.allDone())
})
t.Run("summary all passed", func(t *testing.T) {
tr := NewTaskTracker()
tr.out = &bytes.Buffer{}
tr.Add("a").Done("ok")
tr.Add("b").Done("ok")
tr.Add("c").Done("ok")
assert.Equal(t, "3/3 passed", tr.Summary())
})
t.Run("summary with failures", func(t *testing.T) {
tr := NewTaskTracker()
tr.out = &bytes.Buffer{}
tr.Add("a").Done("ok")
tr.Add("b").Fail("error")
tr.Add("c").Done("ok")
assert.Equal(t, "2/3 passed, 1 failed", tr.Summary())
})
t.Run("wait completes for non-TTY", func(t *testing.T) {
var buf bytes.Buffer
tr := NewTaskTracker()
tr.out = &buf
task := tr.Add("quick")
go func() {
time.Sleep(10 * time.Millisecond)
task.Done("done")
}()
tr.Wait()
assert.Contains(t, buf.String(), "quick")
assert.Contains(t, buf.String(), "done")
})
t.Run("name width alignment", func(t *testing.T) {
tr := NewTaskTracker()
tr.out = &bytes.Buffer{}
tr.Add("short")
tr.Add("very-long-repo-name")
w := tr.nameWidth()
assert.Equal(t, 19, w)
})
t.Run("String output format", func(t *testing.T) {
tr := NewTaskTracker()
tr.out = &bytes.Buffer{}
tr.Add("repo-a").Done("clean")
tr.Add("repo-b").Fail("dirty")
tr.Add("repo-c").Update("pulling")
out := tr.String()
assert.Contains(t, out, "✓")
assert.Contains(t, out, "✗")
assert.Contains(t, out, "⠋")
})
}
func TestTaskTracker_Bad(t *testing.T) {
t.Run("allDone with no tasks", func(t *testing.T) {
tr := NewTaskTracker()
tr.out = &bytes.Buffer{}
assert.True(t, tr.allDone())
})
t.Run("allDone incomplete", func(t *testing.T) {
tr := NewTaskTracker()
tr.out = &bytes.Buffer{}
tr.Add("pending")
assert.False(t, tr.allDone())
})
}
func TestTrackedTask_Good(t *testing.T) {
t.Run("thread safety", func(t *testing.T) {
tr := NewTaskTracker()
tr.out = &bytes.Buffer{}
task := tr.Add("concurrent")
var wg sync.WaitGroup
for range 100 {
wg.Add(1)
go func() {
defer wg.Done()
task.Update("running")
}()
}
wg.Wait()
_, status, state := task.snapshot()
require.Equal(t, taskRunning, state)
require.Equal(t, "running", status)
})
}