@@ -336,6 +336,172 @@ def test_both_custom_paths(self):
336336 assert args .source_root == source_path
337337 assert args .target_root == target_path
338338
339+ def test_verbose_flag (self ):
340+ """Test that --verbose flag is parsed correctly."""
341+ with patch ("sys.argv" , ["relink.py" , "--verbose" ]):
342+ args = relink .parse_arguments ()
343+ assert args .verbose is True
344+ assert args .quiet is False
345+
346+ def test_quiet_flag (self ):
347+ """Test that --quiet flag is parsed correctly."""
348+ with patch ("sys.argv" , ["relink.py" , "--quiet" ]):
349+ args = relink .parse_arguments ()
350+ assert args .quiet is True
351+ assert args .verbose is False
352+
353+ def test_verbose_short_flag (self ):
354+ """Test that -v flag is parsed correctly."""
355+ with patch ("sys.argv" , ["relink.py" , "-v" ]):
356+ args = relink .parse_arguments ()
357+ assert args .verbose is True
358+
359+ def test_quiet_short_flag (self ):
360+ """Test that -q flag is parsed correctly."""
361+ with patch ("sys.argv" , ["relink.py" , "-q" ]):
362+ args = relink .parse_arguments ()
363+ assert args .quiet is True
364+
365+ def test_default_verbosity (self ):
366+ """Test that default verbosity has both flags as False."""
367+ with patch ("sys.argv" , ["relink.py" ]):
368+ args = relink .parse_arguments ()
369+ assert args .verbose is False
370+ assert args .quiet is False
371+
372+ def test_verbose_and_quiet_mutually_exclusive (self ):
373+ """Test that --verbose and --quiet cannot be used together."""
374+ with patch ("sys.argv" , ["relink.py" , "--verbose" , "--quiet" ]):
375+ with pytest .raises (SystemExit ) as exc_info :
376+ relink .parse_arguments ()
377+ # Mutually exclusive arguments cause SystemExit with code 2
378+ assert exc_info .value .code == 2
379+
380+ def test_verbose_and_quiet_short_flags_mutually_exclusive (self ):
381+ """Test that -v and -q cannot be used together."""
382+ with patch ("sys.argv" , ["relink.py" , "-v" , "-q" ]):
383+ with pytest .raises (SystemExit ) as exc_info :
384+ relink .parse_arguments ()
385+ # Mutually exclusive arguments cause SystemExit with code 2
386+ assert exc_info .value .code == 2
387+
388+
389+ class TestVerbosityLevels :
390+ """Test suite for verbosity level behavior."""
391+
392+ @pytest .fixture
393+ def temp_dirs (self ):
394+ """Create temporary source and target directories for testing."""
395+ source_dir = tempfile .mkdtemp (prefix = "test_source_" )
396+ target_dir = tempfile .mkdtemp (prefix = "test_target_" )
397+
398+ yield source_dir , target_dir
399+
400+ # Cleanup
401+ shutil .rmtree (source_dir , ignore_errors = True )
402+ shutil .rmtree (target_dir , ignore_errors = True )
403+
404+ def test_quiet_mode_suppresses_info_messages (self , temp_dirs , caplog ):
405+ """Test that quiet mode suppresses INFO level messages."""
406+ source_dir , target_dir = temp_dirs
407+ username = os .environ ["USER" ]
408+
409+ # Create files
410+ source_file = os .path .join (source_dir , "test_file.txt" )
411+ target_file = os .path .join (target_dir , "test_file.txt" )
412+
413+ with open (source_file , "w" , encoding = "utf-8" ) as f :
414+ f .write ("source" )
415+ with open (target_file , "w" , encoding = "utf-8" ) as f :
416+ f .write ("target" )
417+
418+ # Create a symlink to test "Skipping symlink" message
419+ source_link = os .path .join (source_dir , "existing_link.txt" )
420+ dummy_target = os .path .join (tempfile .gettempdir (), "somewhere" )
421+ os .symlink (dummy_target , source_link )
422+
423+ # Run the function with WARNING level (quiet mode)
424+ with caplog .at_level (logging .WARNING ):
425+ relink .find_and_replace_owned_files (source_dir , target_dir , username )
426+
427+ # Verify INFO messages are NOT in the log
428+ assert "Searching for files owned by" not in caplog .text
429+ assert "Skipping symlink:" not in caplog .text
430+ assert "Found owned file:" not in caplog .text
431+ assert "Deleted original file:" not in caplog .text
432+ assert "Created symbolic link:" not in caplog .text
433+
434+ def test_quiet_mode_shows_warnings (self , temp_dirs , caplog ):
435+ """Test that quiet mode still shows WARNING level messages."""
436+ source_dir , target_dir = temp_dirs
437+ username = os .environ ["USER" ]
438+
439+ # Create only source file (no corresponding target) to trigger warning
440+ source_file = os .path .join (source_dir , "orphan.txt" )
441+ with open (source_file , "w" , encoding = "utf-8" ) as f :
442+ f .write ("orphan content" )
443+
444+ # Run the function with WARNING level (quiet mode)
445+ with caplog .at_level (logging .WARNING ):
446+ relink .find_and_replace_owned_files (source_dir , target_dir , username )
447+
448+ # Verify WARNING message IS in the log
449+ assert "Warning: Corresponding file not found" in caplog .text
450+
451+ def test_quiet_mode_shows_errors (self , temp_dirs , caplog ):
452+ """Test that quiet mode still shows ERROR level messages."""
453+ source_dir , target_dir = temp_dirs
454+ username = os .environ ["USER" ]
455+
456+ # Test 1: Invalid username error
457+ invalid_username = "nonexistent_user_12345"
458+ with caplog .at_level (logging .WARNING ):
459+ relink .find_and_replace_owned_files (
460+ source_dir , target_dir , invalid_username
461+ )
462+ assert "Error: User" in caplog .text
463+ assert "not found" in caplog .text
464+
465+ # Clear the log for next test
466+ caplog .clear ()
467+
468+ # Test 2: Error deleting file
469+ source_file = os .path .join (source_dir , "test.txt" )
470+ target_file = os .path .join (target_dir , "test.txt" )
471+
472+ with open (source_file , "w" , encoding = "utf-8" ) as f :
473+ f .write ("source" )
474+ with open (target_file , "w" , encoding = "utf-8" ) as f :
475+ f .write ("target" )
476+
477+ def mock_rename (src , dst ):
478+ raise OSError ("Simulated rename error" )
479+
480+ with patch ("os.rename" , side_effect = mock_rename ):
481+ with caplog .at_level (logging .WARNING ):
482+ relink .find_and_replace_owned_files (source_dir , target_dir , username )
483+ assert "Error deleting file" in caplog .text
484+
485+ # Clear the log for next test
486+ caplog .clear ()
487+
488+ # Test 3: Error creating symlink
489+ source_file2 = os .path .join (source_dir , "test2.txt" )
490+ target_file2 = os .path .join (target_dir , "test2.txt" )
491+
492+ with open (source_file2 , "w" , encoding = "utf-8" ) as f :
493+ f .write ("source2" )
494+ with open (target_file2 , "w" , encoding = "utf-8" ) as f :
495+ f .write ("target2" )
496+
497+ def mock_symlink (src , dst ):
498+ raise OSError ("Simulated symlink error" )
499+
500+ with patch ("os.symlink" , side_effect = mock_symlink ):
501+ with caplog .at_level (logging .WARNING ):
502+ relink .find_and_replace_owned_files (source_dir , target_dir , username )
503+ assert "Error creating symlink" in caplog .text
504+
339505
340506class TestEdgeCases :
341507 """Test edge cases and error handling."""
0 commit comments