v0.3.0 761f038

fast_mode_test.go RawHistoryFolder

1package modes
2
3import (
4	"context"
5	"errors"
6	"strings"
7	"testing"
8
9	"github.com/bnema/zut/packages/core"
10	"github.com/bnema/zut/packages/provider"
11)
12
13type recordingFastModeSettingsStore struct {
14	SettingsStore
15	setCalls int
16	last     bool
17}
18
19func (s *recordingFastModeSettingsStore) SetFastMode(enabled bool) error {
20	s.setCalls++
21	s.last = enabled
22	return nil
23}
24
25type failingFastModeSettingsStore struct {
26	SettingsStore
27	err error
28}
29
30func (s *failingFastModeSettingsStore) SetFastMode(bool) error { return s.err }
31
32type fastModeProviderClient struct{}
33
34func (fastModeProviderClient) Name() string { return "openai" }
35func (fastModeProviderClient) Stream(_ context.Context, _ provider.Request) (<-chan provider.Event, error) {
36	out := make(chan provider.Event)
37	close(out)
38	return out, nil
39}
40
41func TestSettingsDialogOffersFastModeOffByDefault(t *testing.T) {
42	interactive := NewInteractive(InteractiveConfig{Provider: "openai", Model: "gpt-5"})
43	interactive.openSettingsDialog()
44
45	item := findSettingsItem(interactive.settingsDialog.items, "fast_mode")
46	if item == nil {
47		t.Fatal("settings dialog is missing fast mode")
48	}
49	if item.value {
50		t.Fatal("fast mode is enabled by default")
51	}
52}
53
54func TestApplyFastModeUpdatesAgentAndStore(t *testing.T) {
55	store := &recordingFastModeSettingsStore{}
56	agent := core.NewAgent(fastModeProviderClient{}, "gpt-5", "", nil)
57	interactive := NewInteractive(InteractiveConfig{
58		Provider:      "openai",
59		Model:         "gpt-5",
60		SettingsStore: store,
61		Agent:         agent,
62	})
63	interactive.openSettingsDialog()
64	interactive.rend = nil
65
66	interactive.applySettingToggle("fast_mode", true)
67
68	if interactive.cfg.FastMode == nil || !*interactive.cfg.FastMode {
69		t.Fatal("interactive FastMode was not enabled")
70	}
71	if !agent.FastMode {
72		t.Fatal("agent FastMode was not enabled")
73	}
74	if store.setCalls != 1 || !store.last {
75		t.Fatalf("store calls = %d/%v, want one enabled write", store.setCalls, store.last)
76	}
77}
78
79func TestApplyFastModeRejectsUnsupportedProvider(t *testing.T) {
80	store := &recordingFastModeSettingsStore{}
81	interactive := NewInteractive(InteractiveConfig{
82		Provider:      "anthropic",
83		Model:         "claude-sonnet-4-5",
84		SettingsStore: store,
85	})
86	interactive.openSettingsDialog()
87	interactive.rend = nil
88
89	interactive.applySettingToggle("fast_mode", true)
90
91	if interactive.cfg.FastMode != nil && *interactive.cfg.FastMode {
92		t.Fatal("unsupported fast mode was enabled")
93	}
94	if store.setCalls != 0 {
95		t.Fatalf("store calls = %d, want none", store.setCalls)
96	}
97	if !strings.Contains(interactive.statusErr, "only supported for OpenAI providers") {
98		t.Fatalf("status error = %q, want unsupported-provider error", interactive.statusErr)
99	}
100}
101
102func TestApplyFastModeCanDisableUnsupportedProvider(t *testing.T) {
103	enabled := true
104	store := &recordingFastModeSettingsStore{}
105	agent := core.NewAgent(fastModeProviderClient{}, "claude-sonnet-4-5", "", nil)
106	agent.FastMode = true
107	interactive := NewInteractive(InteractiveConfig{
108		Provider:      "anthropic",
109		Model:         "claude-sonnet-4-5",
110		FastMode:      &enabled,
111		SettingsStore: store,
112		Agent:         agent,
113	})
114	interactive.openSettingsDialog()
115	interactive.rend = nil
116
117	interactive.applySettingToggle("fast_mode", false)
118
119	if interactive.cfg.FastMode == nil || *interactive.cfg.FastMode {
120		t.Fatal("fast mode was not disabled")
121	}
122	if agent.FastMode {
123		t.Fatal("agent fast mode was not disabled")
124	}
125	if store.setCalls != 1 || store.last {
126		t.Fatalf("store calls = %d/%v, want one disabled write", store.setCalls, store.last)
127	}
128}
129
130func TestApplyFastModeLeavesLiveStateWhenPersistenceFails(t *testing.T) {
131	store := &failingFastModeSettingsStore{err: errors.New("disk full")}
132	agent := core.NewAgent(fastModeProviderClient{}, "gpt-5", "", nil)
133	interactive := NewInteractive(InteractiveConfig{
134		Provider:      "openai",
135		Model:         "gpt-5",
136		SettingsStore: store,
137		Agent:         agent,
138	})
139	interactive.openSettingsDialog()
140	interactive.rend = nil
141
142	interactive.applySettingToggle("fast_mode", true)
143
144	if interactive.cfg.FastMode != nil && *interactive.cfg.FastMode {
145		t.Fatal("fast mode changed despite persistence failure")
146	}
147	if agent.FastMode {
148		t.Fatal("agent fast mode changed despite persistence failure")
149	}
150	if !strings.Contains(interactive.statusErr, "disk full") {
151		t.Fatalf("status error = %q, want persistence error", interactive.statusErr)
152	}
153}
154
155func findSettingsItem(items []settingsItem, key string) *settingsItem {
156	for idx := range items {
157		if items[idx].key == key {
158			return &items[idx]
159		}
160	}
161	return nil
162}

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