88from unittest .mock import patch
99
1010# Add the parent directory to sys.path so Python can find src
11- sys .path .insert (0 , os .path .abspath (os .path .join (os .path .dirname (__file__ ), ".." )))
11+ sys .path .insert (
12+ 0 , os .path .abspath (os .path .join (os .path .dirname (__file__ ), ".." ))
13+ )
1214
13- from autotarcompress .utils import SizeCalculator
15+ from autotarcompress .utils import (
16+ SizeCalculator ,
17+ ensure_backup_folder ,
18+ validate_and_expand_paths ,
19+ )
1420
1521
1622class TestSizeCalculator :
@@ -30,7 +36,9 @@ def test_size_calculator_initialization(self) -> None:
3036 assert str (calculator .directories [1 ]) == "/test/dir2"
3137 assert len (calculator .ignore_list ) == EXPECTED_IGNORE_COUNT
3238
33- def test_calculate_total_size_with_test_data (self , test_data_dir : str ) -> None :
39+ def test_calculate_total_size_with_test_data (
40+ self , test_data_dir : str
41+ ) -> None :
3442 """Test size calculation with actual test data."""
3543 dirs = [test_data_dir ]
3644 ignore_list : list [str ] = []
@@ -41,10 +49,14 @@ def test_calculate_total_size_with_test_data(self, test_data_dir: str) -> None:
4149 # Should have some size from test files
4250 assert total_size > 0
4351
44- def test_calculate_total_size_with_ignore_list (self , test_data_dir : str ) -> None :
52+ def test_calculate_total_size_with_ignore_list (
53+ self , test_data_dir : str
54+ ) -> None :
4555 """Test size calculation respects ignore list."""
4656 dirs = [test_data_dir ]
47- ignore_list : list [str ] = ["ignored" ] # Should ignore the "ignored" directory
57+ ignore_list : list [str ] = [
58+ "ignored"
59+ ] # Should ignore the "ignored" directory
4860
4961 calculator = SizeCalculator (dirs , ignore_list )
5062 size_with_ignore = calculator .calculate_total_size ()
@@ -58,7 +70,9 @@ def test_calculate_total_size_with_ignore_list(self, test_data_dir: str) -> None
5870 assert size_with_ignore <= size_without_ignore
5971
6072 @patch ("autotarcompress.utils.os.walk" )
61- def test_calculate_total_size_handles_permission_errors (self , mock_walk ) -> None :
73+ def test_calculate_total_size_handles_permission_errors (
74+ self , mock_walk
75+ ) -> None :
6276 """Test that size calculator handles permission errors gracefully."""
6377 # Mock os.walk to raise PermissionError
6478 mock_walk .side_effect = PermissionError ("Permission denied" )
@@ -135,3 +149,83 @@ def test_symlink_handling(self, tmp_path) -> None:
135149 # (from regular file and valid symlink)
136150 total_size = calculator .calculate_total_size ()
137151 assert total_size > 0
152+
153+
154+ def test_validate_and_expand_paths (tmp_path ) -> None :
155+ """Test validate_and_expand_paths returns existing and missing lists."""
156+ existing_dir = tmp_path / "exists"
157+ existing_dir .mkdir ()
158+
159+ missing_dir = tmp_path / "does_not_exist"
160+
161+ # include an empty string which should be skipped
162+ inputs = [str (existing_dir ), str (missing_dir ), "" ]
163+
164+ existing , missing = validate_and_expand_paths (inputs )
165+
166+ # Ensure the existing directory is returned and the missing one is reported
167+ assert str (existing_dir ) in existing
168+ assert str (missing_dir ) in missing
169+
170+
171+ def test_ensure_backup_folder_creates_and_returns_path (tmp_path ) -> None :
172+ """Test ensure_backup_folder creates nested folders and returns a Path."""
173+ nested = tmp_path / "a" / "b" / "c"
174+ # Pass as a string (function will expanduser internally)
175+ result = ensure_backup_folder (str (nested ))
176+
177+ assert result .exists ()
178+ assert result .is_dir ()
179+ # The returned path should match the expanded input path
180+ from pathlib import Path
181+
182+ assert result == Path (str (nested )).expanduser ()
183+
184+
185+ def test_validate_and_expand_paths_handles_none () -> None :
186+ """validate_and_expand_paths should handle a None input gracefully."""
187+ existing , missing = validate_and_expand_paths (None )
188+ assert existing == []
189+ assert missing == []
190+
191+
192+ def test_validate_and_expand_paths_with_file_and_trailing_slash (
193+ tmp_path ,
194+ ) -> None :
195+ """A trailing slash on an existing file should still be reported as existing."""
196+ file = tmp_path / "afile.txt"
197+ file .write_text ("content" )
198+
199+ inputs = [str (file ) + "/" ]
200+ existing , missing = validate_and_expand_paths (inputs )
201+
202+ assert str (file ) in existing
203+ assert missing == []
204+
205+
206+ def test_ensure_backup_folder_idempotent (tmp_path ) -> None :
207+ """ensure_backup_folder should be safe to call multiple times."""
208+ nested = tmp_path / "already" / "exists"
209+ nested .mkdir (parents = True )
210+
211+ first = ensure_backup_folder (str (nested ))
212+ second = ensure_backup_folder (str (nested ))
213+
214+ assert first == second
215+
216+
217+ def test_ensure_backup_folder_permission_error (monkeypatch , tmp_path ) -> None :
218+ """If the folder cannot be created due to permissions, an OSError is raised."""
219+ from pathlib import Path
220+
221+ import pytest
222+
223+ nested = tmp_path / "no_perms"
224+
225+ def fake_mkdir (self , parents = True , exist_ok = True ):
226+ raise PermissionError ("Permission denied" )
227+
228+ monkeypatch .setattr (Path , "mkdir" , fake_mkdir )
229+
230+ with pytest .raises (OSError ):
231+ ensure_backup_folder (str (nested ))
0 commit comments