Files
dl-organizer/tests/test_cli_quarantine.py
T

380 lines
12 KiB
Python
Raw Normal View History

2026-02-09 17:43:35 +08:00
"""Tests for CLI quarantine commands.
This module tests the CLI interface for quarantine operations.
"""
import json
from pathlib import Path
from click.testing import CliRunner
import pytest
from vlm.cli import main
from vlm.config import Config
@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"