forked from cyclotruc/gitingest
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtest_repository_clone.py
308 lines (246 loc) · 11 KB
/
test_repository_clone.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
"""
Tests for the `repository_clone` module.
These tests cover various scenarios for cloning repositories, verifying that the appropriate Git commands are invoked
and handling edge cases such as nonexistent URLs, timeouts, redirects, and specific commits or branches.
"""
import asyncio
from unittest.mock import AsyncMock, patch
import pytest
from gitingest.exceptions import AsyncTimeoutError
from gitingest.repository_clone import CloneConfig, _check_repo_exists, clone_repo
@pytest.mark.asyncio
async def test_clone_repo_with_commit() -> None:
"""
Test cloning a repository with a specific commit hash.
Given a valid URL and a commit hash:
When `clone_repo` is called,
Then the repository should be cloned and checked out at that commit.
"""
clone_config = CloneConfig(
url="https://github.com/user/repo",
local_path="/tmp/repo",
commit="a" * 40, # Simulating a valid commit hash
branch="main",
)
with patch("gitingest.repository_clone._check_repo_exists", return_value=True) as mock_check:
with patch("gitingest.repository_clone._run_git_command", new_callable=AsyncMock) as mock_exec:
mock_process = AsyncMock()
mock_process.communicate.return_value = (b"output", b"error")
mock_exec.return_value = mock_process
await clone_repo(clone_config)
mock_check.assert_called_once_with(clone_config.url)
assert mock_exec.call_count == 2 # Clone and checkout calls
@pytest.mark.asyncio
async def test_clone_repo_without_commit() -> None:
"""
Test cloning a repository when no commit hash is provided.
Given a valid URL and no commit hash:
When `clone_repo` is called,
Then only the clone operation should be performed (no checkout).
"""
query = CloneConfig(
url="https://github.com/user/repo",
local_path="/tmp/repo",
commit=None,
branch="main",
)
with patch("gitingest.repository_clone._check_repo_exists", return_value=True) as mock_check:
with patch("gitingest.repository_clone._run_git_command", new_callable=AsyncMock) as mock_exec:
mock_process = AsyncMock()
mock_process.communicate.return_value = (b"output", b"error")
mock_exec.return_value = mock_process
await clone_repo(query)
mock_check.assert_called_once_with(query.url)
assert mock_exec.call_count == 1 # Only clone call
@pytest.mark.asyncio
async def test_clone_repo_nonexistent_repository() -> None:
"""
Test cloning a nonexistent repository URL.
Given an invalid or nonexistent URL:
When `clone_repo` is called,
Then a ValueError should be raised with an appropriate error message.
"""
clone_config = CloneConfig(
url="https://github.com/user/nonexistent-repo",
local_path="/tmp/repo",
commit=None,
branch="main",
)
with patch("gitingest.repository_clone._check_repo_exists", return_value=False) as mock_check:
with pytest.raises(ValueError, match="Repository not found"):
await clone_repo(clone_config)
mock_check.assert_called_once_with(clone_config.url)
@pytest.mark.asyncio
@pytest.mark.parametrize(
"mock_stdout, return_code, expected",
[
(b"HTTP/1.1 200 OK\n", 0, True), # Existing repo
(b"HTTP/1.1 404 Not Found\n", 0, False), # Non-existing repo
(b"HTTP/1.1 200 OK\n", 1, False), # Failed request
],
)
async def test_check_repo_exists(mock_stdout: bytes, return_code: int, expected: bool) -> None:
"""
Test the `_check_repo_exists` function with different Git HTTP responses.
Given various stdout lines and return codes:
When `_check_repo_exists` is called,
Then it should correctly indicate whether the repository exists.
"""
url = "https://github.com/user/repo"
with patch("asyncio.create_subprocess_exec", new_callable=AsyncMock) as mock_exec:
mock_process = AsyncMock()
# Mock the subprocess output
mock_process.communicate.return_value = (mock_stdout, b"")
mock_process.returncode = return_code
mock_exec.return_value = mock_process
repo_exists = await _check_repo_exists(url)
assert repo_exists is expected
@pytest.mark.asyncio
async def test_clone_repo_invalid_url() -> None:
"""
Test cloning when the URL is invalid or empty.
Given an empty URL:
When `clone_repo` is called,
Then a ValueError should be raised with an appropriate error message.
"""
clone_config = CloneConfig(
url="",
local_path="/tmp/repo",
)
with pytest.raises(ValueError, match="The 'url' parameter is required."):
await clone_repo(clone_config)
@pytest.mark.asyncio
async def test_clone_repo_invalid_local_path() -> None:
"""
Test cloning when the local path is invalid or empty.
Given an empty local path:
When `clone_repo` is called,
Then a ValueError should be raised with an appropriate error message.
"""
clone_config = CloneConfig(
url="https://github.com/user/repo",
local_path="",
)
with pytest.raises(ValueError, match="The 'local_path' parameter is required."):
await clone_repo(clone_config)
@pytest.mark.asyncio
async def test_clone_repo_with_custom_branch() -> None:
"""
Test cloning a repository with a specified custom branch.
Given a valid URL and a branch:
When `clone_repo` is called,
Then the repository should be cloned shallowly to that branch.
"""
clone_config = CloneConfig(url="https://github.com/user/repo", local_path="/tmp/repo", branch="feature-branch")
with patch("gitingest.repository_clone._check_repo_exists", return_value=True):
with patch("gitingest.repository_clone._run_git_command", new_callable=AsyncMock) as mock_exec:
await clone_repo(clone_config)
mock_exec.assert_called_once_with(
"git",
"clone",
"--depth=1",
"--single-branch",
"--branch",
"feature-branch",
clone_config.url,
clone_config.local_path,
)
@pytest.mark.asyncio
async def test_git_command_failure() -> None:
"""
Test cloning when the Git command fails during execution.
Given a valid URL, but `_run_git_command` raises a RuntimeError:
When `clone_repo` is called,
Then a RuntimeError should be raised with the correct message.
"""
clone_config = CloneConfig(
url="https://github.com/user/repo",
local_path="/tmp/repo",
)
with patch("gitingest.repository_clone._check_repo_exists", return_value=True):
with patch("gitingest.repository_clone._run_git_command", side_effect=RuntimeError("Git command failed")):
with pytest.raises(RuntimeError, match="Git command failed"):
await clone_repo(clone_config)
@pytest.mark.asyncio
async def test_clone_repo_default_shallow_clone() -> None:
"""
Test cloning a repository with the default shallow clone options.
Given a valid URL and no branch or commit:
When `clone_repo` is called,
Then the repository should be cloned with `--depth=1` and `--single-branch`.
"""
clone_config = CloneConfig(
url="https://github.com/user/repo",
local_path="/tmp/repo",
)
with patch("gitingest.repository_clone._check_repo_exists", return_value=True):
with patch("gitingest.repository_clone._run_git_command", new_callable=AsyncMock) as mock_exec:
await clone_repo(clone_config)
mock_exec.assert_called_once_with(
"git", "clone", "--depth=1", "--single-branch", clone_config.url, clone_config.local_path
)
@pytest.mark.asyncio
async def test_clone_repo_commit_without_branch() -> None:
"""
Test cloning when a commit hash is provided but no branch is specified.
Given a valid URL and a commit hash (but no branch):
When `clone_repo` is called,
Then the repository should be cloned and checked out at that commit.
"""
clone_config = CloneConfig(
url="https://github.com/user/repo",
local_path="/tmp/repo",
commit="a" * 40, # Simulating a valid commit hash
)
with patch("gitingest.repository_clone._check_repo_exists", return_value=True):
with patch("gitingest.repository_clone._run_git_command", new_callable=AsyncMock) as mock_exec:
await clone_repo(clone_config)
assert mock_exec.call_count == 2 # Clone and checkout calls
mock_exec.assert_any_call("git", "clone", "--single-branch", clone_config.url, clone_config.local_path)
mock_exec.assert_any_call("git", "-C", clone_config.local_path, "checkout", clone_config.commit)
@pytest.mark.asyncio
async def test_check_repo_exists_with_redirect() -> None:
"""
Test `_check_repo_exists` when a redirect (302) is returned.
Given a URL that responds with "302 Found":
When `_check_repo_exists` is called,
Then it should return `False`, indicating the repo is inaccessible.
"""
url = "https://github.com/user/repo"
with patch("asyncio.create_subprocess_exec", new_callable=AsyncMock) as mock_exec:
mock_process = AsyncMock()
mock_process.communicate.return_value = (b"HTTP/1.1 302 Found\n", b"")
mock_process.returncode = 0 # Simulate successful request
mock_exec.return_value = mock_process
repo_exists = await _check_repo_exists(url)
assert repo_exists is False
@pytest.mark.asyncio
async def test_check_repo_exists_with_permanent_redirect() -> None:
"""
Test `_check_repo_exists` when a permanent redirect (301) is returned.
Given a URL that responds with "301 Found":
When `_check_repo_exists` is called,
Then it should return `True`, indicating the repo may exist at the new location.
"""
url = "https://github.com/user/repo"
with patch("asyncio.create_subprocess_exec", new_callable=AsyncMock) as mock_exec:
mock_process = AsyncMock()
mock_process.communicate.return_value = (b"HTTP/1.1 301 Found\n", b"")
mock_process.returncode = 0 # Simulate successful request
mock_exec.return_value = mock_process
repo_exists = await _check_repo_exists(url)
assert repo_exists
@pytest.mark.asyncio
async def test_clone_repo_with_timeout() -> None:
"""
Test cloning a repository when a timeout occurs.
Given a valid URL, but `_run_git_command` times out:
When `clone_repo` is called,
Then an `AsyncTimeoutError` should be raised to indicate the operation exceeded time limits.
"""
clone_config = CloneConfig(url="https://github.com/user/repo", local_path="/tmp/repo")
with patch("gitingest.repository_clone._check_repo_exists", return_value=True):
with patch("gitingest.repository_clone._run_git_command", new_callable=AsyncMock) as mock_exec:
mock_exec.side_effect = asyncio.TimeoutError
with pytest.raises(AsyncTimeoutError, match="Operation timed out after"):
await clone_repo(clone_config)