"""Tests for CLI quarantine commands. This module tests the CLI interface for quarantine operations. """ import pytest from click.testing import CliRunner from vlm.cli import main @pytest.fixture def temp_library(tmp_path): """Create a temporary library structure for testing.""" library_root = tmp_path / "library" # Create category directories (library_root / "movie").mkdir(parents=True) (library_root / "series").mkdir(parents=True) (library_root / "anime").mkdir(parents=True) (library_root / "other").mkdir(parents=True) return library_root @pytest.fixture def config_file(tmp_path, temp_library): """Create a temporary config file.""" config_path = tmp_path / "config.yaml" config_content = f""" library_root: "{temp_library}" video_extensions: - .mp4 - .mkv - .avi templates: movie_dir: "movie/{{title}} ({{year}})/" series_dir: "series/{{title}}/Season {{season:02d}}/" movie_filename: "{{title}} ({{year}}){{ext}}" series_filename: "S{{season:02d}}E{{episode:02d}}{{ext}}" quarantine_dir: ".quarantine" log_level: "INFO" """ config_path.write_text(config_content) return config_path class TestQuarantineListCommand: """Tests for 'vlm quarantine list' command.""" def test_list_empty_quarantine(self, config_file): """Test listing when no files are quarantined.""" runner = CliRunner() result = runner.invoke(main, ['--config', str(config_file), 'quarantine', 'list']) assert result.exit_code == 0 assert "No quarantined files found" in result.output def test_list_with_quarantined_files(self, config_file, temp_library): """Test listing quarantined files.""" runner = CliRunner() # Create a test movie file movie_file = temp_library / "movie" / "Test Movie (2020).mkv" movie_file.write_text("test content") # Quarantine the file first result = runner.invoke(main, [ '--config', str(config_file), 'quarantine', 'add', str(movie_file) ]) assert result.exit_code == 0 # Now list quarantined files result = runner.invoke(main, ['--config', str(config_file), 'quarantine', 'list']) assert result.exit_code == 0 assert "Test Movie (2020).mkv" in result.output assert "Category: movie" in result.output def test_list_filter_by_category(self, config_file, temp_library): """Test listing with category filter.""" runner = CliRunner() # Create and quarantine a movie file movie_file = temp_library / "movie" / "Movie.mkv" movie_file.write_text("movie content") result = runner.invoke(main, [ '--config', str(config_file), 'quarantine', 'add', str(movie_file) ]) assert result.exit_code == 0 # List only movie category result = runner.invoke(main, [ '--config', str(config_file), 'quarantine', 'list', '--category', 'movie' ]) assert result.exit_code == 0 assert "Movie.mkv" in result.output assert "category 'movie'" in result.output class TestQuarantineAddCommand: """Tests for 'vlm quarantine add' command.""" def test_add_movie_file(self, config_file, temp_library): """Test adding a movie file to quarantine.""" runner = CliRunner() # Create a test movie file movie_file = temp_library / "movie" / "Test Movie (2020).mkv" movie_file.write_text("test content") # Quarantine the file result = runner.invoke(main, [ '--config', str(config_file), 'quarantine', 'add', str(movie_file) ]) assert result.exit_code == 0 assert "successfully quarantined" in result.output assert not movie_file.exists() # Original file should be moved # Check quarantine location quarantine_path = temp_library / "movie" / ".quarantine" / "Test Movie (2020).mkv" assert quarantine_path.exists() assert quarantine_path.read_text() == "test content" def test_add_series_file(self, config_file, temp_library): """Test adding a series file to quarantine.""" runner = CliRunner() # Create a test series file series_file = temp_library / "series" / "Show Name" / "S01E01.mkv" series_file.parent.mkdir(parents=True) series_file.write_text("series content") # Quarantine the file result = runner.invoke(main, [ '--config', str(config_file), 'quarantine', 'add', str(series_file) ]) assert result.exit_code == 0 assert "successfully quarantined" in result.output assert not series_file.exists() def test_add_with_reason(self, config_file, temp_library): """Test adding file with a reason.""" runner = CliRunner() # Create a test movie file movie_file = temp_library / "movie" / "Duplicate.mkv" movie_file.write_text("test content") # Quarantine with reason result = runner.invoke(main, [ '--config', str(config_file), 'quarantine', 'add', str(movie_file), '--reason', 'duplicate file' ]) assert result.exit_code == 0 assert "Reason: duplicate file" in result.output assert "successfully quarantined" in result.output def test_add_anime_file_rejected(self, config_file, temp_library): """Test that anime files are rejected.""" runner = CliRunner() # Create a test anime file anime_file = temp_library / "anime" / "Anime Show.mkv" anime_file.write_text("anime content") # Try to quarantine (should fail) result = runner.invoke(main, [ '--config', str(config_file), 'quarantine', 'add', str(anime_file) ]) assert result.exit_code == 1 assert "not supported" in result.output.lower() assert anime_file.exists() # File should still exist def test_add_nonexistent_file(self, config_file, temp_library): """Test adding a file that doesn't exist.""" runner = CliRunner() # Try to quarantine non-existent file nonexistent = temp_library / "movie" / "DoesNotExist.mkv" result = runner.invoke(main, [ '--config', str(config_file), 'quarantine', 'add', str(nonexistent) ]) # Click should catch this before our code runs assert result.exit_code != 0 class TestQuarantineRestoreCommand: """Tests for 'vlm quarantine restore' command.""" def test_restore_movie_file(self, config_file, temp_library): """Test restoring a movie file from quarantine.""" runner = CliRunner() # Create and quarantine a movie file movie_file = temp_library / "movie" / "Test Movie.mkv" movie_file.write_text("test content") result = runner.invoke(main, [ '--config', str(config_file), 'quarantine', 'add', str(movie_file) ]) assert result.exit_code == 0 # Get quarantine path quarantine_path = temp_library / "movie" / ".quarantine" / "Test Movie.mkv" assert quarantine_path.exists() # Restore the file result = runner.invoke(main, [ '--config', str(config_file), 'quarantine', 'restore', str(quarantine_path) ]) assert result.exit_code == 0 assert "successfully restored" in result.output assert movie_file.exists() # Original location should have file assert not quarantine_path.exists() # Quarantine should be empty assert movie_file.read_text() == "test content" def test_restore_series_file(self, config_file, temp_library): """Test restoring a series file from quarantine.""" runner = CliRunner() # Create and quarantine a series file series_file = temp_library / "series" / "Show" / "S01E01.mkv" series_file.parent.mkdir(parents=True) series_file.write_text("series content") result = runner.invoke(main, [ '--config', str(config_file), 'quarantine', 'add', str(series_file) ]) assert result.exit_code == 0 # Get quarantine path quarantine_path = temp_library / "series" / ".quarantine" / "Show" / "S01E01.mkv" assert quarantine_path.exists() # Restore the file result = runner.invoke(main, [ '--config', str(config_file), 'quarantine', 'restore', str(quarantine_path) ]) assert result.exit_code == 0 assert "successfully restored" in result.output assert series_file.exists() def test_restore_conflict(self, config_file, temp_library): """Test restoring when original location is occupied.""" runner = CliRunner() # Create and quarantine a movie file movie_file = temp_library / "movie" / "Movie.mkv" movie_file.write_text("original content") result = runner.invoke(main, [ '--config', str(config_file), 'quarantine', 'add', str(movie_file) ]) assert result.exit_code == 0 # Create a new file at the original location movie_file.write_text("new content") # Try to restore (should fail due to conflict) quarantine_path = temp_library / "movie" / ".quarantine" / "Movie.mkv" result = runner.invoke(main, [ '--config', str(config_file), 'quarantine', 'restore', str(quarantine_path) ]) assert result.exit_code == 1 assert "Cannot restore" in result.output or "already exists" in result.output assert movie_file.read_text() == "new content" # Original location unchanged assert quarantine_path.exists() # File still in quarantine class TestQuarantineIntegration: """Integration tests for quarantine workflow.""" def test_full_quarantine_workflow(self, config_file, temp_library): """Test complete workflow: add -> list -> restore.""" runner = CliRunner() # Create test files movie1 = temp_library / "movie" / "Movie1.mkv" movie2 = temp_library / "movie" / "Movie2.mkv" movie1.write_text("content1") movie2.write_text("content2") # Add both to quarantine result = runner.invoke(main, [ '--config', str(config_file), 'quarantine', 'add', str(movie1), '--reason', 'duplicate' ]) assert result.exit_code == 0 result = runner.invoke(main, [ '--config', str(config_file), 'quarantine', 'add', str(movie2), '--reason', 'low quality' ]) assert result.exit_code == 0 # List quarantined files result = runner.invoke(main, [ '--config', str(config_file), 'quarantine', 'list' ]) assert result.exit_code == 0 assert "Movie1.mkv" in result.output assert "Movie2.mkv" in result.output assert "Total: 2 quarantined file(s)" in result.output # Restore one file quarantine_path1 = temp_library / "movie" / ".quarantine" / "Movie1.mkv" result = runner.invoke(main, [ '--config', str(config_file), 'quarantine', 'restore', str(quarantine_path1) ]) assert result.exit_code == 0 # List again (should show only 1 file now) result = runner.invoke(main, [ '--config', str(config_file), 'quarantine', 'list' ]) assert result.exit_code == 0 assert "Movie1.mkv" not in result.output assert "Movie2.mkv" in result.output assert "Total: 1 quarantined file(s)" in result.output # Verify restored file assert movie1.exists() assert movie1.read_text() == "content1"