3
3
import argparse
4
4
import subprocess
5
5
import sys
6
- from unittest .mock import MagicMock , call , patch
6
+ from unittest .mock import MagicMock , patch
7
7
8
8
import pytest
9
9
16
16
@pytest .fixture
17
17
def commit_loom ():
18
18
"""Fixture for CommitLoom instance with mocked dependencies."""
19
- with patch ("commitloom.cli.main.GitOperations" ) as mock_git , patch (
20
- "commitloom.cli.main.CommitAnalyzer"
21
- ) as mock_analyzer , patch ("commitloom.cli.main.AIService" ) as mock_ai , patch (
22
- "commitloom.cli.main.load_dotenv"
19
+ with (
20
+ patch ("commitloom.cli.main.GitOperations" ) as mock_git ,
21
+ patch ("commitloom.cli.main.CommitAnalyzer" ) as mock_analyzer ,
22
+ patch ("commitloom.cli.main.AIService" ) as mock_ai ,
23
+ patch ("commitloom.cli.main.load_dotenv" ),
23
24
):
24
25
instance = CommitLoom ()
25
26
instance .git = mock_git .return_value
@@ -34,8 +35,8 @@ def test_process_files_in_batches_single_batch(mock_confirm, mock_run, commit_lo
34
35
"""Test processing files when they fit in a single batch."""
35
36
# Setup test data
36
37
files = [
37
- GitFile (path = "file1.py" ),
38
- GitFile (path = "file2.py" ),
38
+ GitFile (path = "file1.py" , status = "M" ),
39
+ GitFile (path = "file2.py" , status = "M" ),
39
40
]
40
41
41
42
# Mock git status and ignore check
@@ -71,19 +72,16 @@ def mock_git_status(cmd, **kwargs):
71
72
)
72
73
mock_confirm .return_value = True
73
74
74
- result = commit_loom .process_files_in_batches (files , auto_commit = False )
75
- assert len (result ) == 1
76
- assert len (result [0 ]["files" ]) == 2
77
- assert result [0 ]["files" ][0 ].path == "file1.py"
78
- assert result [0 ]["files" ][1 ].path == "file2.py"
75
+ commit_loom .process_files_in_batches (files )
76
+ assert commit_loom .git .create_commit .called
79
77
80
78
81
79
@patch ("subprocess.run" )
82
80
@patch ("commitloom.cli.console.confirm_action" )
83
81
def test_process_files_in_batches_multiple_batches (mock_confirm , mock_run , commit_loom ):
84
82
"""Test processing files that need to be split into multiple batches."""
85
83
# Setup test data
86
- files = [GitFile (path = f"file{ i } .py" ) for i in range (10 )]
84
+ files = [GitFile (path = f"file{ i } .py" , status = "M" ) for i in range (10 )]
87
85
88
86
# Mock git status and ignore check
89
87
def mock_git_status (cmd , ** kwargs ):
@@ -121,32 +119,29 @@ def mock_git_status(cmd, **kwargs):
121
119
# Set max_files_threshold to 5 to force multiple batches
122
120
commit_loom .analyzer .config .max_files_threshold = 5
123
121
124
- result = commit_loom .process_files_in_batches (files , auto_commit = False )
125
- assert len (result ) == 2 # Should be split into 2 batches of 5 files each
126
- assert len (result [0 ]["files" ]) == 5
127
- assert len (result [1 ]["files" ]) == 5
122
+ commit_loom .process_files_in_batches (files )
123
+ assert commit_loom .git .create_commit .call_count == 4 # Should create 4 commits for 10 files
128
124
129
125
130
126
@patch ("commitloom.cli.console.print_success" )
131
127
def test_create_combined_commit (mock_print_success , commit_loom ):
132
128
"""Test creating a combined commit from multiple batches."""
133
129
# Mock format_commit_message to return a formatted string
134
130
commit_loom .ai_service .format_commit_message .return_value = (
135
- "📦 chore: combine multiple changes\n \n "
136
- "✨ Features:\n "
131
+ "📦 chore: combine multiple changes\n \n " "✨ Features:\n "
137
132
)
138
133
139
134
batches = [
140
135
{
141
- "files" : [GitFile (path = "file1.py" )],
136
+ "files" : [GitFile (path = "file1.py" , status = "M" )],
142
137
"commit_data" : CommitSuggestion (
143
138
title = "feat: first change" ,
144
139
body = {"Features" : {"emoji" : "✨" , "changes" : ["Change 1" ]}},
145
140
summary = "First summary" ,
146
141
),
147
142
},
148
143
{
149
- "files" : [GitFile (path = "file2.py" )],
144
+ "files" : [GitFile (path = "file2.py" , status = "M" )],
150
145
"commit_data" : CommitSuggestion (
151
146
title = "fix: second change" ,
152
147
body = {"Fixes" : {"emoji" : "🐛" , "changes" : ["Fix 1" ]}},
@@ -175,21 +170,19 @@ def test_create_combined_commit(mock_print_success, commit_loom):
175
170
@patch ("commitloom.cli.main.console" )
176
171
def test_run_no_changes (mock_console , commit_loom ):
177
172
"""Test run when there are no changes."""
178
- commit_loom .git .get_changed_files .return_value = []
173
+ commit_loom .git .get_staged_files .return_value = []
179
174
180
175
commit_loom .run ()
181
176
182
- mock_console .print_error .assert_called_once_with (
183
- "No changes detected in the staging area."
184
- )
177
+ mock_console .print_warning .assert_called_once_with ("No files staged for commit." )
185
178
186
179
187
180
@patch ("commitloom.cli.main.console" )
188
181
def test_run_simple_change (mock_console , commit_loom ):
189
182
"""Test run with a simple change."""
190
183
# Setup test data
191
- files = [GitFile (path = "test.py" )]
192
- commit_loom .git .get_changed_files .return_value = files
184
+ files = [GitFile (path = "test.py" , status = "M" )]
185
+ commit_loom .git .get_staged_files .return_value = files
193
186
commit_loom .git .get_diff .return_value = "test diff"
194
187
commit_loom .analyzer .analyze_diff_complexity .return_value = CommitAnalysis (
195
188
estimated_tokens = 100 ,
@@ -198,9 +191,7 @@ def test_run_simple_change(mock_console, commit_loom):
198
191
warnings = [],
199
192
is_complex = False ,
200
193
)
201
- commit_loom .analyzer .config .max_files_threshold = (
202
- 5 # Set higher than number of files
203
- )
194
+ commit_loom .analyzer .config .max_files_threshold = 5 # Set higher than number of files
204
195
commit_loom .ai_service .generate_commit_message .return_value = (
205
196
CommitSuggestion (
206
197
title = "test commit" ,
@@ -222,8 +213,8 @@ def test_run_simple_change(mock_console, commit_loom):
222
213
def test_run_with_warnings (mock_console , commit_loom ):
223
214
"""Test run with complexity warnings."""
224
215
# Setup test data
225
- files = [GitFile (path = "test.py" )]
226
- commit_loom .git .get_changed_files .return_value = files
216
+ files = [GitFile (path = "test.py" , status = "M" )]
217
+ commit_loom .git .get_staged_files .return_value = files
227
218
commit_loom .git .get_diff .return_value = "test diff"
228
219
commit_loom .analyzer .analyze_diff_complexity .return_value = CommitAnalysis (
229
220
estimated_tokens = 1000 ,
@@ -239,21 +230,15 @@ def test_run_with_warnings(mock_console, commit_loom):
239
230
commit_loom .run ()
240
231
241
232
# Verify all expected messages were printed
242
- mock_console .print_info .assert_has_calls (
243
- [
244
- call ("Analyzing your changes..." ),
245
- call ("Process cancelled. Please review your changes." ),
246
- ]
247
- )
248
233
mock_console .print_warnings .assert_called_once ()
249
234
250
235
251
236
@patch ("commitloom.cli.main.console" )
252
237
def test_run_with_warnings_continue (mock_console , commit_loom ):
253
238
"""Test run with warnings when user chooses to continue."""
254
239
# Setup test data
255
- files = [GitFile (path = "test.py" )]
256
- commit_loom .git .get_changed_files .return_value = files
240
+ files = [GitFile (path = "test.py" , status = "M" )]
241
+ commit_loom .git .get_staged_files .return_value = files
257
242
commit_loom .git .get_diff .return_value = "test diff"
258
243
commit_loom .analyzer .analyze_diff_complexity .return_value = CommitAnalysis (
259
244
estimated_tokens = 1000 ,
@@ -278,18 +263,15 @@ def test_run_with_warnings_continue(mock_console, commit_loom):
278
263
279
264
# Verify expected messages and actions
280
265
mock_console .print_warnings .assert_called_once ()
281
- mock_console .print_info .assert_has_calls (
282
- [call ("Analyzing your changes..." ), call ("\n Generated Commit Message:" )]
283
- )
284
266
commit_loom .git .create_commit .assert_called_once ()
285
267
286
268
287
269
@patch ("commitloom.cli.main.console" )
288
270
def test_run_commit_error (mock_console , commit_loom ):
289
271
"""Test run when commit creation fails."""
290
272
# Setup test data
291
- files = [GitFile (path = "test.py" )]
292
- commit_loom .git .get_changed_files .return_value = files
273
+ files = [GitFile (path = "test.py" , status = "M" )]
274
+ commit_loom .git .get_staged_files .return_value = files
293
275
commit_loom .git .get_diff .return_value = "test diff"
294
276
commit_loom .analyzer .analyze_diff_complexity .return_value = CommitAnalysis (
295
277
estimated_tokens = 100 ,
@@ -298,9 +280,7 @@ def test_run_commit_error(mock_console, commit_loom):
298
280
warnings = [],
299
281
is_complex = False ,
300
282
)
301
- commit_loom .analyzer .config .max_files_threshold = (
302
- 5 # Set higher than number of files
303
- )
283
+ commit_loom .analyzer .config .max_files_threshold = 5 # Set higher than number of files
304
284
commit_loom .ai_service .generate_commit_message .return_value = (
305
285
CommitSuggestion (
306
286
title = "test commit" ,
@@ -321,11 +301,11 @@ def test_run_commit_error(mock_console, commit_loom):
321
301
@patch ("commitloom.cli.main.console" )
322
302
def test_run_with_exception (mock_console , commit_loom ):
323
303
"""Test run when an exception occurs."""
324
- commit_loom .git .get_changed_files .side_effect = GitError ("Test error" )
304
+ commit_loom .git .get_staged_files .side_effect = GitError ("Test error" )
325
305
326
306
commit_loom .run ()
327
307
328
- mock_console .print_error .assert_called_with ("An error occurred: Test error" )
308
+ mock_console .print_error .assert_called_with ("An unexpected error occurred: Test error" )
329
309
330
310
331
311
def test_cli_arguments ():
@@ -394,7 +374,7 @@ def test_main_exception_verbose(mock_commit_loom, mock_console):
394
374
def test_process_files_in_batches_with_commit_error (commit_loom ):
395
375
"""Test handling of commit creation error."""
396
376
# Setup test data
397
- files = [GitFile (path = "file1.py" )]
377
+ files = [GitFile (path = "file1.py" , status = "M" )]
398
378
commit_loom .git .get_diff .return_value = "test diff"
399
379
400
380
# Mock token usage
@@ -418,26 +398,26 @@ def test_process_files_in_batches_with_commit_error(commit_loom):
418
398
# Mock create_commit to return False (indicating no changes)
419
399
commit_loom .git .create_commit .return_value = False
420
400
421
- result = commit_loom .process_files_in_batches (files , auto_commit = True )
422
- assert len ( result ) == 0
401
+ commit_loom .process_files_in_batches (files )
402
+ assert commit_loom . git . reset_staged_changes . called
423
403
424
404
425
405
def test_process_files_in_batches_with_git_error (commit_loom ):
426
406
"""Test handling of git error during batch processing."""
427
407
# Setup test data
428
- files = [GitFile (path = "file1.py" )]
408
+ files = [GitFile (path = "file1.py" , status = "M" )]
429
409
commit_loom .git .stage_files .side_effect = GitError ("Git error" )
430
410
431
- result = commit_loom .process_files_in_batches (files , auto_commit = True )
432
- assert len ( result ) == 0
411
+ commit_loom .process_files_in_batches (files )
412
+ assert commit_loom . git . reset_staged_changes . called
433
413
434
414
435
415
@patch ("subprocess.run" )
436
416
@patch ("commitloom.cli.console.confirm_action" )
437
417
def test_process_files_in_batches_user_cancel (mock_confirm , mock_run , commit_loom ):
438
418
"""Test user cancellation during batch processing."""
439
419
# Setup test data
440
- files = [GitFile (path = "file1.py" )]
420
+ files = [GitFile (path = "file1.py" , status = "M" )]
441
421
442
422
# Mock git status and ignore check
443
423
def mock_git_status (cmd , ** kwargs ):
@@ -470,34 +450,31 @@ def mock_git_status(cmd, **kwargs):
470
450
# Mock user cancellation
471
451
mock_confirm .return_value = False
472
452
473
- result = commit_loom .process_files_in_batches (files , auto_commit = False )
474
- assert len (result ) == 0
475
- commit_loom .git .reset_staged_changes .assert_called_once ()
453
+ commit_loom .process_files_in_batches (files )
454
+ assert commit_loom .git .reset_staged_changes .called
476
455
477
456
478
457
def test_process_files_in_batches_empty_input (commit_loom ):
479
458
"""Test processing with no files."""
480
- result = commit_loom .process_files_in_batches ([], auto_commit = True )
481
- assert len ( result ) == 0
459
+ commit_loom .process_files_in_batches ([])
460
+ assert not commit_loom . git . create_commit . called
482
461
483
462
484
463
@patch ("subprocess.run" )
485
464
def test_create_batches_with_invalid_files (mock_run , commit_loom ):
486
465
"""Test batch creation with invalid files."""
487
466
# Mock git status to return empty for invalid file
488
- mock_run .return_value = MagicMock (
489
- stdout = "" ,
490
- returncode = 0
491
- )
467
+ mock_run .return_value = MagicMock (stdout = "" , returncode = 0 )
492
468
493
- files = [GitFile (path = "invalid.py" )]
469
+ files = [GitFile (path = "invalid.py" , status = "M" )]
494
470
batches = commit_loom ._create_batches (files )
495
471
assert len (batches ) == 0
496
472
497
473
498
474
@patch ("subprocess.run" )
499
475
def test_create_batches_with_mixed_files (mock_run , commit_loom ):
500
476
"""Test batch creation with mix of valid and invalid files."""
477
+
501
478
def mock_git_status (cmd , ** kwargs ):
502
479
if "status" in cmd and "--porcelain" in cmd :
503
480
# Return a properly formatted git status output with a modified file
@@ -507,10 +484,7 @@ def mock_git_status(cmd, **kwargs):
507
484
mock_run .side_effect = mock_git_status
508
485
commit_loom .git .should_ignore_file .side_effect = lambda x : x == "invalid.py"
509
486
510
- files = [
511
- GitFile (path = "valid.py" ),
512
- GitFile (path = "invalid.py" )
513
- ]
487
+ files = [GitFile (path = "valid.py" , status = "M" ), GitFile (path = "invalid.py" , status = "M" )]
514
488
batches = commit_loom ._create_batches (files )
515
489
assert len (batches ) == 1
516
490
assert len (batches [0 ]) == 1
@@ -522,6 +496,6 @@ def test_create_batches_with_git_error(mock_run, commit_loom):
522
496
"""Test batch creation when git command fails."""
523
497
mock_run .side_effect = subprocess .CalledProcessError (1 , "git status" , stderr = b"error" )
524
498
525
- files = [GitFile (path = "file.py" )]
499
+ files = [GitFile (path = "file.py" , status = "M" )]
526
500
batches = commit_loom ._create_batches (files )
527
501
assert len (batches ) == 0
0 commit comments