v0.2.32 9ee726b

tool_gate_test.go RawHistoryFolder

1package modes
2
3import "testing"
4
5func newGateTestInteractive() *Interactive {
6	return &Interactive{
7		toolGate: map[string]int{},
8		dirty:    make(chan struct{}, 1),
9	}
10}
11
12// While paced text is still draining, a tool that arrives mid-stream
13// must stay gated until the streaming buffer reaches the position
14// captured when the tool started.
15func TestToolGateHoldsToolUntilTextDrains(t *testing.T) {
16	i := newGateTestInteractive()
17
18	// Simulate a turn that streamed 10 runes already with 20 more
19	// queued in the pacer when the tool call arrives.
20	i.streamOn = true
21	i.streaming.WriteString("0123456789") // 10 painted
22	i.streamPending = []rune("01234567890123456789")
23
24	i.gateToolLocked("t1")
25	if got := i.toolGate["t1"]; got != 30 {
26		t.Fatalf("gate = %d, want 30 (10 painted + 20 pending)", got)
27	}
28
29	if i.toolGateOpenLocked("t1") {
30		t.Fatal("tool should be gated while only 10/30 runes painted")
31	}
32
33	// Drain halfway: still gated.
34	i.streaming.WriteString("0123456789") // 20 painted
35	if i.toolGateOpenLocked("t1") {
36		t.Fatal("tool should still be gated at 20/30")
37	}
38
39	// Reach the gate: now visible.
40	i.streaming.WriteString("0123456789") // 30 painted
41	if !i.toolGateOpenLocked("t1") {
42		t.Fatal("tool should be visible once streaming reaches the gate")
43	}
44}
45
46// A tool call on a turn with no active text stream shows immediately.
47func TestToolGateOpenWhenNoStream(t *testing.T) {
48	i := newGateTestInteractive()
49	i.streamOn = false
50
51	i.gateToolLocked("t1")
52	if got := i.toolGate["t1"]; got != 0 {
53		t.Fatalf("gate = %d, want 0 for non-streaming turn", got)
54	}
55	if !i.toolGateOpenLocked("t1") {
56		t.Fatal("tool should be visible immediately when no text is streaming")
57	}
58}
59
60// First registration wins: a later EvToolCall must not move an
61// existing gate (e.g. push it forward as more text arrives).
62func TestToolGateFirstRegistrationWins(t *testing.T) {
63	i := newGateTestInteractive()
64	i.streamOn = true
65	i.streaming.WriteString("01234") // 5
66	i.streamPending = []rune("01234")
67
68	i.gateToolLocked("t1") // gate 10
69	first := i.toolGate["t1"]
70
71	// More text queues up, then the same tool is re-registered.
72	i.streamPending = append(i.streamPending, []rune("567890")...)
73	i.gateToolLocked("t1")
74
75	if i.toolGate["t1"] != first {
76		t.Fatalf("gate moved from %d to %d; first registration must win", first, i.toolGate["t1"])
77	}
78}
79
80// Once streaming finalizes, the buffer resets to length 0; gates that
81// were already satisfied must not re-hide their tools.
82func TestOpenAllToolGatesSurvivesStreamReset(t *testing.T) {
83	i := newGateTestInteractive()
84	i.streamOn = true
85	i.streaming.WriteString("0123456789")
86	i.gateToolLocked("t1") // gate 10, satisfied
87
88	if !i.toolGateOpenLocked("t1") {
89		t.Fatal("precondition: tool should be open before reset")
90	}
91
92	// Finalize the stream (mirrors resetStreamingStateLocked / pacer
93	// flush): buffer resets to 0 but gates are opened first.
94	i.openAllToolGatesLocked()
95	i.streaming.Reset()
96	i.streamOn = false
97
98	if !i.toolGateOpenLocked("t1") {
99		t.Fatal("tool re-hidden after stream reset; gate should have been opened")
100	}
101}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 477

↑↓ moveenter openesc close