v0.33.1 9577fee

migrate_config_test.go RawHistoryFolder

1package usecase
2
3import (
4	"context"
5	"errors"
6	"testing"
7
8	"github.com/stretchr/testify/assert"
9	"github.com/stretchr/testify/mock"
10	"github.com/stretchr/testify/require"
11
12	"github.com/bnema/dumber/internal/application/port"
13	"github.com/bnema/dumber/internal/application/port/mocks"
14)
15
16func TestMigrateConfigUseCase_Check_NoMigrationNeeded(t *testing.T) {
17	mockMigrator := mocks.NewMockConfigMigrator(t)
18	mockFormatter := mocks.NewMockDiffFormatter(t)
19	mockMigrator.EXPECT().CheckMigration().Return(nil, nil)
20
21	uc := NewMigrateConfigUseCase(mockMigrator, mockFormatter)
22	ctx := context.Background()
23
24	result, err := uc.Check(ctx, CheckConfigMigrationInput{})
25
26	require.NoError(t, err)
27	assert.False(t, result.NeedsMigration)
28	assert.Empty(t, result.MissingKeys)
29}
30
31func TestMigrateConfigUseCase_Check_MigrationNeeded(t *testing.T) {
32	mockMigrator := mocks.NewMockConfigMigrator(t)
33	mockFormatter := mocks.NewMockDiffFormatter(t)
34
35	migrationResult := &port.MigrationResult{
36		MissingKeys: []string{"key1", "key2"},
37		ConfigFile:  "/path/to/config.toml",
38	}
39	mockMigrator.EXPECT().CheckMigration().Return(migrationResult, nil)
40
41	// Mock GetKeyInfo for each missing key
42	mockMigrator.EXPECT().GetKeyInfo("key1").Return(port.KeyInfo{
43		Key:          "key1",
44		Type:         "bool",
45		DefaultValue: "true",
46	})
47	mockMigrator.EXPECT().GetKeyInfo("key2").Return(port.KeyInfo{
48		Key:          "key2",
49		Type:         "string",
50		DefaultValue: `"default"`,
51	})
52
53	uc := NewMigrateConfigUseCase(mockMigrator, mockFormatter)
54	ctx := context.Background()
55
56	result, err := uc.Check(ctx, CheckConfigMigrationInput{})
57
58	require.NoError(t, err)
59	assert.True(t, result.NeedsMigration)
60	assert.Len(t, result.MissingKeys, 2)
61	assert.Equal(t, "/path/to/config.toml", result.ConfigFile)
62
63	// Verify key info
64	assert.Equal(t, "key1", result.MissingKeys[0].Key)
65	assert.Equal(t, "bool", result.MissingKeys[0].Type)
66	assert.Equal(t, "key2", result.MissingKeys[1].Key)
67	assert.Equal(t, "string", result.MissingKeys[1].Type)
68}
69
70func TestMigrateConfigUseCase_Check_Error(t *testing.T) {
71	mockMigrator := mocks.NewMockConfigMigrator(t)
72	mockFormatter := mocks.NewMockDiffFormatter(t)
73	expectedErr := errors.New("check failed")
74	mockMigrator.EXPECT().CheckMigration().Return(nil, expectedErr)
75
76	uc := NewMigrateConfigUseCase(mockMigrator, mockFormatter)
77	ctx := context.Background()
78
79	result, err := uc.Check(ctx, CheckConfigMigrationInput{})
80
81	require.Error(t, err)
82	assert.Nil(t, result)
83	assert.Equal(t, expectedErr, err)
84}
85
86func TestMigrateConfigUseCase_Execute_NoMigrationNeeded(t *testing.T) {
87	mockMigrator := mocks.NewMockConfigMigrator(t)
88	mockFormatter := mocks.NewMockDiffFormatter(t)
89	mockMigrator.EXPECT().DetectChanges().Return(nil, nil)
90
91	uc := NewMigrateConfigUseCase(mockMigrator, mockFormatter)
92	ctx := context.Background()
93
94	result, err := uc.Execute(ctx, MigrateConfigInput{})
95
96	require.NoError(t, err)
97	assert.Empty(t, result.AddedKeys)
98}
99
100func TestMigrateConfigUseCase_Execute_Success(t *testing.T) {
101	mockMigrator := mocks.NewMockConfigMigrator(t)
102	mockFormatter := mocks.NewMockDiffFormatter(t)
103
104	changes := []port.KeyChange{
105		{Type: port.KeyChangeAdded, NewKey: "key1"},
106		{Type: port.KeyChangeAdded, NewKey: "key2"},
107	}
108	mockMigrator.EXPECT().DetectChanges().Return(changes, nil)
109	mockMigrator.EXPECT().GetConfigFile().Return("/path/to/config.toml", nil)
110	mockMigrator.EXPECT().Migrate().Return([]string{"key1", "key2"}, nil)
111
112	uc := NewMigrateConfigUseCase(mockMigrator, mockFormatter)
113	ctx := context.Background()
114
115	result, err := uc.Execute(ctx, MigrateConfigInput{})
116
117	require.NoError(t, err)
118	assert.Len(t, result.AddedKeys, 2)
119	assert.Equal(t, "/path/to/config.toml", result.ConfigFile)
120}
121
122func TestMigrateConfigUseCase_Execute_MigrateError(t *testing.T) {
123	mockMigrator := mocks.NewMockConfigMigrator(t)
124	mockFormatter := mocks.NewMockDiffFormatter(t)
125
126	changes := []port.KeyChange{
127		{Type: port.KeyChangeAdded, NewKey: "key1"},
128	}
129	mockMigrator.EXPECT().DetectChanges().Return(changes, nil)
130	mockMigrator.EXPECT().GetConfigFile().Return("/path/to/config.toml", nil)
131
132	expectedErr := errors.New("migrate failed")
133	mockMigrator.EXPECT().Migrate().Return(nil, expectedErr)
134
135	uc := NewMigrateConfigUseCase(mockMigrator, mockFormatter)
136	ctx := context.Background()
137
138	result, err := uc.Execute(ctx, MigrateConfigInput{})
139
140	require.Error(t, err)
141	assert.Nil(t, result)
142	assert.Equal(t, expectedErr, err)
143}
144
145func TestMigrateConfigUseCase_Execute_DetectChangesError(t *testing.T) {
146	mockMigrator := mocks.NewMockConfigMigrator(t)
147	mockFormatter := mocks.NewMockDiffFormatter(t)
148
149	expectedErr := errors.New("detect changes failed")
150	mockMigrator.EXPECT().DetectChanges().Return(nil, expectedErr)
151
152	uc := NewMigrateConfigUseCase(mockMigrator, mockFormatter)
153	ctx := context.Background()
154
155	result, err := uc.Execute(ctx, MigrateConfigInput{})
156
157	require.Error(t, err)
158	assert.Nil(t, result)
159	assert.Equal(t, expectedErr, err)
160}
161
162func TestMigrateConfigUseCase_Check_EmptyMissingKeys(t *testing.T) {
163	mockMigrator := mocks.NewMockConfigMigrator(t)
164	mockFormatter := mocks.NewMockDiffFormatter(t)
165
166	// Return a result with empty missing keys
167	migrationResult := &port.MigrationResult{
168		MissingKeys: []string{},
169		ConfigFile:  "/path/to/config.toml",
170	}
171	mockMigrator.EXPECT().CheckMigration().Return(migrationResult, nil)
172
173	uc := NewMigrateConfigUseCase(mockMigrator, mockFormatter)
174	ctx := context.Background()
175
176	result, err := uc.Check(ctx, CheckConfigMigrationInput{})
177
178	require.NoError(t, err)
179	assert.False(t, result.NeedsMigration)
180	assert.Empty(t, result.MissingKeys)
181}
182
183func TestNewMigrateConfigUseCase(t *testing.T) {
184	mockMigrator := mocks.NewMockConfigMigrator(t)
185	mockFormatter := mocks.NewMockDiffFormatter(t)
186	uc := NewMigrateConfigUseCase(mockMigrator, mockFormatter)
187
188	assert.NotNil(t, uc)
189	assert.Equal(t, mockMigrator, uc.migrator)
190	assert.Equal(t, mockFormatter, uc.diffFormatter)
191}
192
193// Ensure mock expectations are set up correctly
194func TestMigrateConfigUseCase_MockExpectations(t *testing.T) {
195	mockMigrator := mocks.NewMockConfigMigrator(t)
196	mockFormatter := mocks.NewMockDiffFormatter(t)
197
198	// Set up expectations with mock.Anything for flexibility
199	mockMigrator.On("CheckMigration").Return(&port.MigrationResult{
200		MissingKeys: []string{"test.key"},
201		ConfigFile:  "/test/config.toml",
202	}, nil).Once()
203
204	mockMigrator.On("GetKeyInfo", mock.AnythingOfType("string")).Return(port.KeyInfo{
205		Key:          "test.key",
206		Type:         "string",
207		DefaultValue: `"test"`,
208	}).Once()
209
210	uc := NewMigrateConfigUseCase(mockMigrator, mockFormatter)
211	ctx := context.Background()
212
213	result, err := uc.Check(ctx, CheckConfigMigrationInput{})
214
215	require.NoError(t, err)
216	assert.True(t, result.NeedsMigration)
217	mockMigrator.AssertExpectations(t)
218}
219
220func TestMigrateConfigUseCase_DetectChanges_NoChanges(t *testing.T) {
221	mockMigrator := mocks.NewMockConfigMigrator(t)
222	mockFormatter := mocks.NewMockDiffFormatter(t)
223	mockMigrator.EXPECT().DetectChanges().Return(nil, nil)
224
225	uc := NewMigrateConfigUseCase(mockMigrator, mockFormatter)
226	ctx := context.Background()
227
228	result, err := uc.DetectChanges(ctx, DetectChangesInput{})
229
230	require.NoError(t, err)
231	assert.False(t, result.HasChanges)
232	assert.Empty(t, result.Changes)
233	assert.Equal(t, "No changes detected.", result.DiffText)
234}
235
236func TestMigrateConfigUseCase_DetectChanges_WithAddedKey(t *testing.T) {
237	mockMigrator := mocks.NewMockConfigMigrator(t)
238	mockFormatter := mocks.NewMockDiffFormatter(t)
239
240	changes := []port.KeyChange{
241		{
242			Type:     port.KeyChangeAdded,
243			NewKey:   "workspace.styling.mode_indicator_toaster_enabled",
244			NewValue: "true",
245		},
246	}
247	mockMigrator.EXPECT().DetectChanges().Return(changes, nil)
248	mockFormatter.EXPECT().FormatChangesAsDiff(changes).Return("Config migration changes:\n\n  + workspace.styling.mode_indicator_toaster_enabled = true\n")
249
250	uc := NewMigrateConfigUseCase(mockMigrator, mockFormatter)
251	ctx := context.Background()
252
253	result, err := uc.DetectChanges(ctx, DetectChangesInput{})
254
255	require.NoError(t, err)
256	assert.True(t, result.HasChanges)
257	assert.Len(t, result.Changes, 1)
258	assert.Contains(t, result.DiffText, "+ workspace.styling.mode_indicator_toaster_enabled")
259}
260
261func TestMigrateConfigUseCase_DetectChanges_WithRenamedKey(t *testing.T) {
262	mockMigrator := mocks.NewMockConfigMigrator(t)
263	mockFormatter := mocks.NewMockDiffFormatter(t)
264
265	changes := []port.KeyChange{
266		{
267			Type:     port.KeyChangeRenamed,
268			OldKey:   "workspace.styling.pane_mode_border_color",
269			NewKey:   "workspace.styling.pane_mode_color",
270			OldValue: `"#4A90E2"`,
271		},
272	}
273	mockMigrator.EXPECT().DetectChanges().Return(changes, nil)
274	mockFormatter.EXPECT().FormatChangesAsDiff(changes).Return("Config migration changes:\n\n  ~ workspace.styling.pane_mode_border_color -> workspace.styling.pane_mode_color\n    (value: \"#4A90E2\")\n")
275
276	uc := NewMigrateConfigUseCase(mockMigrator, mockFormatter)
277	ctx := context.Background()
278
279	result, err := uc.DetectChanges(ctx, DetectChangesInput{})
280
281	require.NoError(t, err)
282	assert.True(t, result.HasChanges)
283	assert.Len(t, result.Changes, 1)
284	assert.Contains(t, result.DiffText, "~ workspace.styling.pane_mode_border_color -> workspace.styling.pane_mode_color")
285}
286
287func TestMigrateConfigUseCase_DetectChanges_WithRemovedKey(t *testing.T) {
288	mockMigrator := mocks.NewMockConfigMigrator(t)
289	mockFormatter := mocks.NewMockDiffFormatter(t)
290
291	changes := []port.KeyChange{
292		{
293			Type:     port.KeyChangeRemoved,
294			OldKey:   "workspace.styling.pane_mode_border_width",
295			OldValue: "4",
296		},
297	}
298	mockMigrator.EXPECT().DetectChanges().Return(changes, nil)
299	mockFormatter.EXPECT().FormatChangesAsDiff(changes).Return("Config migration changes:\n\n  - workspace.styling.pane_mode_border_width = 4 (deprecated)\n")
300
301	uc := NewMigrateConfigUseCase(mockMigrator, mockFormatter)
302	ctx := context.Background()
303
304	result, err := uc.DetectChanges(ctx, DetectChangesInput{})
305
306	require.NoError(t, err)
307	assert.True(t, result.HasChanges)
308	assert.Len(t, result.Changes, 1)
309	assert.Contains(t, result.DiffText, "- workspace.styling.pane_mode_border_width")
310	assert.Contains(t, result.DiffText, "(deprecated)")
311}
312
313func TestMigrateConfigUseCase_DetectChanges_Error(t *testing.T) {
314	mockMigrator := mocks.NewMockConfigMigrator(t)
315	mockFormatter := mocks.NewMockDiffFormatter(t)
316	expectedErr := errors.New("detect changes failed")
317	mockMigrator.EXPECT().DetectChanges().Return(nil, expectedErr)
318
319	uc := NewMigrateConfigUseCase(mockMigrator, mockFormatter)
320	ctx := context.Background()
321
322	result, err := uc.DetectChanges(ctx, DetectChangesInput{})
323
324	require.Error(t, err)
325	assert.Nil(t, result)
326	assert.Equal(t, expectedErr, err)
327}
328
329func TestMigrateConfigUseCase_DetectChanges_MultipleChanges(t *testing.T) {
330	mockMigrator := mocks.NewMockConfigMigrator(t)
331	mockFormatter := mocks.NewMockDiffFormatter(t)
332
333	changes := []port.KeyChange{
334		{
335			Type:     port.KeyChangeAdded,
336			NewKey:   "workspace.styling.mode_border_width",
337			NewValue: "4",
338		},
339		{
340			Type:     port.KeyChangeRenamed,
341			OldKey:   "workspace.styling.pane_mode_border_color",
342			NewKey:   "workspace.styling.pane_mode_color",
343			OldValue: `"#4A90E2"`,
344		},
345		{
346			Type:     port.KeyChangeRemoved,
347			OldKey:   "workspace.styling.pane_mode_border_width",
348			OldValue: "4",
349		},
350	}
351	mockMigrator.EXPECT().DetectChanges().Return(changes, nil)
352	mockFormatter.EXPECT().FormatChangesAsDiff(changes).Return(
353		"Config migration changes:\n\n" +
354			"  + workspace.styling.mode_border_width = 4\n" +
355			"  ~ workspace.styling.pane_mode_border_color -> workspace.styling.pane_mode_color\n" +
356			"    (value: \"#4A90E2\")\n" +
357			"  - workspace.styling.pane_mode_border_width = 4 (deprecated)\n",
358	)
359
360	uc := NewMigrateConfigUseCase(mockMigrator, mockFormatter)
361	ctx := context.Background()
362
363	result, err := uc.DetectChanges(ctx, DetectChangesInput{})
364
365	require.NoError(t, err)
366	assert.True(t, result.HasChanges)
367	assert.Len(t, result.Changes, 3)
368
369	// Verify diff contains all change types
370	assert.Contains(t, result.DiffText, "+ workspace.styling.mode_border_width")
371	assert.Contains(t, result.DiffText, "~ workspace.styling.pane_mode_border_color")
372	assert.Contains(t, result.DiffText, "- workspace.styling.pane_mode_border_width")
373}

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 8

Tags 42

↑↓ moveenter openesc close