33
44from __future__ import annotations
55
6+ from pathlib import Path
7+ import sys
68from unittest .mock import MagicMock
79
810import pytest
11+ from packaging .requirements import InvalidRequirement
912
1013from colrev .package_manager import check
1114from colrev .package_manager .package_manager import PackageManager
15+ from colrev .package_manager .package_manager import _validate_external_package_selection
16+ from colrev .package_manager .package_manager import _validate_internal_package_selection
1217
1318
1419@pytest .mark .parametrize (
@@ -38,20 +43,27 @@ def test_check_package_installed_validates_package_name_before_subprocess(
3843
3944
4045def test_install_accepts_internal_package_and_calls_subprocess (
41- monkeypatch : pytest .MonkeyPatch ,
46+ monkeypatch : pytest .MonkeyPatch , tmp_path : Path
4247) -> None :
48+ internal_dir = tmp_path / "colrev-allowed"
49+ internal_dir .mkdir ()
50+ (internal_dir / "pyproject.toml" ).write_text ("[project]\n name='colrev-allowed'\n " )
51+
4352 run_mock = MagicMock ()
4453 monkeypatch .setattr (
4554 "colrev.package_manager.package_manager.subprocess.run" , run_mock
4655 )
4756 monkeypatch .setattr (
4857 "colrev.package_manager.package_manager.get_internal_packages_dict" ,
49- MagicMock (return_value = {"colrev.allowed" : "colrev-allowed" }),
58+ MagicMock (return_value = {"colrev.allowed" : str ( internal_dir ) }),
5059 )
5160
5261 PackageManager ().install (packages = ["colrev.allowed" ], upgrade = False )
5362
54- run_mock .assert_called_once_with (["pip" , "install" , "colrev-allowed" ], check = True )
63+ run_mock .assert_called_once_with (
64+ [sys .executable , "-m" , "pip" , "install" , str (internal_dir .resolve ())],
65+ check = True ,
66+ )
5567
5668
5769def test_install_rejects_unknown_internal_package_before_subprocess (
@@ -70,3 +82,98 @@ def test_install_rejects_unknown_internal_package_before_subprocess(
7082 PackageManager ().install (packages = ["colrev.unknown" ], upgrade = False )
7183
7284 run_mock .assert_not_called ()
85+
86+
87+ @pytest .mark .parametrize ("package_spec" , ["requests" , "requests>=2.0" ])
88+ def test_validate_external_package_accepts (package_spec : str ) -> None :
89+ assert (
90+ _validate_external_package_selection (selected_package = package_spec )
91+ == package_spec
92+ )
93+
94+
95+ @pytest .mark .parametrize (
96+ "package_spec" , ["" , "--index-url=https://example.com" , "-r requirements.txt" ]
97+ )
98+ def test_validate_external_package_rejects (package_spec : str ) -> None :
99+ with pytest .raises ((ValueError , InvalidRequirement )):
100+ _validate_external_package_selection (selected_package = package_spec )
101+
102+
103+ def test_validate_internal_package_selection_accepts (tmp_path : Path ) -> None :
104+ package_dir = tmp_path / "internal"
105+ package_dir .mkdir ()
106+ (package_dir / "pyproject.toml" ).write_text ("[project]\n name='internal'\n " )
107+
108+ selected = _validate_internal_package_selection (
109+ selected_package = "colrev.allowed" ,
110+ internal_packages_dict = {"colrev.allowed" : str (package_dir )},
111+ )
112+ assert selected == str (package_dir .resolve ())
113+
114+
115+ def test_validate_internal_package_selection_rejects_missing_path (
116+ tmp_path : Path ,
117+ ) -> None :
118+ missing_dir = tmp_path / "missing"
119+ with pytest .raises (ValueError ):
120+ _validate_internal_package_selection (
121+ selected_package = "colrev.allowed" ,
122+ internal_packages_dict = {"colrev.allowed" : str (missing_dir )},
123+ )
124+
125+
126+ def test_validate_internal_package_selection_rejects_missing_pyproject (
127+ tmp_path : Path ,
128+ ) -> None :
129+ package_dir = tmp_path / "internal"
130+ package_dir .mkdir ()
131+ with pytest .raises (ValueError ):
132+ _validate_internal_package_selection (
133+ selected_package = "colrev.allowed" ,
134+ internal_packages_dict = {"colrev.allowed" : str (package_dir )},
135+ )
136+
137+
138+ def test_install_uses_uv_executable_when_enabled (
139+ monkeypatch : pytest .MonkeyPatch ,
140+ ) -> None :
141+ run_mock = MagicMock ()
142+ monkeypatch .setattr (
143+ "colrev.package_manager.package_manager.subprocess.run" , run_mock
144+ )
145+ monkeypatch .setattr (
146+ "colrev.package_manager.package_manager.shutil.which" , lambda _ : "/usr/bin/uv"
147+ )
148+ monkeypatch .setattr (
149+ "colrev.package_manager.package_manager.get_internal_packages_dict" ,
150+ MagicMock (return_value = {}),
151+ )
152+
153+ PackageManager ().install (packages = ["requests" ], upgrade = False , uv = True )
154+
155+ run_mock .assert_called_once_with (
156+ ["/usr/bin/uv" , "pip" , "install" , "requests" ],
157+ check = True ,
158+ )
159+
160+
161+ def test_install_validates_external_packages_before_subprocess (
162+ monkeypatch : pytest .MonkeyPatch ,
163+ ) -> None :
164+ run_mock = MagicMock ()
165+ monkeypatch .setattr (
166+ "colrev.package_manager.package_manager.subprocess.run" , run_mock
167+ )
168+ monkeypatch .setattr (
169+ "colrev.package_manager.package_manager.get_internal_packages_dict" ,
170+ MagicMock (return_value = {}),
171+ )
172+
173+ with pytest .raises (ValueError , match = "must not start with '-'" ):
174+ PackageManager ().install (
175+ packages = ["--index-url=https://example.com" ],
176+ upgrade = False ,
177+ )
178+
179+ run_mock .assert_not_called ()
0 commit comments