okami.mom/albedo

git clone https://git.nkp.pet/okami.mom/albedo

dawntui: clear stopping state when session becomes idlebb7b485

main
3.3 KiB131 linesraw
1package tui
2
3import (
4	"strings"
5	"testing"
6	"time"
7
8	"albedo/cli/internal/daemon"
9	tea "charm.land/bubbletea/v2"
10)
11
12func TestEscapeStoppingCompactionReconcilesWhenIdle(t *testing.T) {
13	m := newTestChatModel(t, &daemon.Session{ID: "s"})
14	m.SetSize(80, 30)
15
16	phaseCompacting := daemon.PhaseCompacting
17	m.Status.Running, m.Status.Idle, m.Status.Phase = true, false, &phaseCompacting
18
19	// Press esc to stop compaction
20	var cmd tea.Cmd
21	m, cmd = m.Update(tea.KeyPressMsg{Code: tea.KeyEscape})
22	if cmd == nil {
23		t.Fatal("expected interrupt command on esc")
24	}
25	if !m.Stopping {
26		t.Fatal("expected Stopping to be true after pressing esc")
27	}
28	if !strings.Contains(m.statusLine(), "stopping…") {
29		t.Fatalf("expected statusLine to show stopping…, got %q", m.statusLine())
30	}
31
32	// Daemon finishes interrupting compaction and emits status idle
33	phaseIdle := daemon.PhaseIdle
34	m.handleStreamEvent(daemon.StreamEvent{
35		Type: "status",
36		Status: &daemon.AgentStatus{
37			Running: false,
38			Idle:    true,
39			Phase:   &phaseIdle,
40		},
41	})
42
43	if m.Stopping {
44		t.Fatal("expected Stopping to be false after status became idle")
45	}
46	if !m.Stopped {
47		t.Fatal("expected Stopped to be true after stopping was reconciled")
48	}
49	if strings.Contains(m.statusLine(), "stopping…") {
50		t.Fatalf("statusLine still shows stopping… after idle: %q", m.statusLine())
51	}
52	if m.animating() {
53		t.Fatal("expected animating to be false after stopping completed and session is idle")
54	}
55}
56
57func TestEscapeStoppingPendingUsersReconcilesWhenSettled(t *testing.T) {
58	m := newTestChatModel(t, &daemon.Session{ID: "s"})
59	m.SetSize(80, 30)
60	m.Status.Running, m.Status.Idle = false, true
61	m.pendingUsers = []PendingUserTurn{
62		{OperationID: "op-1", Text: "queued message", At: time.Now().UnixMilli()},
63	}
64
65	// Press esc to cancel queued input
66	m, _ = m.Update(tea.KeyPressMsg{Code: tea.KeyEscape})
67	if !m.Stopping {
68		t.Fatal("expected Stopping to be true after pressing esc with pending users")
69	}
70
71	// Status event arrives while input is still pending: Stopping remains true
72	phaseIdle := daemon.PhaseIdle
73	m.handleStreamEvent(daemon.StreamEvent{
74		Type: "status",
75		Status: &daemon.AgentStatus{
76			Running: false,
77			Idle:    true,
78			Phase:   &phaseIdle,
79		},
80	})
81	if !m.Stopping {
82		t.Fatal("expected Stopping to stay true while pendingUsers still contains items")
83	}
84
85	// Receipt settles the input turn
86	cancelled := "cancelled"
87	m.handleStreamEvent(daemon.StreamEvent{
88		Type: "input",
89		Receipt: &daemon.InputReceipt{
90			ID:       "op-1",
91			Delivery: &cancelled,
92		},
93	})
94
95	if m.Stopping {
96		t.Fatal("expected Stopping to be false once pending input is settled")
97	}
98	if !m.Stopped {
99		t.Fatal("expected Stopped to be true after pending input is settled")
100	}
101	if m.animating() {
102		t.Fatal("expected animating to be false")
103	}
104}
105
106func TestChatStatusPollReconcilesStopping(t *testing.T) {
107	m := newTestChatModel(t, &daemon.Session{ID: "s"})
108	m.SetSize(80, 30)
109	m.Stopping = true
110	m.Status.Running, m.Status.Idle = true, false
111
112	phaseIdle := daemon.PhaseIdle
113	status := daemon.AgentStatus{
114		Running: false,
115		Idle:    true,
116		Phase:   &phaseIdle,
117	}
118	m, _ = m.Update(ChatStatusMsg{
119		SessionID:  m.SessionID,
120		Generation: m.Generation,
121		Revision:   m.statusRevision,
122		Status:     &status,
123	})
124
125	if m.Stopping {
126		t.Fatal("expected Stopping to be false after ChatStatusMsg reported idle")
127	}
128	if !m.Stopped {
129		t.Fatal("expected Stopped to be true")
130	}
131}