55from pathlib import Path
66
77from manage import uninstall_command as UC
8- from manage .exceptions import FilesInUseError
8+ from manage .exceptions import ArgumentError , FilesInUseError
99
1010
1111def test_purge_global_dir (monkeypatch , registry , tmp_path ):
@@ -22,17 +22,19 @@ def test_purge_global_dir(monkeypatch, registry, tmp_path):
2222
2323def test_null_purge (fake_config ):
2424 cmd = fake_config
25- cmd .args = ["--purge" ]
25+ cmd .args = []
2626 cmd .confirm = False
2727 cmd .purge = True
28+ cmd .cleanup = False
2829 UC .execute (cmd )
2930
3031
3132def test_purge_unknown_files (fake_config ):
3233 cmd = fake_config
33- cmd .args = ["--purge" ]
34+ cmd .args = []
3435 cmd .confirm = False
3536 cmd .purge = True
37+ cmd .cleanup = False
3638
3739 unknown_file = cmd .install_dir / "unknown.txt"
3840 unknown_file .write_bytes (b"unknown" )
@@ -48,9 +50,10 @@ def test_purge_unknown_files(fake_config):
4850
4951def test_purge_preserves_runtime_in_use (fake_config , monkeypatch ):
5052 cmd = fake_config
51- cmd .args = ["--purge" ]
53+ cmd .args = []
5254 cmd .confirm = False
5355 cmd .purge = True
56+ cmd .cleanup = False
5457
5558 runtime = cmd .install_dir / "runtime"
5659 runtime .mkdir ()
@@ -78,3 +81,131 @@ def locked_rmtree(path, *args, **kwargs):
7881
7982 assert executable .is_file ()
8083 assert not unknown_dir .exists ()
84+
85+
86+ def test_cleanup_preserves_recognized_runtimes (fake_config , monkeypatch ):
87+ cmd = fake_config
88+ cmd .args = []
89+ cmd .purge = False
90+ cmd .cleanup = True
91+
92+ runtime = cmd .install_dir / "runtime"
93+ runtime .mkdir ()
94+ executable = runtime / "python.exe"
95+ executable .write_bytes (b"preserve" )
96+ cmd .installs = [{
97+ "display-name" : "Recognized runtime" ,
98+ "prefix" : runtime ,
99+ }]
100+
101+ cmd .download_dir .mkdir ()
102+ cached_file = cmd .download_dir / "cached.zip"
103+ cached_file .write_bytes (b"cached" )
104+ unknown_file = cmd .install_dir / "unknown.txt"
105+ unknown_file .write_bytes (b"unknown" )
106+ broken_runtime = cmd .install_dir / "broken-runtime"
107+ broken_runtime .mkdir ()
108+ (broken_runtime / "__install__.json" ).write_text ("invalid" )
109+
110+ prompts = []
111+ cmd .ask_yn = lambda prompt : prompts .append (prompt ) or True
112+ refreshed = []
113+ monkeypatch .setattr (UC , "update_all_shortcuts" , refreshed .append )
114+
115+ UC .execute (cmd )
116+
117+ assert prompts == [
118+ "Clean up cached files and unrecognized content? "
119+ "This will preserve recognized runtimes."
120+ ]
121+ assert executable .read_bytes () == b"preserve"
122+ assert not cmd .download_dir .exists ()
123+ assert not unknown_file .exists ()
124+ assert not broken_runtime .exists ()
125+ assert refreshed == [cmd ]
126+
127+
128+ def test_cleanup_shared_download_dir_preserves_recognized_runtimes (
129+ fake_config , monkeypatch
130+ ):
131+ cmd = fake_config
132+ cmd .args = []
133+ cmd .purge = False
134+ cmd .cleanup = True
135+ cmd .download_dir = cmd .install_dir
136+
137+ runtime = cmd .install_dir / "runtime"
138+ runtime .mkdir ()
139+ executable = runtime / "python.exe"
140+ executable .write_bytes (b"preserve" )
141+ cmd .installs = [{
142+ "display-name" : "Recognized runtime" ,
143+ "prefix" : runtime ,
144+ }]
145+
146+ cached_file = cmd .download_dir / "cached.zip"
147+ cached_file .write_bytes (b"cached" )
148+ unknown_dir = cmd .install_dir / "unknown"
149+ unknown_dir .mkdir ()
150+
151+ cmd .ask_yn = lambda prompt : True
152+ refreshed = []
153+ monkeypatch .setattr (UC , "update_all_shortcuts" , refreshed .append )
154+
155+ UC .execute (cmd )
156+
157+ assert executable .read_bytes () == b"preserve"
158+ assert not cached_file .exists ()
159+ assert not unknown_dir .exists ()
160+ assert refreshed == [cmd ]
161+
162+
163+ def test_cleanup_declined_preserves_all_files (fake_config , monkeypatch ):
164+ cmd = fake_config
165+ cmd .args = []
166+ cmd .purge = False
167+ cmd .cleanup = True
168+
169+ cmd .download_dir .mkdir ()
170+ cached_file = cmd .download_dir / "cached.zip"
171+ cached_file .write_bytes (b"cached" )
172+ unknown_file = cmd .install_dir / "unknown.txt"
173+ unknown_file .write_bytes (b"unknown" )
174+
175+ prompts = []
176+ cmd .ask_yn = lambda prompt : prompts .append (prompt ) or False
177+ monkeypatch .setattr (
178+ UC ,
179+ "update_all_shortcuts" ,
180+ lambda cmd : pytest .fail ("shortcuts refreshed after cleanup was declined" ),
181+ )
182+
183+ UC .execute (cmd )
184+
185+ assert prompts == [
186+ "Clean up cached files and unrecognized content? "
187+ "This will preserve recognized runtimes."
188+ ]
189+ assert cached_file .read_bytes () == b"cached"
190+ assert unknown_file .read_bytes () == b"unknown"
191+
192+
193+ @pytest .mark .parametrize ("option" , ["purge" , "cleanup" ])
194+ def test_global_uninstall_options_reject_tags (fake_config , option ):
195+ cmd = fake_config
196+ cmd .args = ["3.14" ]
197+ cmd .purge = option == "purge"
198+ cmd .cleanup = option == "cleanup"
199+
200+ with pytest .raises (ArgumentError , match = "does not accept runtime tags" ):
201+ UC .execute (cmd )
202+
203+
204+ def test_purge_and_cleanup_are_mutually_exclusive (fake_config ):
205+ cmd = fake_config
206+ cmd .args = []
207+ cmd .purge = True
208+ cmd .cleanup = True
209+
210+ with pytest .raises (ArgumentError , match = "cannot be combined" ):
211+ UC .execute (cmd )
0 commit comments