88from __future__ import annotations
99
1010import json
11- import tempfile
12- from pathlib import Path
13- from typing import Any , Callable
11+ import pathlib
12+ import typing as t
1413
1514import hypothesis .strategies as st
1615import yaml
17- from hypothesis import given , settings
16+ from hypothesis import HealthCheck , given , settings
1817
1918from vcspull .config .loader import load_config , resolve_includes , save_config
2019from vcspull .config .models import Repository , Settings , VCSPullConfig
2120
2221
2322# Reuse strategies from test_models_property.py
2423@st .composite
25- def valid_url_strategy (draw : Callable [[st .SearchStrategy [Any ]], Any ]) -> str :
24+ def valid_url_strategy (draw : t . Callable [[st .SearchStrategy [t . Any ]], t . Any ]) -> str :
2625 """Generate valid URLs for repositories."""
2726 protocols = ["https://" , "http://" , "git://" , "ssh://git@" ]
2827 domains = ["github.com" , "gitlab.com" , "bitbucket.org" , "example.com" ]
@@ -53,7 +52,7 @@ def valid_url_strategy(draw: Callable[[st.SearchStrategy[Any]], Any]) -> str:
5352
5453
5554@st .composite
56- def valid_path_strategy (draw : Callable [[st .SearchStrategy [Any ]], Any ]) -> str :
55+ def valid_path_strategy (draw : t . Callable [[st .SearchStrategy [t . Any ]], t . Any ]) -> str :
5756 """Generate valid paths for repositories."""
5857 base_dirs = ["~/code" , "~/projects" , "/tmp" , "./projects" ]
5958 sub_dirs = [
@@ -78,7 +77,9 @@ def valid_path_strategy(draw: Callable[[st.SearchStrategy[Any]], Any]) -> str:
7877
7978
8079@st .composite
81- def repository_strategy (draw : Callable [[st .SearchStrategy [Any ]], Any ]) -> Repository :
80+ def repository_strategy (
81+ draw : t .Callable [[st .SearchStrategy [t .Any ]], t .Any ],
82+ ) -> Repository :
8283 """Generate valid Repository instances."""
8384 name = draw (st .one_of (st .none (), st .text (min_size = 1 , max_size = 20 )))
8485 url = draw (valid_url_strategy ())
@@ -130,7 +131,7 @@ def repository_strategy(draw: Callable[[st.SearchStrategy[Any]], Any]) -> Reposi
130131
131132
132133@st .composite
133- def settings_strategy (draw : Callable [[st .SearchStrategy [Any ]], Any ]) -> Settings :
134+ def settings_strategy (draw : t . Callable [[st .SearchStrategy [t . Any ]], t . Any ]) -> Settings :
134135 """Generate valid Settings instances."""
135136 sync_remotes = draw (st .booleans ())
136137 default_vcs = draw (st .one_of (st .none (), st .sampled_from (["git" , "hg" , "svn" ])))
@@ -145,14 +146,14 @@ def settings_strategy(draw: Callable[[st.SearchStrategy[Any]], Any]) -> Settings
145146
146147@st .composite
147148def vcspull_config_strategy (
148- draw : Callable [[st .SearchStrategy [Any ]], Any ],
149+ draw : t . Callable [[st .SearchStrategy [t . Any ]], t . Any ],
149150 with_includes : bool = False ,
150151) -> VCSPullConfig :
151152 """Generate valid VCSPullConfig instances.
152153
153154 Parameters
154155 ----------
155- draw : Callable
156+ draw : t. Callable
156157 Hypothesis draw function
157158 with_includes : bool, optional
158159 Whether to add include files to the config, by default False
@@ -181,192 +182,171 @@ def vcspull_config_strategy(
181182 )
182183
183184
184- # Helper function to save a config to a temporary file
185- def save_temp_config (config : VCSPullConfig , suffix : str = ".yaml" ) -> Path :
186- """Save a config to a temporary file.
187-
188- Parameters
189- ----------
190- config : VCSPullConfig
191- Configuration to save
192- suffix : str, optional
193- File suffix, by default ".yaml"
194-
195- Returns
196- -------
197- Path
198- Path to the saved file
199- """
200- with tempfile .NamedTemporaryFile (suffix = suffix , delete = False ) as f :
201- temp_path = Path (f .name )
202-
203- # Save the config to the temporary file
204- format_type = "yaml" if suffix in (".yaml" , ".yml" ) else "json"
205- save_config (config , temp_path , format_type = format_type )
206-
207- return temp_path
208-
209-
210185class TestConfigLoaderProperties :
211186 """Property-based tests for configuration loading."""
212187
213188 @given (config = vcspull_config_strategy ())
214189 @settings (
215- max_examples = 10
216- ) # Limit the number of examples to avoid too many temp files
217- def test_load_save_roundtrip (self , config : VCSPullConfig ) -> None :
190+ max_examples = 10 , # Limit examples to avoid too many temp files
191+ suppress_health_check = [HealthCheck .function_scoped_fixture ],
192+ )
193+ def test_load_save_roundtrip (
194+ self , config : VCSPullConfig , tmp_path : pathlib .Path
195+ ) -> None :
218196 """Test that saving and loading a configuration preserves its content."""
219197 # Save the config to a temporary YAML file
220- yaml_path = save_temp_config (config , suffix = ".yaml" )
221- try :
222- # Load the config back
223- loaded_config = load_config (yaml_path )
224-
225- # Check that loaded config matches original
226- assert loaded_config .settings .model_dump () == config .settings .model_dump ()
227- assert len (loaded_config .repositories ) == len (config .repositories )
228- for i , repo in enumerate (config .repositories ):
229- assert loaded_config .repositories [i ].url == repo .url
230- assert loaded_config .repositories [i ].path == repo .path
231-
232- # Also test with JSON format
233- json_path = save_temp_config (config , suffix = ".json" )
234- try :
235- json_loaded_config = load_config (json_path )
236-
237- # Check that JSON loaded config matches original
238- assert (
239- json_loaded_config .settings .model_dump ()
240- == config .settings .model_dump ()
241- )
242- assert len (json_loaded_config .repositories ) == len (config .repositories )
243- finally :
244- # Cleanup JSON temp file
245- json_path .unlink (missing_ok = True )
198+ yaml_path = tmp_path / "config.yaml"
199+ save_config (config , yaml_path , format_type = "yaml" )
246200
247- finally :
248- # Cleanup YAML temp file
249- yaml_path .unlink (missing_ok = True )
201+ # Load the config back
202+ loaded_config = load_config (yaml_path )
203+
204+ # Check that loaded config matches original
205+ assert loaded_config .settings .model_dump () == config .settings .model_dump ()
206+ assert len (loaded_config .repositories ) == len (config .repositories )
207+ for i , repo in enumerate (config .repositories ):
208+ assert loaded_config .repositories [i ].url == repo .url
209+ assert loaded_config .repositories [i ].path == repo .path
210+
211+ # Also test with JSON format
212+ json_path = tmp_path / "config.json"
213+ save_config (config , json_path , format_type = "json" )
214+
215+ # Load JSON config
216+ json_loaded_config = load_config (json_path )
217+
218+ # Check that JSON loaded config matches original
219+ assert json_loaded_config .settings .model_dump () == config .settings .model_dump ()
220+ assert len (json_loaded_config .repositories ) == len (config .repositories )
250221
251222 @given (
252223 main_config = vcspull_config_strategy (with_includes = True ),
253224 included_configs = st .lists (vcspull_config_strategy (), min_size = 1 , max_size = 3 ),
254225 )
255- @settings (max_examples = 10 ) # Limit the number of examples
226+ @settings (
227+ max_examples = 10 , # Limit the number of examples
228+ suppress_health_check = [HealthCheck .function_scoped_fixture ],
229+ )
256230 def test_include_resolution (
257- self , main_config : VCSPullConfig , included_configs : list [VCSPullConfig ]
231+ self ,
232+ main_config : VCSPullConfig ,
233+ included_configs : list [VCSPullConfig ],
234+ tmp_path : pathlib .Path ,
258235 ) -> None :
259236 """Test that include resolution properly merges configurations."""
260- with tempfile .TemporaryDirectory () as temp_dir :
261- temp_dir_path = Path (temp_dir )
262-
263- # Create and save included configs
264- included_paths = []
265- for i , include_config in enumerate (included_configs ):
266- include_path = temp_dir_path / f"include{ i } .yaml"
267- save_config (include_config , include_path )
268- included_paths .append (include_path )
237+ # Create and save included configs
238+ included_paths = []
239+ for i , include_config in enumerate (included_configs ):
240+ include_path = tmp_path / f"include{ i } .yaml"
241+ save_config (include_config , include_path )
242+ included_paths .append (include_path )
269243
270- # Update main config's includes to point to the actual files
271- main_config .includes = [str (path ) for path in included_paths ]
244+ # Update main config's includes to point to the actual files
245+ main_config .includes = [str (path ) for path in included_paths ]
272246
273- # Save main config
274- main_path = temp_dir_path / "main.yaml"
275- save_config (main_config , main_path )
247+ # Save main config
248+ main_path = tmp_path / "main.yaml"
249+ save_config (main_config , main_path )
276250
277- # Load and resolve includes
278- loaded_config = load_config (main_path )
279- resolved_config = resolve_includes (loaded_config , main_path .parent )
251+ # Load and resolve includes
252+ loaded_config = load_config (main_path )
253+ resolved_config = resolve_includes (loaded_config , main_path .parent )
280254
281- # Verify all repositories are present in the resolved config
282- all_repos = list (main_config .repositories )
283- for include_config in included_configs :
284- all_repos .extend (include_config .repositories )
255+ # Verify all repositories are present in the resolved config
256+ all_repos = list (main_config .repositories )
257+ for include_config in included_configs :
258+ all_repos .extend (include_config .repositories )
285259
286- # Check that all repositories are present in the resolved config
287- assert len (resolved_config .repositories ) == len (all_repos )
260+ # Check that all repositories are present in the resolved config
261+ assert len (resolved_config .repositories ) == len (all_repos )
288262
289- # Check that includes are cleared
290- assert len (resolved_config .includes ) == 0
263+ # Check that includes are cleared
264+ assert len (resolved_config .includes ) == 0
291265
292- # Verify URLs of repositories match (as a basic check)
293- resolved_urls = {repo .url for repo in resolved_config .repositories }
294- original_urls = {repo .url for repo in all_repos }
295- assert resolved_urls == original_urls
266+ # Verify URLs of repositories match (as a basic check)
267+ resolved_urls = {repo .url for repo in resolved_config .repositories }
268+ original_urls = {repo .url for repo in all_repos }
269+ assert resolved_urls == original_urls
296270
297271 @given (configs = st .lists (vcspull_config_strategy (), min_size = 2 , max_size = 4 ))
298- @settings (max_examples = 10 )
299- def test_nested_includes_resolution (self , configs : list [VCSPullConfig ]) -> None :
272+ @settings (
273+ max_examples = 10 ,
274+ suppress_health_check = [HealthCheck .function_scoped_fixture ],
275+ )
276+ def test_nested_includes_resolution (
277+ self ,
278+ configs : list [VCSPullConfig ],
279+ tmp_path : pathlib .Path ,
280+ ) -> None :
300281 """Test that nested includes are resolved properly."""
301- with tempfile .TemporaryDirectory () as temp_dir :
302- temp_dir_path = Path (temp_dir )
303-
304- # Save configs with nested includes
305- # Last config has no includes
306- paths = []
307- for i , config in enumerate (configs ):
308- config_path = temp_dir_path / f"config{ i } .yaml"
282+ # Save configs with nested includes
283+ # Last config has no includes
284+ paths = []
285+ for i , config in enumerate (configs ):
286+ config_path = tmp_path / f"config{ i } .yaml"
309287
310- # Add includes to each config (except the last one)
311- if i < len (configs ) - 1 :
312- config .includes = [f"config{ i + 1 } .yaml" ]
313- else :
314- config .includes = []
288+ # Add includes to each config (except the last one)
289+ if i < len (configs ) - 1 :
290+ config .includes = [f"config{ i + 1 } .yaml" ]
291+ else :
292+ config .includes = []
315293
316- save_config (config , config_path )
317- paths .append (config_path )
294+ save_config (config , config_path )
295+ paths .append (config_path )
318296
319- # Load and resolve includes for the first config
320- first_config = load_config (paths [0 ])
321- resolved_config = resolve_includes (first_config , temp_dir_path )
297+ # Load and resolve includes for the first config
298+ first_config = load_config (paths [0 ])
299+ resolved_config = resolve_includes (first_config , tmp_path )
322300
323- # Gather all repositories from original configs
324- all_repos = []
325- for config in configs :
326- all_repos .extend (config .repositories )
301+ # Gather all repositories from original configs
302+ all_repos = []
303+ for config in configs :
304+ all_repos .extend (config .repositories )
327305
328- # Check repository count
329- assert len (resolved_config .repositories ) == len (all_repos )
306+ # Check repository count
307+ assert len (resolved_config .repositories ) == len (all_repos )
330308
331- # Check all repositories are included
332- resolved_urls = {repo .url for repo in resolved_config .repositories }
333- original_urls = {repo .url for repo in all_repos }
334- assert resolved_urls == original_urls
309+ # Check all repositories are included
310+ resolved_urls = {repo .url for repo in resolved_config .repositories }
311+ original_urls = {repo .url for repo in all_repos }
312+ assert resolved_urls == original_urls
335313
336- # Check no includes remain
337- assert len (resolved_config .includes ) == 0
314+ # Check no includes remain
315+ assert len (resolved_config .includes ) == 0
338316
339317 @given (config = vcspull_config_strategy ())
340- @settings (max_examples = 10 )
341- def test_save_config_formats (self , config : VCSPullConfig ) -> None :
318+ @settings (
319+ max_examples = 10 ,
320+ suppress_health_check = [HealthCheck .function_scoped_fixture ],
321+ )
322+ def test_save_config_formats (
323+ self , config : VCSPullConfig , tmp_path : pathlib .Path
324+ ) -> None :
342325 """Test that configs can be saved in different formats."""
343- with tempfile .TemporaryDirectory () as temp_dir :
344- temp_dir_path = Path (temp_dir )
345-
346- # Save in YAML format
347- yaml_path = temp_dir_path / "config.yaml"
348- saved_yaml_path = save_config (config , yaml_path , format_type = "yaml" )
349- assert saved_yaml_path .exists ()
350-
351- # Verify YAML file is valid
352- with saved_yaml_path .open () as f :
353- yaml_content = yaml .safe_load (f )
354- assert isinstance (yaml_content , dict )
355-
356- # Save in JSON format
357- json_path = temp_dir_path / "config.json"
358- saved_json_path = save_config (config , json_path , format_type = "json" )
359- assert saved_json_path .exists ()
360-
361- # Verify JSON file is valid
362- with saved_json_path .open () as f :
363- json_content = json .load (f )
364- assert isinstance (json_content , dict )
365-
366- # Load both formats and compare
367- yaml_config = load_config (saved_yaml_path )
368- json_config = load_config (saved_json_path )
369-
370- # Check that both loaded configs match the original
371- assert yaml_config .model_dump () == config .model_dump ()
372- assert json_config .model_dump () == config .model_dump ()
326+ # Save in YAML format
327+ yaml_path = tmp_path / "config.yaml"
328+ saved_yaml_path = save_config (config , yaml_path , format_type = "yaml" )
329+ assert saved_yaml_path .exists ()
330+
331+ # Verify YAML file is valid
332+ with saved_yaml_path .open () as f :
333+ yaml_content = yaml .safe_load (f )
334+ assert isinstance (yaml_content , dict )
335+
336+ # Save in JSON format
337+ json_path = tmp_path / "config.json"
338+ saved_json_path = save_config (config , json_path , format_type = "json" )
339+ assert saved_json_path .exists ()
340+
341+ # Verify JSON file is valid
342+ with saved_json_path .open () as f :
343+ json_content = json .load (f )
344+ assert isinstance (json_content , dict )
345+
346+ # Load both formats and compare
347+ yaml_config = load_config (saved_yaml_path )
348+ json_config = load_config (saved_json_path )
349+
350+ # Check that both loaded configs match the original
351+ assert yaml_config .model_dump () == config .model_dump ()
352+ assert json_config .model_dump () == config .model_dump ()
0 commit comments