Skip to content

Commit e258638

Browse files
committed
tests(async_git): Add tests for async_git_repo fixture
why: Verify the new fixture works correctly. what: - Test fixture provides initialized repo - Test status() and remotes_get() work
1 parent 5408987 commit e258638

1 file changed

Lines changed: 267 additions & 12 deletions

File tree

tests/sync/_async/test_git.py

Lines changed: 267 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33
from __future__ import annotations
44

55
import asyncio
6-
import typing as t
76
from pathlib import Path
87

98
import pytest
109

10+
from libvcs.pytest_plugin import CreateRepoPytestFixtureFn
1111
from libvcs.sync._async.git import AsyncGitSync
1212
from libvcs.sync.git import GitRemote
1313

@@ -64,7 +64,7 @@ class TestAsyncGitSyncObtain:
6464
async def test_obtain_basic(
6565
self,
6666
tmp_path: Path,
67-
create_git_remote_repo: t.Any,
67+
create_git_remote_repo: CreateRepoPytestFixtureFn,
6868
) -> None:
6969
"""Test basic obtain operation."""
7070
remote_repo = create_git_remote_repo()
@@ -83,7 +83,7 @@ async def test_obtain_basic(
8383
async def test_obtain_shallow(
8484
self,
8585
tmp_path: Path,
86-
create_git_remote_repo: t.Any,
86+
create_git_remote_repo: CreateRepoPytestFixtureFn,
8787
) -> None:
8888
"""Test shallow clone via obtain."""
8989
remote_repo = create_git_remote_repo()
@@ -106,7 +106,7 @@ class TestAsyncGitSyncUpdateRepo:
106106
async def test_update_repo_basic(
107107
self,
108108
tmp_path: Path,
109-
create_git_remote_repo: t.Any,
109+
create_git_remote_repo: CreateRepoPytestFixtureFn,
110110
) -> None:
111111
"""Test basic update_repo operation."""
112112
remote_repo = create_git_remote_repo()
@@ -128,7 +128,7 @@ async def test_update_repo_basic(
128128
async def test_update_repo_obtains_if_missing(
129129
self,
130130
tmp_path: Path,
131-
create_git_remote_repo: t.Any,
131+
create_git_remote_repo: CreateRepoPytestFixtureFn,
132132
) -> None:
133133
"""Test update_repo clones if repo doesn't exist."""
134134
remote_repo = create_git_remote_repo()
@@ -153,7 +153,7 @@ class TestAsyncGitSyncGetRevision:
153153
async def test_get_revision(
154154
self,
155155
tmp_path: Path,
156-
create_git_remote_repo: t.Any,
156+
create_git_remote_repo: CreateRepoPytestFixtureFn,
157157
) -> None:
158158
"""Test get_revision returns current HEAD or 'initial'."""
159159
remote_repo = create_git_remote_repo()
@@ -179,7 +179,7 @@ class TestAsyncGitSyncRemotes:
179179
async def test_remotes_get(
180180
self,
181181
tmp_path: Path,
182-
create_git_remote_repo: t.Any,
182+
create_git_remote_repo: CreateRepoPytestFixtureFn,
183183
) -> None:
184184
"""Test remotes_get returns remotes."""
185185
remote_repo = create_git_remote_repo()
@@ -199,7 +199,7 @@ async def test_remotes_get(
199199
async def test_remote_get_single(
200200
self,
201201
tmp_path: Path,
202-
create_git_remote_repo: t.Any,
202+
create_git_remote_repo: CreateRepoPytestFixtureFn,
203203
) -> None:
204204
"""Test remote() returns single remote."""
205205
remote_repo = create_git_remote_repo()
@@ -219,7 +219,7 @@ async def test_remote_get_single(
219219
async def test_set_remote(
220220
self,
221221
tmp_path: Path,
222-
create_git_remote_repo: t.Any,
222+
create_git_remote_repo: CreateRepoPytestFixtureFn,
223223
) -> None:
224224
"""Test set_remote adds a new remote."""
225225
remote_repo = create_git_remote_repo()
@@ -249,7 +249,7 @@ class TestAsyncGitSyncStatus:
249249
async def test_status(
250250
self,
251251
tmp_path: Path,
252-
create_git_remote_repo: t.Any,
252+
create_git_remote_repo: CreateRepoPytestFixtureFn,
253253
) -> None:
254254
"""Test status() returns GitStatus without error."""
255255
remote_repo = create_git_remote_repo()
@@ -269,14 +269,269 @@ async def test_status(
269269
assert isinstance(status, GitStatus)
270270

271271

272+
class TestAsyncGitRepoFixture:
273+
"""Tests for the async_git_repo pytest fixture."""
274+
275+
@pytest.mark.asyncio
276+
async def test_async_git_repo_fixture(
277+
self,
278+
async_git_repo: AsyncGitSync,
279+
) -> None:
280+
"""Test that async_git_repo fixture provides a working repository."""
281+
# Verify the repo exists and is initialized
282+
assert async_git_repo.path.exists()
283+
assert (async_git_repo.path / ".git").exists()
284+
285+
# Verify we can perform async operations
286+
revision = await async_git_repo.get_revision()
287+
assert revision
288+
assert len(revision.strip()) == 40 # Full SHA
289+
290+
@pytest.mark.asyncio
291+
async def test_async_git_repo_status(
292+
self,
293+
async_git_repo: AsyncGitSync,
294+
) -> None:
295+
"""Test that status() works on fixture-provided repo."""
296+
from libvcs.sync.git import GitStatus
297+
298+
status = await async_git_repo.status()
299+
assert isinstance(status, GitStatus)
300+
301+
@pytest.mark.asyncio
302+
async def test_async_git_repo_remotes(
303+
self,
304+
async_git_repo: AsyncGitSync,
305+
) -> None:
306+
"""Test that remotes are properly configured on fixture-provided repo."""
307+
remotes = await async_git_repo.remotes_get()
308+
assert "origin" in remotes
309+
310+
311+
class TestAsyncGitSyncFromPipUrl:
312+
"""Tests for AsyncGitSync.from_pip_url()."""
313+
314+
def test_from_pip_url_https(self, tmp_path: Path) -> None:
315+
"""Test from_pip_url with git+https URL."""
316+
repo = AsyncGitSync.from_pip_url(
317+
pip_url="git+https://github.com/test/repo.git",
318+
path=tmp_path / "pip_repo",
319+
)
320+
assert repo.url == "https://github.com/test/repo.git"
321+
assert repo.path == tmp_path / "pip_repo"
322+
323+
def test_from_pip_url_with_revision(self, tmp_path: Path) -> None:
324+
"""Test from_pip_url with revision specifier."""
325+
repo = AsyncGitSync.from_pip_url(
326+
pip_url="git+https://github.com/test/repo.git@v1.0.0",
327+
path=tmp_path / "pip_repo",
328+
)
329+
assert repo.url == "https://github.com/test/repo.git"
330+
assert repo.rev == "v1.0.0"
331+
332+
def test_from_pip_url_ssh(self, tmp_path: Path) -> None:
333+
"""Test from_pip_url with git+ssh URL."""
334+
repo = AsyncGitSync.from_pip_url(
335+
pip_url="git+ssh://git@github.com/test/repo.git",
336+
path=tmp_path / "pip_repo",
337+
)
338+
assert repo.url == "ssh://git@github.com/test/repo.git"
339+
340+
341+
class TestAsyncGitSyncGetGitVersion:
342+
"""Tests for AsyncGitSync.get_git_version()."""
343+
344+
@pytest.mark.asyncio
345+
async def test_get_git_version(
346+
self,
347+
async_git_repo: AsyncGitSync,
348+
) -> None:
349+
"""Test get_git_version returns version string."""
350+
version = await async_git_repo.get_git_version()
351+
assert "git version" in version
352+
353+
@pytest.mark.asyncio
354+
async def test_get_git_version_format(
355+
self,
356+
async_git_repo: AsyncGitSync,
357+
) -> None:
358+
"""Test get_git_version returns expected format."""
359+
version = await async_git_repo.get_git_version()
360+
# Version string should contain numbers
361+
import re
362+
363+
assert re.search(r"\d+\.\d+", version)
364+
365+
366+
class TestAsyncGitSyncUpdateRepoStash:
367+
"""Tests for AsyncGitSync.update_repo() stash handling."""
368+
369+
@pytest.mark.asyncio
370+
async def test_update_repo_with_uncommitted_changes(
371+
self,
372+
tmp_path: Path,
373+
create_git_remote_repo: CreateRepoPytestFixtureFn,
374+
) -> None:
375+
"""Test update_repo with uncommitted changes triggers stash."""
376+
from libvcs.pytest_plugin import git_remote_repo_single_commit_post_init
377+
378+
# Create remote with a commit
379+
remote_repo = create_git_remote_repo(
380+
remote_repo_post_init=git_remote_repo_single_commit_post_init
381+
)
382+
repo_path = tmp_path / "stash_test_repo"
383+
384+
repo = AsyncGitSync(
385+
url=f"file://{remote_repo}",
386+
path=repo_path,
387+
)
388+
await repo.obtain()
389+
390+
# Create uncommitted changes
391+
test_file = repo_path / "local_change.txt"
392+
test_file.write_text("local uncommitted content")
393+
await repo.cmd.run(["add", "local_change.txt"])
394+
395+
# Update should handle the uncommitted changes (may stash if needed)
396+
# This tests the stash logic path
397+
await repo.update_repo()
398+
399+
# Repo should still exist and be valid
400+
assert repo_path.exists()
401+
revision = await repo.get_revision()
402+
assert revision
403+
404+
@pytest.mark.asyncio
405+
async def test_update_repo_clean_working_tree(
406+
self,
407+
tmp_path: Path,
408+
create_git_remote_repo: CreateRepoPytestFixtureFn,
409+
) -> None:
410+
"""Test update_repo with clean working tree skips stash."""
411+
from libvcs.pytest_plugin import git_remote_repo_single_commit_post_init
412+
413+
remote_repo = create_git_remote_repo(
414+
remote_repo_post_init=git_remote_repo_single_commit_post_init
415+
)
416+
repo_path = tmp_path / "clean_repo"
417+
418+
repo = AsyncGitSync(
419+
url=f"file://{remote_repo}",
420+
path=repo_path,
421+
)
422+
await repo.obtain()
423+
424+
# No changes - update should succeed without stash
425+
await repo.update_repo()
426+
427+
assert repo_path.exists()
428+
429+
430+
class TestAsyncGitSyncSetRemotes:
431+
"""Tests for AsyncGitSync.set_remotes()."""
432+
433+
@pytest.mark.asyncio
434+
async def test_set_remotes_overwrite_false(
435+
self,
436+
tmp_path: Path,
437+
create_git_remote_repo: CreateRepoPytestFixtureFn,
438+
) -> None:
439+
"""Test set_remotes with overwrite=False preserves existing remotes."""
440+
remote_repo = create_git_remote_repo()
441+
repo_path = tmp_path / "set_remotes_repo"
442+
443+
repo = AsyncGitSync(
444+
url=f"file://{remote_repo}",
445+
path=repo_path,
446+
)
447+
await repo.obtain()
448+
449+
# Get original origin URL
450+
original_remotes = await repo.remotes_get()
451+
original_origin_url = original_remotes["origin"].fetch_url
452+
453+
# Try to set remotes with overwrite=False
454+
await repo.set_remotes(overwrite=False)
455+
456+
# Origin should still have same URL
457+
updated_remotes = await repo.remotes_get()
458+
assert updated_remotes["origin"].fetch_url == original_origin_url
459+
460+
@pytest.mark.asyncio
461+
async def test_set_remotes_adds_new_remote(
462+
self,
463+
tmp_path: Path,
464+
create_git_remote_repo: CreateRepoPytestFixtureFn,
465+
) -> None:
466+
"""Test set_remotes adds new remotes from configuration."""
467+
remote_repo = create_git_remote_repo()
468+
upstream_repo = create_git_remote_repo()
469+
repo_path = tmp_path / "add_remote_repo"
470+
471+
repo = AsyncGitSync(
472+
url=f"file://{remote_repo}",
473+
path=repo_path,
474+
remotes={"upstream": f"file://{upstream_repo}"},
475+
)
476+
await repo.obtain()
477+
478+
# Set remotes should add upstream
479+
await repo.set_remotes(overwrite=False)
480+
481+
remotes = await repo.remotes_get()
482+
assert "origin" in remotes
483+
assert "upstream" in remotes
484+
485+
486+
class TestAsyncGitSyncGetCurrentRemoteName:
487+
"""Tests for AsyncGitSync.get_current_remote_name()."""
488+
489+
@pytest.mark.asyncio
490+
async def test_get_current_remote_name_default(
491+
self,
492+
async_git_repo: AsyncGitSync,
493+
) -> None:
494+
"""Test get_current_remote_name returns origin by default."""
495+
remote_name = await async_git_repo.get_current_remote_name()
496+
assert remote_name == "origin"
497+
498+
@pytest.mark.asyncio
499+
async def test_get_current_remote_name_on_detached_head(
500+
self,
501+
tmp_path: Path,
502+
create_git_remote_repo: CreateRepoPytestFixtureFn,
503+
) -> None:
504+
"""Test get_current_remote_name on detached HEAD falls back to origin."""
505+
from libvcs.pytest_plugin import git_remote_repo_single_commit_post_init
506+
507+
remote_repo = create_git_remote_repo(
508+
remote_repo_post_init=git_remote_repo_single_commit_post_init
509+
)
510+
repo_path = tmp_path / "detached_head_repo"
511+
512+
repo = AsyncGitSync(
513+
url=f"file://{remote_repo}",
514+
path=repo_path,
515+
)
516+
await repo.obtain()
517+
518+
# Detach HEAD
519+
head = await repo.cmd.rev_parse(args="HEAD", verify=True)
520+
await repo.cmd.checkout(branch=head.strip(), detach=True)
521+
522+
# Should fall back to 'origin'
523+
remote_name = await repo.get_current_remote_name()
524+
assert remote_name == "origin"
525+
526+
272527
class TestAsyncGitSyncConcurrency:
273528
"""Tests for concurrent AsyncGitSync operations."""
274529

275530
@pytest.mark.asyncio
276531
async def test_concurrent_obtain(
277532
self,
278533
tmp_path: Path,
279-
create_git_remote_repo: t.Any,
534+
create_git_remote_repo: CreateRepoPytestFixtureFn,
280535
) -> None:
281536
"""Test multiple concurrent obtain operations."""
282537
remote_repo = create_git_remote_repo()
@@ -301,7 +556,7 @@ async def clone_repo(i: int) -> AsyncGitSync:
301556
async def test_concurrent_status_calls(
302557
self,
303558
tmp_path: Path,
304-
create_git_remote_repo: t.Any,
559+
create_git_remote_repo: CreateRepoPytestFixtureFn,
305560
) -> None:
306561
"""Test multiple concurrent status calls on same repo."""
307562
remote_repo = create_git_remote_repo()

0 commit comments

Comments
 (0)