33import shutil
44
55from typing import TYPE_CHECKING
6- from unittest . mock import MagicMock
6+ from typing import cast
77
88import pytest
99
10+ from dulwich .client import FetchPackResult
1011from dulwich .repo import Repo
1112
1213from poetry .console .exceptions import PoetryRuntimeError
1314from poetry .vcs .git .backend import Git
15+ from poetry .vcs .git .backend import GitRefSpec
1416from poetry .vcs .git .backend import annotated_tag
1517from poetry .vcs .git .backend import is_revision_sha
1618from poetry .vcs .git .backend import urlpathjoin
19+ from tests .helpers import MOCK_DEFAULT_GIT_REVISION
1720
1821
1922if TYPE_CHECKING :
2023 from pathlib import Path
2124
25+ from pytest_mock import MockerFixture
26+
2227 from tests .vcs .git .git_fixture import TempRepoFixture
2328
2429
2530VALID_SHA = "c5c7624ef64f34d9f50c3b7e8118f7f652fddbbd"
2631
32+ FULL_SHA_MAIN = "f7c3bc1d808e04732adf679965ccc34ca7ae3441"
33+ FULL_SHA_TAG = "d4f6c2a8b9e1073451f28c96a5db7e3f9c2a8b7e"
34+ SHORT_SHA = "f7c3bc1d"
35+
36+
37+ @pytest .fixture ()
38+ def repo_mock (mocker : MockerFixture ) -> Repo :
39+ repo = mocker .MagicMock (spec = Repo )
40+
41+ repo .get_config .return_value .get .return_value = (
42+ b"https://github.com/python-poetry/poetry.git"
43+ )
44+
45+ repo .head .return_value = MOCK_DEFAULT_GIT_REVISION .encode ("utf-8" )
46+
47+ # Mock object store for short SHA resolution
48+ repo .object_store = mocker .MagicMock ()
49+ repo .object_store .iter_prefix .return_value = [FULL_SHA_MAIN .encode ()]
50+
51+ return cast ("Repo" , repo )
52+
53+
54+ @pytest .fixture ()
55+ def fetch_pack_result (mocker : MockerFixture ) -> FetchPackResult :
56+ mock_fetch_pack_result = mocker .MagicMock (spec = FetchPackResult )
57+ mock_fetch_pack_result .refs = {
58+ b"refs/heads/main" : FULL_SHA_MAIN .encode (),
59+ b"refs/heads/feature" : b"a9b8c7d6e5f4321098765432109876543210abcd" ,
60+ b"refs/tags/v1.0.0" : FULL_SHA_TAG .encode (),
61+ annotated_tag (b"refs/tags/v1.0.0" ): FULL_SHA_TAG .encode (),
62+ b"HEAD" : FULL_SHA_MAIN .encode (),
63+ }
64+ mock_fetch_pack_result .symrefs = {b"HEAD" : b"refs/heads/main" }
65+
66+ return cast ("FetchPackResult" , mock_fetch_pack_result )
67+
2768
2869def test_invalid_revision_sha () -> None :
2970 result = is_revision_sha ("invalid_input" )
@@ -65,13 +106,23 @@ def test_annotated_tag(tag: str | bytes) -> None:
65106 assert tag == b"my-tag^{}"
66107
67108
68- def test_get_remote_url () -> None :
69- repo = MagicMock (spec = Repo )
70- repo .get_config .return_value .get .return_value = (
71- b"https://github.com/python-poetry/poetry.git"
109+ def test_get_remote_url (repo_mock : Repo ) -> None :
110+ assert (
111+ Git .get_remote_url (repo_mock ) == "https://github.com/python-poetry/poetry.git"
72112 )
73113
74- assert Git .get_remote_url (repo ) == "https://github.com/python-poetry/poetry.git"
114+
115+ def test_get_revision (repo_mock : Repo ) -> None :
116+ assert Git .get_revision (repo_mock ) == MOCK_DEFAULT_GIT_REVISION
117+
118+
119+ def test_info (repo_mock : Repo ) -> None :
120+ info = Git .info (repo_mock )
121+
122+ assert info .origin == "https://github.com/python-poetry/poetry.git"
123+ assert (
124+ info .revision == MOCK_DEFAULT_GIT_REVISION
125+ ) # revision already mocked in helper
75126
76127
77128@pytest .mark .parametrize (
@@ -87,6 +138,103 @@ def test_urlpathjoin(url: str, expected_result: str) -> None:
87138 assert result == expected_result
88139
89140
141+ def test_git_refspec () -> None :
142+ git_ref = GitRefSpec ("main" , "1234" , "v2" )
143+
144+ assert git_ref .branch == "main"
145+ assert git_ref .revision == "1234"
146+ assert git_ref .tag == "v2"
147+ assert git_ref .ref == b"HEAD"
148+
149+
150+ @pytest .mark .parametrize (
151+ "refspec, expected_ref, expected_branch, expected_revision, expected_tag" ,
152+ [
153+ # Basic parameter tests
154+ (
155+ GitRefSpec (branch = "main" ),
156+ b"refs/heads/main" ,
157+ "main" ,
158+ None ,
159+ None ,
160+ ),
161+ (
162+ GitRefSpec (tag = "v1.0.0" ),
163+ annotated_tag (b"refs/tags/v1.0.0" ),
164+ None ,
165+ None ,
166+ "v1.0.0" ,
167+ ),
168+ (
169+ GitRefSpec (branch = "refs/heads/feature" ),
170+ b"refs/heads/feature" ,
171+ "refs/heads/feature" ,
172+ None ,
173+ None ,
174+ ),
175+ # Cross-parameter resolution tests
176+ (
177+ GitRefSpec (revision = "v1.0.0" ),
178+ annotated_tag (b"refs/tags/v1.0.0" ),
179+ None ,
180+ None ,
181+ "v1.0.0" ,
182+ ),
183+ (
184+ GitRefSpec (revision = "main" ),
185+ b"refs/heads/main" ,
186+ "main" ,
187+ None ,
188+ None ,
189+ ),
190+ (
191+ GitRefSpec (branch = "v1.0.0" ),
192+ annotated_tag (b"refs/tags/v1.0.0" ),
193+ None ,
194+ None ,
195+ "v1.0.0" ,
196+ ),
197+ (
198+ GitRefSpec (revision = "refs/heads/main" ),
199+ b"refs/heads/main" ,
200+ "refs/heads/main" ,
201+ None ,
202+ None ,
203+ ),
204+ # SHA resolution tests with realistic values
205+ (
206+ GitRefSpec (revision = SHORT_SHA ),
207+ b"refs/heads/main" ,
208+ None ,
209+ FULL_SHA_MAIN ,
210+ None ,
211+ ),
212+ (
213+ GitRefSpec (revision = FULL_SHA_MAIN ),
214+ b"refs/heads/main" ,
215+ None ,
216+ FULL_SHA_MAIN ,
217+ None ,
218+ ),
219+ ],
220+ )
221+ def test_git_ref_spec_resolve (
222+ fetch_pack_result : FetchPackResult ,
223+ repo_mock : Repo ,
224+ refspec : GitRefSpec ,
225+ expected_ref : bytes ,
226+ expected_branch : str | None ,
227+ expected_revision : str | None ,
228+ expected_tag : str | None ,
229+ ) -> None :
230+ refspec .resolve (fetch_pack_result , repo_mock )
231+
232+ assert refspec .ref == expected_ref
233+ assert refspec .branch == expected_branch
234+ assert refspec .revision == expected_revision
235+ assert refspec .tag == expected_tag
236+
237+
90238@pytest .mark .skip_git_mock
91239def test_clone_success (tmp_path : Path , temp_repo : TempRepoFixture ) -> None :
92240 source_root_dir = tmp_path / "test-repo"
0 commit comments