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}