fancygit 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fancygit-1.0.0.dist-info/METADATA +169 -0
- fancygit-1.0.0.dist-info/RECORD +29 -0
- fancygit-1.0.0.dist-info/WHEEL +5 -0
- fancygit-1.0.0.dist-info/entry_points.txt +2 -0
- fancygit-1.0.0.dist-info/top_level.txt +2 -0
- src/__init__.py +1 -0
- src/colors.py +260 -0
- src/git_error.py +55 -0
- src/git_error_parser.py +43 -0
- src/git_insights.py +304 -0
- src/git_runner.py +20 -0
- src/loading_animation.py +167 -0
- src/merge_conflict.py +27 -0
- src/mermaid_export.py +430 -0
- src/ollama_client.py +142 -0
- src/output_colorizer.py +358 -0
- src/repo_state.py +29 -0
- src/utils.py +0 -0
- tests/README.md +186 -0
- tests/__init__.py +0 -0
- tests/conftest.py +61 -0
- tests/test_conflict_parser_integration.py +65 -0
- tests/test_fancygit_advanced.py +504 -0
- tests/test_fancygit_commands.py +507 -0
- tests/test_fancygit_integration.py +158 -0
- tests/test_fancygit_workflows.py +441 -0
- tests/test_git_error.py +74 -0
- tests/test_git_error_parser.py +129 -0
- tests/test_git_runner.py +118 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
import tempfile
|
|
3
|
+
import os
|
|
4
|
+
import shutil
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from unittest.mock import patch
|
|
8
|
+
|
|
9
|
+
# Add project root to path for imports
|
|
10
|
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
|
|
11
|
+
|
|
12
|
+
from fancygit import FancyGit
|
|
13
|
+
|
|
14
|
+
@pytest.mark.integration
|
|
15
|
+
@pytest.mark.slow
|
|
16
|
+
class TestConflictParserIntegration:
|
|
17
|
+
"""Integration tests for conflict parser functionality"""
|
|
18
|
+
|
|
19
|
+
def setup_method(self):
|
|
20
|
+
"""Setup method called before each test"""
|
|
21
|
+
# Initialize FancyGit with mocked config
|
|
22
|
+
with patch.object(FancyGit, '_load_commands', return_value=['add', 'commit', 'push', 'pull']):
|
|
23
|
+
with patch.object(FancyGit, '_load_confirmation_state', return_value=True):
|
|
24
|
+
with patch.object(FancyGit, '_load_ai_analysis_state', return_value=False):
|
|
25
|
+
with patch.object(FancyGit, '_load_animation_type', return_value='simple'):
|
|
26
|
+
self.fancy_git = FancyGit()
|
|
27
|
+
|
|
28
|
+
def test_conflict_parser_no_conflicts(self, temp_git_repo):
|
|
29
|
+
"""Test conflict parser when no conflicts exist"""
|
|
30
|
+
conflicts = self.fancy_git.conflict_parser()
|
|
31
|
+
|
|
32
|
+
# Should return empty list when no conflicts
|
|
33
|
+
assert isinstance(conflicts, list)
|
|
34
|
+
assert len(conflicts) == 0
|
|
35
|
+
|
|
36
|
+
def test_conflict_parser_method_exists(self):
|
|
37
|
+
"""Test that conflict_parser method exists and is callable"""
|
|
38
|
+
assert hasattr(self.fancy_git, 'conflict_parser')
|
|
39
|
+
assert callable(self.fancy_git.conflict_parser)
|
|
40
|
+
|
|
41
|
+
# Should return a list
|
|
42
|
+
result = self.fancy_git.conflict_parser()
|
|
43
|
+
assert isinstance(result, list)
|
|
44
|
+
|
|
45
|
+
def test_get_repo_state_integration(self, temp_git_repo):
|
|
46
|
+
"""Test repository state functionality"""
|
|
47
|
+
repo_state = self.fancy_git.get_repo_state()
|
|
48
|
+
|
|
49
|
+
# Should return a dictionary with expected keys
|
|
50
|
+
assert isinstance(repo_state, dict)
|
|
51
|
+
expected_keys = ['branch', 'clean', 'staged', 'modified', 'untracked', 'conflicts', 'ahead', 'behind']
|
|
52
|
+
for key in expected_keys:
|
|
53
|
+
assert key in repo_state
|
|
54
|
+
|
|
55
|
+
# In a clean temp repo, should have no conflicts
|
|
56
|
+
assert isinstance(repo_state['conflicts'], list)
|
|
57
|
+
assert len(repo_state['conflicts']) == 0
|
|
58
|
+
|
|
59
|
+
# The repo should be relatively clean (may have untracked files on some platforms)
|
|
60
|
+
# Allow for platform differences in what "clean" means
|
|
61
|
+
assert isinstance(repo_state['clean'], bool)
|
|
62
|
+
# If not clean, it should be due to untracked files, not real changes
|
|
63
|
+
if not repo_state['clean']:
|
|
64
|
+
assert len(repo_state['staged']) == 0
|
|
65
|
+
assert len(repo_state['modified']) == 0
|
|
@@ -0,0 +1,504 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from unittest.mock import patch, MagicMock
|
|
3
|
+
import sys
|
|
4
|
+
import os
|
|
5
|
+
|
|
6
|
+
# Add project root to path for imports
|
|
7
|
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
|
|
8
|
+
|
|
9
|
+
from fancygit import FancyGit
|
|
10
|
+
|
|
11
|
+
@pytest.mark.unit
|
|
12
|
+
class TestFancyGitAdvancedCommands:
|
|
13
|
+
"""Test cases for advanced git commands"""
|
|
14
|
+
|
|
15
|
+
def setup_method(self):
|
|
16
|
+
"""Setup method called before each test"""
|
|
17
|
+
with patch.object(FancyGit, '_load_commands', return_value=[
|
|
18
|
+
'archive', 'bisect', 'bundle', 'cherry-pick', 'clean', 'clone', 'describe',
|
|
19
|
+
'fetch', 'format-patch', 'gc', 'grep', 'gui', 'init', 'maintenance',
|
|
20
|
+
'notes', 'range-diff', 'restore', 'revert', 'shortlog', 'show',
|
|
21
|
+
'sparse-checkout', 'submodule', 'switch', 'tag', 'worktree', 'log', 'status'
|
|
22
|
+
]):
|
|
23
|
+
with patch.object(FancyGit, '_load_confirmation_state', return_value=False):
|
|
24
|
+
with patch.object(FancyGit, '_load_ai_analysis_state', return_value=False):
|
|
25
|
+
with patch.object(FancyGit, '_load_output_coloring_state', return_value=False):
|
|
26
|
+
with patch.object(FancyGit, '_load_animation_type', return_value='dots'):
|
|
27
|
+
self.fancy_git = FancyGit()
|
|
28
|
+
|
|
29
|
+
@patch('src.git_runner.GitRunner.run_git_command')
|
|
30
|
+
def test_archive_command(self, mock_run_git):
|
|
31
|
+
"""Test git archive command"""
|
|
32
|
+
mock_run_git.return_value = (0, "", "")
|
|
33
|
+
|
|
34
|
+
result = self.fancy_git.execute_command('archive', 'HEAD', '--format=zip', '--output=archive.zip')
|
|
35
|
+
|
|
36
|
+
assert result is True
|
|
37
|
+
mock_run_git.assert_called_once_with(['archive', 'HEAD', '--format=zip', '--output=archive.zip'])
|
|
38
|
+
|
|
39
|
+
@patch('src.git_runner.GitRunner.run_git_command')
|
|
40
|
+
def test_bisect_command(self, mock_run_git):
|
|
41
|
+
"""Test git bisect command"""
|
|
42
|
+
mock_run_git.return_value = (0, "Bisecting: 0 revisions left to test after this (roughly 0 steps)", "")
|
|
43
|
+
|
|
44
|
+
result = self.fancy_git.execute_command('bisect', 'start')
|
|
45
|
+
|
|
46
|
+
assert result is True
|
|
47
|
+
mock_run_git.assert_called_once_with(['bisect', 'start'])
|
|
48
|
+
|
|
49
|
+
@patch('src.git_runner.GitRunner.run_git_command')
|
|
50
|
+
def test_bundle_command(self, mock_run_git):
|
|
51
|
+
"""Test git bundle command"""
|
|
52
|
+
mock_run_git.return_value = (0, "Creating bundle file repo.bundle", "")
|
|
53
|
+
|
|
54
|
+
result = self.fancy_git.execute_command('bundle', 'create', 'repo.bundle', 'main')
|
|
55
|
+
|
|
56
|
+
assert result is True
|
|
57
|
+
mock_run_git.assert_called_once_with(['bundle', 'create', 'repo.bundle', 'main'])
|
|
58
|
+
|
|
59
|
+
@patch('src.git_runner.GitRunner.run_git_command')
|
|
60
|
+
def test_cherry_pick_command(self, mock_run_git):
|
|
61
|
+
"""Test git cherry-pick command"""
|
|
62
|
+
mock_run_git.return_value = (0, "[main 1234567] Cherry picked commit from feature-branch\n 1 file changed, 1 insertion(+)", "")
|
|
63
|
+
|
|
64
|
+
result = self.fancy_git.execute_command('cherry-pick', 'abcdef123')
|
|
65
|
+
|
|
66
|
+
assert result is True
|
|
67
|
+
mock_run_git.assert_called_once_with(['cherry-pick', 'abcdef123'])
|
|
68
|
+
|
|
69
|
+
@patch('src.git_runner.GitRunner.run_git_command')
|
|
70
|
+
def test_clean_command(self, mock_run_git):
|
|
71
|
+
"""Test git clean command"""
|
|
72
|
+
mock_run_git.return_value = (0, "Removing test.txt", "")
|
|
73
|
+
|
|
74
|
+
result = self.fancy_git.execute_command('clean', '-f')
|
|
75
|
+
|
|
76
|
+
assert result is True
|
|
77
|
+
mock_run_git.assert_called_once_with(['clean', '-f'])
|
|
78
|
+
|
|
79
|
+
@patch('src.git_runner.GitRunner.run_git_command')
|
|
80
|
+
def test_clone_command(self, mock_run_git):
|
|
81
|
+
"""Test git clone command"""
|
|
82
|
+
mock_run_git.return_value = (0, "Cloning into 'repo'...\nremote: Enumerating objects: 100, done.", "")
|
|
83
|
+
|
|
84
|
+
result = self.fancy_git.execute_command('clone', 'https://github.com/user/repo.git')
|
|
85
|
+
|
|
86
|
+
assert result is True
|
|
87
|
+
mock_run_git.assert_called_once_with(['clone', 'https://github.com/user/repo.git'])
|
|
88
|
+
|
|
89
|
+
@patch('src.git_runner.GitRunner.run_git_command')
|
|
90
|
+
def test_describe_command(self, mock_run_git):
|
|
91
|
+
"""Test git describe command"""
|
|
92
|
+
mock_run_git.return_value = (0, "v1.0.0-5-g1234567", "")
|
|
93
|
+
|
|
94
|
+
result = self.fancy_git.execute_command('describe', '--tags')
|
|
95
|
+
|
|
96
|
+
assert result is True
|
|
97
|
+
mock_run_git.assert_called_once_with(['describe', '--tags'])
|
|
98
|
+
|
|
99
|
+
@patch('src.git_runner.GitRunner.run_git_command')
|
|
100
|
+
def test_fetch_command(self, mock_run_git):
|
|
101
|
+
"""Test git fetch command"""
|
|
102
|
+
mock_run_git.return_value = (0, "From github.com:user/repo\n * branch main -> FETCH_HEAD", "")
|
|
103
|
+
|
|
104
|
+
result = self.fancy_git.execute_command('fetch', 'origin')
|
|
105
|
+
|
|
106
|
+
assert result is True
|
|
107
|
+
mock_run_git.assert_called_once_with(['fetch', 'origin'])
|
|
108
|
+
|
|
109
|
+
@patch('src.git_runner.GitRunner.run_git_command')
|
|
110
|
+
def test_format_patch_command(self, mock_run_git):
|
|
111
|
+
"""Test git format-patch command"""
|
|
112
|
+
mock_run_git.return_value = (0, "0001-First-commit.patch\n0002-Second-commit.patch", "")
|
|
113
|
+
|
|
114
|
+
result = self.fancy_git.execute_command('format-patch', 'HEAD~2')
|
|
115
|
+
|
|
116
|
+
assert result is True
|
|
117
|
+
mock_run_git.assert_called_once_with(['format-patch', 'HEAD~2'])
|
|
118
|
+
|
|
119
|
+
@patch('src.git_runner.GitRunner.run_git_command')
|
|
120
|
+
def test_gc_command(self, mock_run_git):
|
|
121
|
+
"""Test git gc command"""
|
|
122
|
+
mock_run_git.return_value = (0, "Counting objects: 100, done.\nCompressing objects: 100% (50/50), done.", "")
|
|
123
|
+
|
|
124
|
+
result = self.fancy_git.execute_command('gc', '--aggressive')
|
|
125
|
+
|
|
126
|
+
assert result is True
|
|
127
|
+
mock_run_git.assert_called_once_with(['gc', '--aggressive'])
|
|
128
|
+
|
|
129
|
+
@patch('src.git_runner.GitRunner.run_git_command')
|
|
130
|
+
def test_grep_command(self, mock_run_git):
|
|
131
|
+
"""Test git grep command"""
|
|
132
|
+
mock_run_git.return_value = (0, "src/main.py:10:print('Hello World')", "")
|
|
133
|
+
|
|
134
|
+
result = self.fancy_git.execute_command('grep', 'Hello', '*.py')
|
|
135
|
+
|
|
136
|
+
assert result is True
|
|
137
|
+
mock_run_git.assert_called_once_with(['grep', 'Hello', '*.py'])
|
|
138
|
+
|
|
139
|
+
@patch('src.git_runner.GitRunner.run_git_command')
|
|
140
|
+
def test_gui_command(self, mock_run_git):
|
|
141
|
+
"""Test git gui command"""
|
|
142
|
+
mock_run_git.return_value = (0, "", "")
|
|
143
|
+
|
|
144
|
+
result = self.fancy_git.execute_command('gui')
|
|
145
|
+
|
|
146
|
+
assert result is True
|
|
147
|
+
mock_run_git.assert_called_once_with(['gui'])
|
|
148
|
+
|
|
149
|
+
@patch('src.git_runner.GitRunner.run_git_command')
|
|
150
|
+
def test_init_command(self, mock_run_git):
|
|
151
|
+
"""Test git init command"""
|
|
152
|
+
mock_run_git.return_value = (0, "Initialized empty Git repository in /path/to/repo/.git/", "")
|
|
153
|
+
|
|
154
|
+
result = self.fancy_git.execute_command('init')
|
|
155
|
+
|
|
156
|
+
assert result is True
|
|
157
|
+
mock_run_git.assert_called_once_with(['init'])
|
|
158
|
+
|
|
159
|
+
@patch('src.git_runner.GitRunner.run_git_command')
|
|
160
|
+
def test_maintenance_command(self, mock_run_git):
|
|
161
|
+
"""Test git maintenance command"""
|
|
162
|
+
mock_run_git.return_value = (0, "Task 'gc' scheduled for next run", "")
|
|
163
|
+
|
|
164
|
+
result = self.fancy_git.execute_command('maintenance', 'start')
|
|
165
|
+
|
|
166
|
+
assert result is True
|
|
167
|
+
mock_run_git.assert_called_once_with(['maintenance', 'start'])
|
|
168
|
+
|
|
169
|
+
@patch('src.git_runner.GitRunner.run_git_command')
|
|
170
|
+
def test_notes_command(self, mock_run_git):
|
|
171
|
+
"""Test git notes command"""
|
|
172
|
+
mock_run_git.return_value = (0, "Notes added to commit 1234567", "")
|
|
173
|
+
|
|
174
|
+
result = self.fancy_git.execute_command('notes', 'add', '-m', 'Important note', 'HEAD')
|
|
175
|
+
|
|
176
|
+
assert result is True
|
|
177
|
+
mock_run_git.assert_called_once_with(['notes', 'add', '-m', 'Important note', 'HEAD'])
|
|
178
|
+
|
|
179
|
+
@patch('src.git_runner.GitRunner.run_git_command')
|
|
180
|
+
def test_range_diff_command(self, mock_run_git):
|
|
181
|
+
"""Test git range-diff command"""
|
|
182
|
+
mock_run_git.return_value = (0, "1: abcdef = 1: 123456 First commit\n2: ghijkl ! 2: 7890ab Second commit", "")
|
|
183
|
+
|
|
184
|
+
result = self.fancy_git.execute_command('range-diff', 'main..feature', 'origin/main..origin/feature')
|
|
185
|
+
|
|
186
|
+
assert result is True
|
|
187
|
+
mock_run_git.assert_called_once_with(['range-diff', 'main..feature', 'origin/main..origin/feature'])
|
|
188
|
+
|
|
189
|
+
@patch('src.git_runner.GitRunner.run_git_command')
|
|
190
|
+
def test_restore_command(self, mock_run_git):
|
|
191
|
+
"""Test git restore command"""
|
|
192
|
+
mock_run_git.return_value = (0, "", "")
|
|
193
|
+
|
|
194
|
+
result = self.fancy_git.execute_command('restore', 'test.txt')
|
|
195
|
+
|
|
196
|
+
assert result is True
|
|
197
|
+
mock_run_git.assert_called_once_with(['restore', 'test.txt'])
|
|
198
|
+
|
|
199
|
+
@patch('src.git_runner.GitRunner.run_git_command')
|
|
200
|
+
def test_shortlog_command(self, mock_run_git):
|
|
201
|
+
"""Test git shortlog command"""
|
|
202
|
+
mock_run_git.return_value = (0, "Alice (5):\n Commit 1\n Commit 2\nBob (3):\n Commit 3", "")
|
|
203
|
+
|
|
204
|
+
result = self.fancy_git.execute_command('shortlog', '-s')
|
|
205
|
+
|
|
206
|
+
assert result is True
|
|
207
|
+
mock_run_git.assert_called_once_with(['shortlog', '-s'])
|
|
208
|
+
|
|
209
|
+
@patch('src.git_runner.GitRunner.run_git_command')
|
|
210
|
+
def test_show_command(self, mock_run_git):
|
|
211
|
+
"""Test git show command"""
|
|
212
|
+
mock_run_git.return_value = (0, "commit 1234567890abcdef\nAuthor: Alice <alice@example.com>\n\n Test commit\n\ndiff --git a/test.txt b/test.txt", "")
|
|
213
|
+
|
|
214
|
+
result = self.fancy_git.execute_command('show', 'HEAD')
|
|
215
|
+
|
|
216
|
+
assert result is True
|
|
217
|
+
mock_run_git.assert_called_once_with(['show', 'HEAD'])
|
|
218
|
+
|
|
219
|
+
@patch('src.git_runner.GitRunner.run_git_command')
|
|
220
|
+
def test_sparse_checkout_command(self, mock_run_git):
|
|
221
|
+
"""Test git sparse-checkout command"""
|
|
222
|
+
mock_run_git.return_value = (0, "", "")
|
|
223
|
+
|
|
224
|
+
result = self.fancy_git.execute_command('sparse-checkout', 'init', '--cone')
|
|
225
|
+
|
|
226
|
+
assert result is True
|
|
227
|
+
mock_run_git.assert_called_once_with(['sparse-checkout', 'init', '--cone'])
|
|
228
|
+
|
|
229
|
+
@patch('src.git_runner.GitRunner.run_git_command')
|
|
230
|
+
def test_submodule_command(self, mock_run_git):
|
|
231
|
+
"""Test git submodule command"""
|
|
232
|
+
mock_run_git.return_value = (0, "Cloning into 'external/lib'\nSubmodule path 'external/lib': checked out '1234567890abcdef'", "")
|
|
233
|
+
|
|
234
|
+
result = self.fancy_git.execute_command('submodule', 'update', '--init')
|
|
235
|
+
|
|
236
|
+
assert result is True
|
|
237
|
+
mock_run_git.assert_called_once_with(['submodule', 'update', '--init'])
|
|
238
|
+
|
|
239
|
+
@patch('src.git_runner.GitRunner.run_git_command')
|
|
240
|
+
def test_switch_command(self, mock_run_git):
|
|
241
|
+
"""Test git switch command"""
|
|
242
|
+
mock_run_git.return_value = (0, "Switched to branch 'feature-branch'", "")
|
|
243
|
+
|
|
244
|
+
result = self.fancy_git.execute_command('switch', 'feature-branch')
|
|
245
|
+
|
|
246
|
+
assert result is True
|
|
247
|
+
mock_run_git.assert_called_once_with(['switch', 'feature-branch'])
|
|
248
|
+
|
|
249
|
+
@patch('src.git_runner.GitRunner.run_git_command')
|
|
250
|
+
def test_tag_command(self, mock_run_git):
|
|
251
|
+
"""Test git tag command"""
|
|
252
|
+
mock_run_git.return_value = (0, "", "")
|
|
253
|
+
|
|
254
|
+
result = self.fancy_git.execute_command('tag', '-a', 'v1.0.0', '-m', 'Version 1.0.0')
|
|
255
|
+
|
|
256
|
+
assert result is True
|
|
257
|
+
mock_run_git.assert_called_once_with(['tag', '-a', 'v1.0.0', '-m', 'Version 1.0.0'])
|
|
258
|
+
|
|
259
|
+
@patch('src.git_runner.GitRunner.run_git_command')
|
|
260
|
+
def test_worktree_command(self, mock_run_git):
|
|
261
|
+
"""Test git worktree command"""
|
|
262
|
+
mock_run_git.return_value = (0, "Preparing worktree (checking out 'feature-branch')\nBranch 'feature-branch' set up to track local branch 'feature-branch'.", "")
|
|
263
|
+
|
|
264
|
+
result = self.fancy_git.execute_command('worktree', 'add', '../feature-worktree', 'feature-branch')
|
|
265
|
+
|
|
266
|
+
assert result is True
|
|
267
|
+
mock_run_git.assert_called_once_with(['worktree', 'add', '../feature-worktree', 'feature-branch'])
|
|
268
|
+
|
|
269
|
+
@pytest.mark.unit
|
|
270
|
+
class TestFancyGitCommandEdgeCases:
|
|
271
|
+
"""Test cases for edge cases and error scenarios"""
|
|
272
|
+
|
|
273
|
+
def setup_method(self):
|
|
274
|
+
"""Setup method called before each test"""
|
|
275
|
+
with patch.object(FancyGit, '_load_commands', return_value=['add', 'commit', 'push', 'pull', 'status', 'log', 'show']):
|
|
276
|
+
with patch.object(FancyGit, '_load_confirmation_state', return_value=False):
|
|
277
|
+
with patch.object(FancyGit, '_load_ai_analysis_state', return_value=False):
|
|
278
|
+
with patch.object(FancyGit, '_load_output_coloring_state', return_value=False):
|
|
279
|
+
with patch.object(FancyGit, '_load_animation_type', return_value='dots'):
|
|
280
|
+
self.fancy_git = FancyGit()
|
|
281
|
+
|
|
282
|
+
@patch('src.git_runner.GitRunner.run_git_command')
|
|
283
|
+
def test_command_with_large_output(self, mock_run_git):
|
|
284
|
+
"""Test command that produces large output"""
|
|
285
|
+
large_output = "commit " + "x" * 10000 + "\n" + "Author: Test User <test@example.com>\n" + "Date: Mon Jan 1 12:00:00 2024\n\n Large commit message"
|
|
286
|
+
mock_run_git.return_value = (0, large_output, "")
|
|
287
|
+
|
|
288
|
+
result = self.fancy_git.execute_command('log', '-1', '--pretty=fuller')
|
|
289
|
+
|
|
290
|
+
# Command succeeds but may have warnings due to output processing
|
|
291
|
+
assert isinstance(result, bool)
|
|
292
|
+
mock_run_git.assert_called_once_with(['log', '-1', '--pretty=fuller'])
|
|
293
|
+
|
|
294
|
+
@patch('src.git_runner.GitRunner.run_git_command')
|
|
295
|
+
def test_command_with_binary_output(self, mock_run_git):
|
|
296
|
+
"""Test command that might produce binary output"""
|
|
297
|
+
mock_run_git.return_value = (0, b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR'.decode('latin1'), "")
|
|
298
|
+
|
|
299
|
+
result = self.fancy_git.execute_command('show', 'HEAD:image.png')
|
|
300
|
+
|
|
301
|
+
assert result is True
|
|
302
|
+
mock_run_git.assert_called_once_with(['show', 'HEAD:image.png'])
|
|
303
|
+
|
|
304
|
+
@patch('src.git_runner.GitRunner.run_git_command')
|
|
305
|
+
def test_command_with_newline_characters(self, mock_run_git):
|
|
306
|
+
"""Test command with various newline characters"""
|
|
307
|
+
output_with_newlines = "Line 1\r\nLine 2\nLine 3\r\n"
|
|
308
|
+
mock_run_git.return_value = (0, output_with_newlines, "")
|
|
309
|
+
|
|
310
|
+
result = self.fancy_git.execute_command('log', '--oneline')
|
|
311
|
+
|
|
312
|
+
assert result is True
|
|
313
|
+
mock_run_git.assert_called_once_with(['log', '--oneline'])
|
|
314
|
+
|
|
315
|
+
@patch('src.git_runner.GitRunner.run_git_command')
|
|
316
|
+
def test_command_with_unicode_characters(self, mock_run_git):
|
|
317
|
+
"""Test command with unicode characters in output"""
|
|
318
|
+
unicode_output = "commit 1234567890abcdef\nAuthor: José García <jose@example.com>\nDate: Mon Jan 1 12:00:00 2024\n\n Add 🚀 emoji support and fix ñ issue"
|
|
319
|
+
mock_run_git.return_value = (0, unicode_output, "")
|
|
320
|
+
|
|
321
|
+
result = self.fancy_git.execute_command('log', '-1')
|
|
322
|
+
|
|
323
|
+
assert result is True
|
|
324
|
+
mock_run_git.assert_called_once_with(['log', '-1'])
|
|
325
|
+
|
|
326
|
+
@patch('src.git_runner.GitRunner.run_git_command')
|
|
327
|
+
def test_command_with_special_shell_characters(self, mock_run_git):
|
|
328
|
+
"""Test command with special shell characters in arguments"""
|
|
329
|
+
mock_run_git.return_value = (0, "Commit successful", "")
|
|
330
|
+
|
|
331
|
+
result = self.fancy_git.execute_command('commit', '-m', 'Fix $PATH & update "config.json" file; rm -rf /tmp/*')
|
|
332
|
+
|
|
333
|
+
assert result is True
|
|
334
|
+
mock_run_git.assert_called_once_with(['commit', '-m', 'Fix $PATH & update "config.json" file; rm -rf /tmp/*'])
|
|
335
|
+
|
|
336
|
+
@patch('src.git_runner.GitRunner.run_git_command')
|
|
337
|
+
def test_command_with_very_long_arguments(self, mock_run_git):
|
|
338
|
+
"""Test command with very long arguments"""
|
|
339
|
+
long_message = "A" * 1000 # 1000 character message
|
|
340
|
+
mock_run_git.return_value = (0, "Commit successful", "")
|
|
341
|
+
|
|
342
|
+
result = self.fancy_git.execute_command('commit', '-m', long_message)
|
|
343
|
+
|
|
344
|
+
assert result is True
|
|
345
|
+
mock_run_git.assert_called_once_with(['commit', '-m', long_message])
|
|
346
|
+
|
|
347
|
+
@patch('src.git_runner.GitRunner.run_git_command')
|
|
348
|
+
def test_command_return_code_variations(self, mock_run_git):
|
|
349
|
+
"""Test commands with different return codes"""
|
|
350
|
+
test_cases = [
|
|
351
|
+
(0, True), # Success
|
|
352
|
+
(1, False), # General error - use a real error pattern
|
|
353
|
+
(128, False), # Fatal error - use a real error pattern
|
|
354
|
+
(255, False), # Signal termination - use a real error pattern
|
|
355
|
+
]
|
|
356
|
+
|
|
357
|
+
for return_code, expected_result in test_cases:
|
|
358
|
+
if return_code == 0:
|
|
359
|
+
mock_run_git.return_value = (0, "Success", "")
|
|
360
|
+
else:
|
|
361
|
+
mock_run_git.return_value = (return_code, "", "error: fatal error occurred")
|
|
362
|
+
|
|
363
|
+
result = self.fancy_git.execute_command('status')
|
|
364
|
+
|
|
365
|
+
# For return code 0 (success), the result should be True
|
|
366
|
+
# For error codes, the result should be False
|
|
367
|
+
if return_code == 0:
|
|
368
|
+
assert result is True
|
|
369
|
+
else:
|
|
370
|
+
assert result is False
|
|
371
|
+
|
|
372
|
+
@patch('src.git_runner.GitRunner.run_git_command')
|
|
373
|
+
def test_command_with_stderr_only(self, mock_run_git):
|
|
374
|
+
"""Test command that outputs only to stderr"""
|
|
375
|
+
mock_run_git.return_value = (0, "", "warning: This is a warning message")
|
|
376
|
+
|
|
377
|
+
result = self.fancy_git.execute_command('status')
|
|
378
|
+
|
|
379
|
+
# Command succeeds but may show warnings
|
|
380
|
+
assert isinstance(result, bool)
|
|
381
|
+
mock_run_git.assert_called_once_with(['status'])
|
|
382
|
+
|
|
383
|
+
@patch('src.git_runner.GitRunner.run_git_command')
|
|
384
|
+
def test_command_with_mixed_output(self, mock_run_git):
|
|
385
|
+
"""Test command with mixed stdout and stderr output"""
|
|
386
|
+
mock_run_git.return_value = (0, "On branch main\nnothing to commit", "warning: LF will be replaced by CRLF")
|
|
387
|
+
|
|
388
|
+
result = self.fancy_git.execute_command('status')
|
|
389
|
+
|
|
390
|
+
# Command succeeds but may show warnings
|
|
391
|
+
assert isinstance(result, bool)
|
|
392
|
+
mock_run_git.assert_called_once_with(['status'])
|
|
393
|
+
|
|
394
|
+
@patch('src.git_runner.GitRunner.run_git_command')
|
|
395
|
+
def test_command_timeout_simulation(self, mock_run_git):
|
|
396
|
+
"""Test command timeout simulation"""
|
|
397
|
+
mock_run_git.return_value = (1, "", "fatal: the remote end hung up unexpectedly")
|
|
398
|
+
|
|
399
|
+
result = self.fancy_git.execute_command('push', 'origin', 'main')
|
|
400
|
+
|
|
401
|
+
assert result is False
|
|
402
|
+
mock_run_git.assert_called_once_with(['push', 'origin', 'main'])
|
|
403
|
+
|
|
404
|
+
@patch('src.git_runner.GitRunner.run_git_command')
|
|
405
|
+
def test_command_with_empty_arguments(self, mock_run_git):
|
|
406
|
+
"""Test command with empty string arguments"""
|
|
407
|
+
mock_run_git.return_value = (0, "", "")
|
|
408
|
+
|
|
409
|
+
result = self.fancy_git.execute_command('commit', '-m', '')
|
|
410
|
+
|
|
411
|
+
assert result is True
|
|
412
|
+
mock_run_git.assert_called_once_with(['commit', '-m', ''])
|
|
413
|
+
|
|
414
|
+
@patch('src.git_runner.GitRunner.run_git_command')
|
|
415
|
+
def test_command_with_whitespace_arguments(self, mock_run_git):
|
|
416
|
+
"""Test command with whitespace-only arguments"""
|
|
417
|
+
mock_run_git.return_value = (0, "", "")
|
|
418
|
+
|
|
419
|
+
result = self.fancy_git.execute_command('commit', '-m', ' \t\n ')
|
|
420
|
+
|
|
421
|
+
assert result is True
|
|
422
|
+
mock_run_git.assert_called_once_with(['commit', '-m', ' \t\n '])
|
|
423
|
+
|
|
424
|
+
@pytest.mark.unit
|
|
425
|
+
class TestFancyGitCommandPerformance:
|
|
426
|
+
"""Test cases for command performance and optimization"""
|
|
427
|
+
|
|
428
|
+
def setup_method(self):
|
|
429
|
+
"""Setup method called before each test"""
|
|
430
|
+
with patch.object(FancyGit, '_load_commands', return_value=['add', 'commit', 'push', 'pull', 'status', 'log', 'branch', 'diff']):
|
|
431
|
+
with patch.object(FancyGit, '_load_confirmation_state', return_value=False):
|
|
432
|
+
with patch.object(FancyGit, '_load_ai_analysis_state', return_value=False):
|
|
433
|
+
with patch.object(FancyGit, '_load_output_coloring_state', return_value=False):
|
|
434
|
+
with patch.object(FancyGit, '_load_animation_type', return_value='dots'):
|
|
435
|
+
self.fancy_git = FancyGit()
|
|
436
|
+
|
|
437
|
+
@patch('src.git_runner.GitRunner.run_git_command')
|
|
438
|
+
def test_rapid_command_execution(self, mock_run_git):
|
|
439
|
+
"""Test rapid execution of multiple commands"""
|
|
440
|
+
mock_run_git.return_value = (0, "Success", "")
|
|
441
|
+
|
|
442
|
+
# Execute multiple commands rapidly
|
|
443
|
+
results = []
|
|
444
|
+
for i in range(10):
|
|
445
|
+
result = self.fancy_git.execute_command('status')
|
|
446
|
+
results.append(result)
|
|
447
|
+
|
|
448
|
+
# All commands should succeed
|
|
449
|
+
assert all(results)
|
|
450
|
+
assert mock_run_git.call_count == 10
|
|
451
|
+
|
|
452
|
+
@patch('src.git_runner.GitRunner.run_git_command')
|
|
453
|
+
def test_command_execution_with_different_speeds(self, mock_run_git):
|
|
454
|
+
"""Test commands with different execution speeds"""
|
|
455
|
+
import time
|
|
456
|
+
|
|
457
|
+
# Mock different return values for different calls
|
|
458
|
+
mock_run_git.side_effect = [
|
|
459
|
+
(0, "Slow command result", ""),
|
|
460
|
+
(0, "Fast command result", ""),
|
|
461
|
+
(0, "Slow command result", ""),
|
|
462
|
+
(0, "Fast command result", "")
|
|
463
|
+
]
|
|
464
|
+
|
|
465
|
+
start_time = time.time()
|
|
466
|
+
|
|
467
|
+
result1 = self.fancy_git.execute_command('status')
|
|
468
|
+
result2 = self.fancy_git.execute_command('log', '-1')
|
|
469
|
+
result3 = self.fancy_git.execute_command('branch')
|
|
470
|
+
result4 = self.fancy_git.execute_command('diff')
|
|
471
|
+
|
|
472
|
+
end_time = time.time()
|
|
473
|
+
|
|
474
|
+
# All commands should succeed
|
|
475
|
+
assert all([isinstance(r, bool) for r in [result1, result2, result3, result4]])
|
|
476
|
+
assert mock_run_git.call_count == 4
|
|
477
|
+
|
|
478
|
+
@patch('src.git_runner.GitRunner.run_git_command')
|
|
479
|
+
def test_memory_usage_with_large_outputs(self, mock_run_git):
|
|
480
|
+
"""Test memory usage with large command outputs"""
|
|
481
|
+
# Create a large output (simulating git log with many commits)
|
|
482
|
+
large_output = "\n".join([f"commit {i:040d}" for i in range(1000)])
|
|
483
|
+
mock_run_git.return_value = (0, large_output, "")
|
|
484
|
+
|
|
485
|
+
result = self.fancy_git.execute_command('log')
|
|
486
|
+
|
|
487
|
+
# Command succeeds but may have warnings due to large output
|
|
488
|
+
assert isinstance(result, bool)
|
|
489
|
+
mock_run_git.assert_called_once_with(['log'])
|
|
490
|
+
|
|
491
|
+
@patch('src.git_runner.GitRunner.run_git_command')
|
|
492
|
+
def test_concurrent_command_simulation(self, mock_run_git):
|
|
493
|
+
"""Test simulation of concurrent command access"""
|
|
494
|
+
mock_run_git.return_value = (0, "Success", "")
|
|
495
|
+
|
|
496
|
+
# Simulate multiple rapid calls
|
|
497
|
+
results = []
|
|
498
|
+
for i in range(5):
|
|
499
|
+
result = self.fancy_git.execute_command('status')
|
|
500
|
+
results.append(result)
|
|
501
|
+
|
|
502
|
+
# All should succeed
|
|
503
|
+
assert all(results)
|
|
504
|
+
assert mock_run_git.call_count == 5
|