1package modes
2
3import "testing"
4
5func TestNormalizeAutoCompactThreshold(t *testing.T) {
6 tests := []struct {
7 name string
8 value *int
9 want int
10 }{
11 {name: "missing", value: nil, want: 85},
12 {name: "off", value: autoCompactIntPtr(0), want: 0},
13 {name: "seventy", value: autoCompactIntPtr(70), want: 70},
14 {name: "eighty", value: autoCompactIntPtr(80), want: 80},
15 {name: "eighty five", value: autoCompactIntPtr(85), want: 85},
16 {name: "ninety", value: autoCompactIntPtr(90), want: 90},
17 {name: "invalid low", value: autoCompactIntPtr(42), want: 85},
18 {name: "invalid high", value: autoCompactIntPtr(100), want: 85},
19 }
20
21 for _, tt := range tests {
22 t.Run(tt.name, func(t *testing.T) {
23 if got := normalizeAutoCompactThreshold(tt.value); got != tt.want {
24 t.Fatalf("normalizeAutoCompactThreshold(%v) = %d, want %d", tt.value, got, tt.want)
25 }
26 })
27 }
28}
29
30func TestShouldAutoCompactUsesConfiguredThreshold(t *testing.T) {
31 tests := []struct {
32 name string
33 used int
34 window int
35 threshold int
36 want bool
37 }{
38 {name: "below threshold", used: 84, window: 100, threshold: 85, want: false},
39 {name: "at threshold", used: 85, window: 100, threshold: 85, want: true},
40 {name: "above threshold", used: 86, window: 100, threshold: 85, want: true},
41 {name: "seventy percent preset", used: 70, window: 100, threshold: 70, want: true},
42 {name: "eighty percent preset", used: 80, window: 100, threshold: 80, want: true},
43 {name: "ninety percent preset", used: 90, window: 100, threshold: 90, want: true},
44 {name: "off", used: 100, window: 100, threshold: 0, want: false},
45 {name: "missing usage", used: 0, window: 100, threshold: 85, want: false},
46 {name: "missing window", used: 85, window: 0, threshold: 85, want: false},
47 }
48
49 for _, tt := range tests {
50 t.Run(tt.name, func(t *testing.T) {
51 if got := shouldAutoCompact(tt.used, tt.window, tt.threshold); got != tt.want {
52 t.Fatalf("shouldAutoCompact(%d, %d, %d) = %t, want %t", tt.used, tt.window, tt.threshold, got, tt.want)
53 }
54 })
55 }
56}
57
58func TestSettingsDialogOffersAutoCompactThresholdPresets(t *testing.T) {
59 interactive := NewInteractive(InteractiveConfig{})
60 interactive.openSettingsDialog()
61
62 var thresholdItem *settingsItem
63 for idx := range interactive.settingsDialog.items {
64 item := &interactive.settingsDialog.items[idx]
65 if item.key == "auto_compact_threshold" {
66 thresholdItem = item
67 break
68 }
69 }
70 if thresholdItem == nil {
71 t.Fatal("settings dialog is missing auto-compact threshold")
72 }
73
74 wantValues := []string{"0", "70", "80", "85", "90"}
75 if len(thresholdItem.options) != len(wantValues) {
76 t.Fatalf("auto-compact options = %d, want %d", len(thresholdItem.options), len(wantValues))
77 }
78 for idx, want := range wantValues {
79 if got := thresholdItem.options[idx].value; got != want {
80 t.Fatalf("auto-compact option %d = %q, want %q", idx, got, want)
81 }
82 }
83 if got := thresholdItem.options[thresholdItem.choice].value; got != "85" {
84 t.Fatalf("default auto-compact choice = %q, want 85", got)
85 }
86}
87
88func TestApplyAutoCompactThresholdSynchronizesLiveUpdate(t *testing.T) {
89 initialThreshold := 85
90 releaseStore := make(chan struct{})
91 store := &blockingAutoCompactSettingsStore{
92 entered: make(chan int, 1),
93 release: releaseStore,
94 }
95 interactive := NewInteractive(InteractiveConfig{
96 AutoCompactThreshold: &initialThreshold,
97 SettingsStore: store,
98 })
99
100 interactive.mu.Lock()
101 locked := true
102 released := false
103 done := make(chan struct{})
104 go func() {
105 defer close(done)
106 interactive.applyAutoCompactThresholdSetting("70")
107 }()
108 defer func() {
109 if !released {
110 close(releaseStore)
111 }
112 if locked {
113 interactive.mu.Unlock()
114 }
115 <-done
116 }()
117
118 if got := <-store.entered; got != 70 {
119 t.Fatalf("persisted threshold = %d, want 70", got)
120 }
121 if got := *interactive.cfg.AutoCompactThreshold; got != 85 {
122 t.Fatalf("threshold changed without holding Interactive.mu: got %d, want 85", got)
123 }
124
125 close(releaseStore)
126 released = true
127 interactive.mu.Unlock()
128 locked = false
129 <-done
130 if got := *interactive.cfg.AutoCompactThreshold; got != 70 {
131 t.Fatalf("threshold after setting completes = %d, want 70", got)
132 }
133}
134
135type blockingAutoCompactSettingsStore struct {
136 SettingsStore
137 entered chan int
138 release <-chan struct{}
139}
140
141func (s *blockingAutoCompactSettingsStore) SetAutoCompactThreshold(percent int) error {
142 s.entered <- percent
143 <-s.release
144 return nil
145}
146
147func autoCompactIntPtr(value int) *int {
148 return &value
149}