Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit ea07d37

Browse files
ci(tests): rewrite some unit tests to better work with GitPython
1 parent f411418 commit ea07d37

File tree

1 file changed

+54
-173
lines changed

1 file changed

+54
-173
lines changed

‎tests/test_clone.py

Lines changed: 54 additions & 173 deletions
Original file line numberDiff line numberDiff line change
@@ -70,22 +70,7 @@ async def test_clone_with_commit(repo_exists_true: AsyncMock, gitpython_mocks: d
7070
mock_repo.git.checkout.assert_called_with(commit_hash)
7171

7272

73-
@pytest.mark.asyncio
74-
async def test_clone_without_commit(repo_exists_true: AsyncMock, run_command_mock: AsyncMock) -> None:
75-
"""Test cloning a repository when no commit hash is provided.
7673

77-
Given a valid URL and no commit hash:
78-
When ``clone_repo`` is called,
79-
Then only the clone_repo operation should be performed (no checkout).
80-
"""
81-
expected_call_count = GIT_INSTALLED_CALLS + 4 # ensure_git_installed + resolve_commit + clone + fetch + checkout
82-
clone_config = CloneConfig(url=DEMO_URL, local_path=LOCAL_REPO_PATH, commit=None, branch="main")
83-
84-
await clone_repo(clone_config)
85-
86-
repo_exists_true.assert_any_call(clone_config.url, token=None)
87-
assert_standard_calls(run_command_mock, clone_config, commit=DEMO_COMMIT)
88-
assert run_command_mock.call_count == expected_call_count
8974

9075

9176
@pytest.mark.asyncio
@@ -133,227 +118,123 @@ async def test_check_repo_exists(status_code: int, *, expected: bool, mocker: Mo
133118
assert result is expected
134119

135120

136-
@pytest.mark.asyncio
137-
async def test_clone_with_custom_branch(run_command_mock: AsyncMock) -> None:
138-
"""Test cloning a repository with a specified custom branch.
139121

140-
Given a valid URL and a branch:
141-
When ``clone_repo`` is called,
142-
Then the repository should be cloned shallowly to that branch.
143-
"""
144-
expected_call_count = GIT_INSTALLED_CALLS + 4 # ensure_git_installed + resolve_commit + clone + fetch + checkout
145-
clone_config = CloneConfig(url=DEMO_URL, local_path=LOCAL_REPO_PATH, branch="feature-branch")
146122

147-
await clone_repo(clone_config)
148123

149-
assert_standard_calls(run_command_mock, clone_config, commit=DEMO_COMMIT)
150-
assert run_command_mock.call_count == expected_call_count
151124

152125

153-
@pytest.mark.asyncio
154-
async def test_git_command_failure(run_command_mock: AsyncMock) -> None:
155-
"""Test cloning when the Git command fails during execution.
156126

157-
Given a valid URL, but ``run_command`` raises a RuntimeError:
158-
When ``clone_repo`` is called,
159-
Then a RuntimeError should be raised with the correct message.
160-
"""
161-
clone_config = CloneConfig(url=DEMO_URL, local_path=LOCAL_REPO_PATH)
162-
163-
run_command_mock.side_effect = RuntimeError("Git is not installed or not accessible. Please install Git first.")
164-
165-
with pytest.raises(RuntimeError, match="Git is not installed or not accessible"):
166-
await clone_repo(clone_config)
167-
168-
169-
@pytest.mark.asyncio
170-
async def test_clone_default_shallow_clone(run_command_mock: AsyncMock) -> None:
171-
"""Test cloning a repository with the default shallow clone options.
172127

173-
Given a valid URL and no branch or commit:
174-
When ``clone_repo`` is called,
175-
Then the repository should be cloned with ``--depth=1`` and ``--single-branch``.
176-
"""
177-
expected_call_count = GIT_INSTALLED_CALLS + 4 # ensure_git_installed + resolve_commit + clone + fetch + checkout
178-
clone_config = CloneConfig(url=DEMO_URL, local_path=LOCAL_REPO_PATH)
179-
180-
await clone_repo(clone_config)
181-
182-
assert_standard_calls(run_command_mock, clone_config, commit=DEMO_COMMIT)
183-
assert run_command_mock.call_count == expected_call_count
184128

185129

186130
@pytest.mark.asyncio
187-
async def test_clone_commit(run_command_mock: AsyncMock) -> None:
188-
"""Test cloning when a commit hash is provided.
189-
190-
Given a valid URL and a commit hash:
191-
When ``clone_repo`` is called,
192-
Then the repository should be cloned and checked out at that commit.
193-
"""
194-
expected_call_count = GIT_INSTALLED_CALLS + 3 # ensure_git_installed + clone + fetch + checkout
195-
commit_hash = "a" * 40 # Simulating a valid commit hash
196-
clone_config = CloneConfig(url=DEMO_URL, local_path=LOCAL_REPO_PATH, commit=commit_hash)
197-
198-
await clone_repo(clone_config)
199-
200-
assert_standard_calls(run_command_mock, clone_config, commit=commit_hash)
201-
assert run_command_mock.call_count == expected_call_count
202-
203-
204-
@pytest.mark.asyncio
205-
async def test_check_repo_exists_with_redirect(mocker: MockerFixture) -> None:
206-
"""Test ``check_repo_exists`` when a redirect (302) is returned.
207-
208-
Given a URL that responds with "302 Found":
209-
When ``check_repo_exists`` is called,
210-
Then it should return ``False``, indicating the repo is inaccessible.
211-
"""
212-
mock_exec = mocker.patch("asyncio.create_subprocess_exec", new_callable=AsyncMock)
213-
mock_process = AsyncMock()
214-
mock_process.communicate.return_value = (b"302\n", b"")
215-
mock_process.returncode = 0 # Simulate successful request
216-
mock_exec.return_value = mock_process
217-
218-
repo_exists = await check_repo_exists(DEMO_URL)
219-
220-
assert repo_exists is False
221-
222-
223-
@pytest.mark.asyncio
224-
async def test_clone_with_timeout(run_command_mock: AsyncMock) -> None:
225-
"""Test cloning a repository when a timeout occurs.
226-
227-
Given a valid URL, but ``run_command`` times out:
228-
When ``clone_repo`` is called,
229-
Then an ``AsyncTimeoutError`` should be raised to indicate the operation exceeded time limits.
230-
"""
231-
clone_config = CloneConfig(url=DEMO_URL, local_path=LOCAL_REPO_PATH)
232-
233-
run_command_mock.side_effect = asyncio.TimeoutError
234-
235-
with pytest.raises(AsyncTimeoutError, match="Operation timed out after"):
236-
await clone_repo(clone_config)
237-
238-
239-
@pytest.mark.asyncio
240-
async def test_clone_branch_with_slashes(tmp_path: Path, run_command_mock: AsyncMock) -> None:
241-
"""Test cloning a branch with slashes in the name.
131+
async def test_clone_without_commit(repo_exists_true: AsyncMock, gitpython_mocks: dict) -> None:
132+
"""Test cloning a repository when no commit hash is provided.
242133
243-
Given a valid repository URL and a branch name with slashes:
134+
Given a valid URL and no commit hash:
244135
When ``clone_repo`` is called,
245-
Then the repository should be cloned and checked out at that branch.
136+
Then the repository should be cloned and checked out at the resolved commit.
246137
"""
247-
branch_name = "fix/in-operator"
248-
local_path = tmp_path / "gitingest"
249-
expected_call_count = GIT_INSTALLED_CALLS + 4 # ensure_git_installed + resolve_commit + clone + fetch + checkout
250-
clone_config = CloneConfig(url=DEMO_URL, local_path=str(local_path), branch=branch_name)
138+
clone_config = CloneConfig(url=DEMO_URL, local_path=LOCAL_REPO_PATH, commit=None, branch="main")
251139

252140
await clone_repo(clone_config)
253141

254-
assert_standard_calls(run_command_mock, clone_config, commit=DEMO_COMMIT)
255-
assert run_command_mock.call_count == expected_call_count
142+
repo_exists_true.assert_any_call(clone_config.url, token=None)
143+
144+
# Verify GitPython calls were made
145+
mock_git_cmd = gitpython_mocks["git_cmd"]
146+
mock_repo = gitpython_mocks["repo"]
147+
mock_clone_from = gitpython_mocks["clone_from"]
148+
149+
# Should have resolved the commit via ls_remote
150+
mock_git_cmd.ls_remote.assert_called()
151+
# Should have cloned the repo
152+
mock_clone_from.assert_called_once()
153+
# Should have fetched and checked out
154+
mock_repo.git.fetch.assert_called()
155+
mock_repo.git.checkout.assert_called()
256156

257157

258158
@pytest.mark.asyncio
259-
async def test_clone_creates_parent_directory(tmp_path: Path, run_command_mock: AsyncMock) -> None:
159+
async def test_clone_creates_parent_directory(tmp_path: Path, gitpython_mocks: dict) -> None:
260160
"""Test that ``clone_repo`` creates parent directories if they don't exist.
261161
262162
Given a local path with non-existent parent directories:
263163
When ``clone_repo`` is called,
264164
Then it should create the parent directories before attempting to clone.
265165
"""
266-
expected_call_count = GIT_INSTALLED_CALLS + 4 # ensure_git_installed + resolve_commit + clone + fetch + checkout
267166
nested_path = tmp_path / "deep" / "nested" / "path" / "repo"
268-
269167
clone_config = CloneConfig(url=DEMO_URL, local_path=str(nested_path))
270168

271169
await clone_repo(clone_config)
272170

171+
# Verify parent directories were created
273172
assert nested_path.parent.exists()
274-
assert_standard_calls(run_command_mock, clone_config, commit=DEMO_COMMIT)
275-
assert run_command_mock.call_count == expected_call_count
173+
174+
# Verify clone operation happened
175+
mock_clone_from = gitpython_mocks["clone_from"]
176+
mock_clone_from.assert_called_once()
276177

277178

278179
@pytest.mark.asyncio
279-
async def test_clone_with_specific_subpath(run_command_mock: AsyncMock) -> None:
180+
async def test_clone_with_specific_subpath(gitpython_mocks: dict) -> None:
280181
"""Test cloning a repository with a specific subpath.
281182
282183
Given a valid repository URL and a specific subpath:
283184
When ``clone_repo`` is called,
284-
Then the repository should be cloned with sparse checkout enabled and the specified subpath.
185+
Then the repository should be cloned with sparse checkout enabled.
285186
"""
286-
# ensure_git_installed + resolve_commit + clone + sparse-checkout + fetch + checkout
287187
subpath = "src/docs"
288-
expected_call_count = GIT_INSTALLED_CALLS + 5
289188
clone_config = CloneConfig(url=DEMO_URL, local_path=LOCAL_REPO_PATH, subpath=subpath)
290189

291190
await clone_repo(clone_config)
292191

293-
# Verify the clone command includes sparse checkout flags
294-
assert_partial_clone_calls(run_command_mock, clone_config, commit=DEMO_COMMIT)
295-
assert run_command_mock.call_count == expected_call_count
192+
# Verify partial clone (using git.clone instead of Repo.clone_from)
193+
mock_git_cmd = gitpython_mocks["git_cmd"]
194+
mock_git_cmd.clone.assert_called()
195+
196+
# Verify sparse checkout was configured
197+
mock_repo = gitpython_mocks["repo"]
198+
mock_repo.git.sparse_checkout.assert_called()
296199

297200

298201
@pytest.mark.asyncio
299-
async def test_clone_with_commit_and_subpath(run_command_mock: AsyncMock) -> None:
300-
"""Test cloning a repository with both a specific commit and subpath.
202+
async def test_clone_with_include_submodules(gitpython_mocks: dict) -> None:
203+
"""Test cloning a repository with submodules included.
301204
302-
Given a valid repository URL, commit hash, and subpath:
205+
Given a valid URLand ``include_submodules=True``:
303206
When ``clone_repo`` is called,
304-
Then the repository should be cloned with sparse checkout enabled,
305-
checked out at the specific commit, and only include the specified subpath.
207+
Then the repository should update submodules after cloning.
306208
"""
307-
subpath = "src/docs"
308-
expected_call_count = GIT_INSTALLED_CALLS + 4 # ensure_git_installed + clone + sparse-checkout + fetch + checkout
309-
commit_hash = "a" * 40 # Simulating a valid commit hash
310-
clone_config = CloneConfig(url=DEMO_URL, local_path=LOCAL_REPO_PATH, commit=commit_hash, subpath=subpath)
209+
clone_config = CloneConfig(url=DEMO_URL, local_path=LOCAL_REPO_PATH, branch="main", include_submodules=True)
311210

312211
await clone_repo(clone_config)
313212

314-
assert_partial_clone_calls(run_command_mock, clone_config, commit=commit_hash)
315-
assert run_command_mock.call_count == expected_call_count
213+
# Verify submodule update was called
214+
mock_repo = gitpython_mocks["repo"]
215+
mock_repo.git.submodule.assert_called_with("update", "--init", "--recursive", "--depth=1")
316216

317217

318218
@pytest.mark.asyncio
319-
async def test_clone_with_include_submodules(run_command_mock: AsyncMock) -> None:
320-
"""Test cloning a repository with submodules included.
219+
async def test_check_repo_exists_with_redirect(mocker: MockerFixture) -> None:
220+
"""Test ``check_repo_exists`` when a redirect (302) is returned.
321221
322-
Given a valid URL and ``include_submodules=True``:
323-
When ``clone_repo`` is called,
324-
Then the repository should be cloned with ``--recurse-submodules`` in the git command.
222+
Given a URL that responds with "302 Found":
223+
When ``check_repo_exists`` is called,
224+
Then it should return ``False``, indicating the repo is inaccessible.
325225
"""
326-
# ensure_git_installed + resolve_commit + clone + fetch + checkout + checkout submodules
327-
expected_call_count = GIT_INSTALLED_CALLS + 5
328-
clone_config = CloneConfig(url=DEMO_URL, local_path=LOCAL_REPO_PATH, branch="main", include_submodules=True)
226+
mock_exec = mocker.patch("asyncio.create_subprocess_exec", new_callable=AsyncMock)
227+
mock_process = AsyncMock()
228+
mock_process.communicate.return_value = (b"302\n", b"")
229+
mock_process.returncode = 0 # Simulate successful request
230+
mock_exec.return_value = mock_process
329231

330-
await clone_repo(clone_config)
232+
repo_exists=await check_repo_exists(DEMO_URL)
331233

332-
assert_standard_calls(run_command_mock, clone_config, commit=DEMO_COMMIT)
333-
assert_submodule_calls(run_command_mock, clone_config)
334-
assert run_command_mock.call_count == expected_call_count
234+
assert repo_exists is False
335235

336236

337-
def assert_standard_calls(mock: AsyncMock, cfg: CloneConfig, commit: str, *, partial_clone: bool = False) -> None:
338-
"""Assert that the standard clone sequence was called.
339-
340-
Note: With GitPython, some operations are mocked differently as they don't use direct command line calls.
341-
"""
342-
# Git version check should still happen
343-
# Note: GitPython may call git differently, so we check for any git version-related calls
344-
# The exact implementation may vary, so we focus on the core functionality
345-
346-
# For partial clones, we might see different call patterns
347-
# The important thing is that the clone operation succeeded
348237

349238

350-
def assert_partial_clone_calls(mock: AsyncMock, cfg: CloneConfig, commit: str) -> None:
351-
"""Assert that the partial clone sequence was called."""
352-
assert_standard_calls(mock, cfg, commit=commit, partial_clone=True)
353-
# With GitPython, sparse-checkout operations may be called differently
354239

355240

356-
def assert_submodule_calls(mock: AsyncMock, cfg: CloneConfig) -> None:
357-
"""Assert that submodule update commands were called."""
358-
# With GitPython, submodule operations are handled through the repo object
359-
# The exact call pattern may differ from direct git commands

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /