77import tempfile
88import shutil
99import pwd
10+ import logging
1011from unittest .mock import patch
12+
1113import pytest
1214
1315# Add parent directory to path to import relink module
1416sys .path .insert (0 , os .path .dirname (os .path .dirname (os .path .abspath (__file__ ))))
15- import relink
17+ import relink # noqa: E402
18+
19+
20+ @pytest .fixture (scope = "function" , autouse = True )
21+ def configure_logging ():
22+ """Configure logging to output to stdout for all tests."""
23+ # Configure logging before each test
24+ logging .basicConfig (
25+ level = logging .INFO ,
26+ format = "%(message)s" ,
27+ stream = sys .stdout ,
28+ force = True , # Force reconfiguration
29+ )
30+ yield
31+ # Clean up logging handlers after each test
32+ logging .getLogger ().handlers .clear ()
1633
1734
1835class TestFindAndReplaceOwnedFiles :
@@ -86,7 +103,7 @@ def test_nested_directory_structure(self, temp_dirs, current_user):
86103 assert os .path .islink (source_file ), "Nested file should be a symlink"
87104 assert os .readlink (source_file ) == target_file
88105
89- def test_skip_existing_symlinks (self , temp_dirs , current_user , capsys ):
106+ def test_skip_existing_symlinks (self , temp_dirs , current_user , caplog ):
90107 """Test that existing symlinks are skipped."""
91108 source_dir , target_dir = temp_dirs
92109 username = current_user
@@ -107,7 +124,8 @@ def test_skip_existing_symlinks(self, temp_dirs, current_user, capsys):
107124 mtime_before = stat_before .st_mtime
108125
109126 # Run the function
110- relink .find_and_replace_owned_files (source_dir , target_dir , username )
127+ with caplog .at_level (logging .INFO ):
128+ relink .find_and_replace_owned_files (source_dir , target_dir , username )
111129
112130 # Verify the symlink is unchanged (same inode means it wasn't deleted/recreated)
113131 stat_after = os .lstat (source_link )
@@ -119,12 +137,11 @@ def test_skip_existing_symlinks(self, temp_dirs, current_user, capsys):
119137 os .readlink (source_link ) == dummy_target
120138 ), "Symlink target should be unchanged"
121139
122- # Check that "Skipping symlink" message was printed
123- captured = capsys .readouterr ()
124- assert "Skipping symlink:" in captured .out
125- assert source_link in captured .out
140+ # Check that "Skipping symlink" message was logged
141+ assert "Skipping symlink:" in caplog .text
142+ assert source_link in caplog .text
126143
127- def test_missing_target_file (self , temp_dirs , current_user , capsys ):
144+ def test_missing_target_file (self , temp_dirs , current_user , caplog ):
128145 """Test behavior when target file doesn't exist."""
129146 source_dir , target_dir = temp_dirs
130147 username = current_user
@@ -135,17 +152,17 @@ def test_missing_target_file(self, temp_dirs, current_user, capsys):
135152 f .write ("orphan content" )
136153
137154 # Run the function
138- relink .find_and_replace_owned_files (source_dir , target_dir , username )
155+ with caplog .at_level (logging .INFO ):
156+ relink .find_and_replace_owned_files (source_dir , target_dir , username )
139157
140158 # Verify the file is NOT converted to symlink
141159 assert not os .path .islink (source_file ), "File should not be a symlink"
142160 assert os .path .isfile (source_file ), "Original file should still exist"
143161
144162 # Check warning message
145- captured = capsys .readouterr ()
146- assert "Warning: Corresponding file not found" in captured .out
163+ assert "Warning: Corresponding file not found" in caplog .text
147164
148- def test_invalid_username (self , temp_dirs , capsys ):
165+ def test_invalid_username (self , temp_dirs , caplog ):
149166 """Test behavior with invalid username."""
150167 source_dir , target_dir = temp_dirs
151168
@@ -159,12 +176,14 @@ def test_invalid_username(self, temp_dirs, capsys):
159176 raise RuntimeError (f"{ invalid_username = } DOES actually exist" )
160177
161178 # Run the function
162- relink .find_and_replace_owned_files (source_dir , target_dir , invalid_username )
179+ with caplog .at_level (logging .INFO ):
180+ relink .find_and_replace_owned_files (
181+ source_dir , target_dir , invalid_username
182+ )
163183
164184 # Check error message
165- captured = capsys .readouterr ()
166- assert "Error: User" in captured .out
167- assert "not found" in captured .out
185+ assert "Error: User" in caplog .text
186+ assert "not found" in caplog .text
168187
169188 def test_multiple_files (self , temp_dirs , current_user ):
170189 """Test with multiple files in the directory."""
@@ -220,20 +239,20 @@ def test_absolute_paths(self, temp_dirs, current_user):
220239 finally :
221240 os .chdir (cwd )
222241
223- def test_print_searching_message (self , temp_dirs , current_user , capsys ):
242+ def test_print_searching_message (self , temp_dirs , current_user , caplog ):
224243 """Test that searching message is printed."""
225244 source_dir , target_dir = temp_dirs
226245 username = current_user
227246
228247 # Run the function
229- relink .find_and_replace_owned_files (source_dir , target_dir , username )
248+ with caplog .at_level (logging .INFO ):
249+ relink .find_and_replace_owned_files (source_dir , target_dir , username )
230250
231- # Check that searching message was printed
232- captured = capsys .readouterr ()
233- assert f"Searching for files owned by '{ username } '" in captured .out
234- assert f"in '{ os .path .abspath (source_dir )} '" in captured .out
251+ # Check that searching message was logged
252+ assert f"Searching for files owned by '{ username } '" in caplog .text
253+ assert f"in '{ os .path .abspath (source_dir )} '" in caplog .text
235254
236- def test_print_found_owned_file (self , temp_dirs , current_user , capsys ):
255+ def test_print_found_owned_file (self , temp_dirs , current_user , caplog ):
237256 """Test that 'Found owned file' message is printed."""
238257 source_dir , target_dir = temp_dirs
239258 username = current_user
@@ -248,14 +267,14 @@ def test_print_found_owned_file(self, temp_dirs, current_user, capsys):
248267 f .write ("target content" )
249268
250269 # Run the function
251- relink .find_and_replace_owned_files (source_dir , target_dir , username )
270+ with caplog .at_level (logging .INFO ):
271+ relink .find_and_replace_owned_files (source_dir , target_dir , username )
252272
253- # Check that "Found owned file" message was printed
254- captured = capsys .readouterr ()
255- assert "Found owned file:" in captured .out
256- assert source_file in captured .out
273+ # Check that "Found owned file" message was logged
274+ assert "Found owned file:" in caplog .text
275+ assert source_file in caplog .text
257276
258- def test_print_deleted_and_created_messages (self , temp_dirs , current_user , capsys ):
277+ def test_print_deleted_and_created_messages (self , temp_dirs , current_user , caplog ):
259278 """Test that deleted and created symlink messages are printed."""
260279 source_dir , target_dir = temp_dirs
261280 username = current_user
@@ -270,13 +289,13 @@ def test_print_deleted_and_created_messages(self, temp_dirs, current_user, capsy
270289 f .write ("target" )
271290
272291 # Run the function
273- relink .find_and_replace_owned_files (source_dir , target_dir , username )
292+ with caplog .at_level (logging .INFO ):
293+ relink .find_and_replace_owned_files (source_dir , target_dir , username )
274294
275295 # Check messages
276- captured = capsys .readouterr ()
277- assert "Deleted original file:" in captured .out
278- assert "Created symbolic link:" in captured .out
279- assert f"{ source_file } -> { target_file } " in captured .out
296+ assert "Deleted original file:" in caplog .text
297+ assert "Created symbolic link:" in caplog .text
298+ assert f"{ source_file } -> { target_file } " in caplog .text
280299
281300
282301class TestParseArguments :
@@ -387,7 +406,7 @@ def test_file_with_special_characters(self, temp_dirs):
387406 assert os .path .islink (source_file )
388407 assert os .readlink (source_file ) == target_file
389408
390- def test_error_deleting_file (self , temp_dirs , capsys ):
409+ def test_error_deleting_file (self , temp_dirs , caplog ):
391410 """Test error message when file deletion fails."""
392411 source_dir , target_dir = temp_dirs
393412 username = os .environ ["USER" ]
@@ -407,14 +426,14 @@ def mock_rename(src, dst):
407426
408427 with patch ("os.rename" , side_effect = mock_rename ):
409428 # Run the function
410- relink .find_and_replace_owned_files (source_dir , target_dir , username )
429+ with caplog .at_level (logging .INFO ):
430+ relink .find_and_replace_owned_files (source_dir , target_dir , username )
411431
412432 # Check error message
413- captured = capsys .readouterr ()
414- assert "Error deleting file" in captured .out
415- assert source_file in captured .out
433+ assert "Error deleting file" in caplog .text
434+ assert source_file in caplog .text
416435
417- def test_error_creating_symlink (self , temp_dirs , capsys ):
436+ def test_error_creating_symlink (self , temp_dirs , caplog ):
418437 """Test error message when symlink creation fails."""
419438 source_dir , target_dir = temp_dirs
420439 username = os .environ ["USER" ]
@@ -434,9 +453,9 @@ def mock_symlink(src, dst):
434453
435454 with patch ("os.symlink" , side_effect = mock_symlink ):
436455 # Run the function
437- relink .find_and_replace_owned_files (source_dir , target_dir , username )
456+ with caplog .at_level (logging .INFO ):
457+ relink .find_and_replace_owned_files (source_dir , target_dir , username )
438458
439459 # Check error message
440- captured = capsys .readouterr ()
441- assert "Error creating symlink" in captured .out
442- assert source_file in captured .out
460+ assert "Error creating symlink" in caplog .text
461+ assert source_file in caplog .text
0 commit comments