resident_subagent_indicator_test.go RawHistoryFolder
1package modes
2
3import (
4 "strings"
5 "testing"
6 "time"
7
8 "github.com/bnema/zut/packages/agent/subagents"
9 "github.com/bnema/zut/packages/tui"
10)
11
12func TestRenderResidentSubagentActivityLinesShowsOnlyActiveChildren(t *testing.T) {
13 now := time.Date(2026, time.August, 13, 12, 0, 0, 0, time.UTC)
14 lines := renderResidentSubagentActivityLines(tui.Dark, "/", []subagents.ResidentSnapshot{
15 {ID: "running-id", Profile: "reviewer", State: subagents.ResidentRunning, TurnStartedAt: now.Add(-90 * time.Second), ActivityUpdatedAt: now, WaitingForModel: true},
16 {ID: "queued-id", Profile: "planner", State: subagents.ResidentQueued, TurnStartedAt: now.Add(-10 * time.Second), ActivityUpdatedAt: now.Add(-5 * time.Second)},
17 {ID: "idle-id", Profile: "finished", State: subagents.ResidentIdle},
18 }, 80, now)
19 got := strings.Join(plainResidentIndicatorLines(lines), "\n")
20 if !strings.Contains(got, "/ reviewer · Zzzz · activity 0s ago · 1m30s") || !strings.Contains(got, "… planner · queued · activity 5s ago · 10s") {
21 t.Fatalf("activity lines = %q", got)
22 }
23 if strings.Contains(got, "finished") {
24 t.Fatalf("activity lines include inactive child: %q", got)
25 }
26}
27
28func TestRenderResidentSubagentActivityLinesAnimatesModelWait(t *testing.T) {
29 started := time.Date(2026, time.August, 13, 12, 0, 0, 0, time.UTC)
30 now := started.Add(2 * time.Second)
31 lines := renderResidentSubagentActivityLines(tui.Dark, "/", []subagents.ResidentSnapshot{{
32 ID: "running-id", Profile: "reviewer", State: subagents.ResidentRunning,
33 TurnStartedAt: started, ActivityUpdatedAt: started, WaitingForModel: true,
34 }}, 80, now)
35 got := strings.Join(plainResidentIndicatorLines(lines), "\n")
36 if !strings.Contains(got, "zzZz") {
37 t.Fatalf("model-wait indicator = %q, want animated waiting frame", got)
38 }
39}
40
41func TestRenderResidentSubagentActivityLinesShowsRunningDuringToolWork(t *testing.T) {
42 now := time.Date(2026, time.August, 13, 12, 0, 2, 0, time.UTC)
43 lines := renderResidentSubagentActivityLines(tui.Dark, "/", []subagents.ResidentSnapshot{{
44 ID: "running-id", Profile: "reviewer", State: subagents.ResidentRunning,
45 TurnStartedAt: now.Add(-time.Minute), ActivityUpdatedAt: now.Add(-time.Second),
46 }}, 80, now)
47 got := strings.Join(plainResidentIndicatorLines(lines), "\n")
48 if !strings.Contains(got, "/ reviewer · running · activity 1s ago · 1m00s") || strings.Contains(got, "Zzzz") {
49 t.Fatalf("tool-work indicator = %q", got)
50 }
51}
52
53func TestLimitResidentSubagentActivityLinesSummarizesOverflow(t *testing.T) {
54 got := plainResidentIndicatorLines(limitResidentSubagentActivityLines(tui.Dark, []string{"one", "two", "three"}, 0, 2, 80))
55 want := []string{"one", " … 2 more active subagents"}
56 if strings.Join(got, "\n") != strings.Join(want, "\n") {
57 t.Fatalf("limited lines = %#v, want %#v", got, want)
58 }
59}
60
61func TestLimitResidentSubagentActivityLinesIncludesAlreadyHiddenChildren(t *testing.T) {
62 got := plainResidentIndicatorLines(limitResidentSubagentActivityLines(tui.Dark, []string{"one", "two", "three"}, 4, 2, 80))
63 want := []string{"one", " … 6 more active subagents"}
64 if strings.Join(got, "\n") != strings.Join(want, "\n") {
65 t.Fatalf("limited lines = %#v, want %#v", got, want)
66 }
67}
68
69func plainResidentIndicatorLines(lines []string) []string {
70 plain := make([]string, len(lines))
71 for index, line := range lines {
72 plain[index] = stripANSIBytes(line)
73 }
74 return plain
75}