Files
dl-organizer/tests/test_cli_state.py
T
6f0df5a774 release: v0.2.0 repository hygiene, CI, and docs sync
Stop tracking personal workflow artifacts at repo root, add CI and MIT
license, align README and agent skills with artifacts/ defaults, and
enable Ruff in dev/CI so releases are verifiable without local-only runs.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-01 15:32:03 +08:00

239 lines
6.6 KiB
Python

"""Tests for CLI state commands."""
from pathlib import Path
import pytest
from click.testing import CliRunner
from vlm.cli import default_config_path, main
@pytest.fixture
def runner():
"""Create a Click CLI test runner."""
return CliRunner()
@pytest.fixture
def temp_state_file(tmp_path, monkeypatch):
"""Create a temporary state file and set up environment."""
state_dir = tmp_path / ".vlm"
state_dir.mkdir(parents=True, exist_ok=True)
state_file = state_dir / "state.json"
# Mock the home directory to use tmp_path
monkeypatch.setattr(Path, 'home', lambda: tmp_path)
return state_file
@pytest.fixture
def temp_config(tmp_path):
"""Create a temporary config file."""
config_dir = tmp_path / ".vlm"
config_dir.mkdir(parents=True, exist_ok=True)
config_file = config_dir / "config.yaml"
# Create a minimal config
config_content = f"""
library_root: {tmp_path / "videos"}
video_extensions:
- .mp4
- .mkv
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_file.write_text(config_content)
return config_file
def test_state_set_and_show(runner, temp_state_file, temp_config):
"""Test setting and showing file state."""
test_file = Path("/test/movie.mkv")
# Set state
result = runner.invoke(main, [
'--config', str(temp_config),
'state', 'set', str(test_file),
'--status', 'reviewed',
'--reason', 'checked manually'
])
assert result.exit_code == 0
assert "State updated" in result.output
assert "reviewed" in result.output
# Verify state file was created
assert temp_state_file.exists()
# Show state
result = runner.invoke(main, [
'--config', str(temp_config),
'state', 'show', str(test_file)
])
assert result.exit_code == 0
assert "reviewed" in result.output
assert "checked manually" in result.output
def test_default_config_path_resolves_at_runtime(tmp_path, monkeypatch):
"""Test CLI default config path follows current home directory."""
monkeypatch.setattr(Path, 'home', lambda: tmp_path)
assert default_config_path() == tmp_path / ".vlm" / "config.yaml"
def test_state_query(runner, temp_state_file, temp_config):
"""Test querying files by status."""
test_files = [
Path("/test/movie1.mkv"),
Path("/test/movie2.mkv"),
Path("/test/movie3.mkv")
]
# Set states for multiple files
for i, test_file in enumerate(test_files):
status = 'ignored' if i < 2 else 'reviewed'
result = runner.invoke(main, [
'--config', str(temp_config),
'state', 'set', str(test_file),
'--status', status
])
assert result.exit_code == 0
# Query for ignored files
result = runner.invoke(main, [
'--config', str(temp_config),
'state', 'query',
'--status', 'ignored'
])
assert result.exit_code == 0
assert "movie1.mkv" in result.output
assert "movie2.mkv" in result.output
assert "movie3.mkv" not in result.output
assert "Total: 2 file(s)" in result.output
def test_state_clear(runner, temp_state_file, temp_config):
"""Test clearing file state."""
test_file = Path("/test/movie.mkv")
# Set state
result = runner.invoke(main, [
'--config', str(temp_config),
'state', 'set', str(test_file),
'--status', 'reviewed'
])
assert result.exit_code == 0
# Clear state
result = runner.invoke(main, [
'--config', str(temp_config),
'state', 'clear', str(test_file)
])
assert result.exit_code == 0
assert "State cleared" in result.output
# Verify state is cleared
result = runner.invoke(main, [
'--config', str(temp_config),
'state', 'show', str(test_file)
])
assert result.exit_code == 0
assert "No state found" in result.output
def test_state_set_invalid_status(runner, temp_state_file, temp_config):
"""Test setting state with invalid status."""
test_file = Path("/test/movie.mkv")
result = runner.invoke(main, [
'--config', str(temp_config),
'state', 'set', str(test_file),
'--status', 'invalid_status'
])
# Should fail due to invalid choice
assert result.exit_code != 0
def test_state_show_nonexistent(runner, temp_state_file, temp_config):
"""Test showing state for file that doesn't have state."""
test_file = Path("/test/nonexistent.mkv")
result = runner.invoke(main, [
'--config', str(temp_config),
'state', 'show', str(test_file)
])
assert result.exit_code == 0
assert "No state found" in result.output
def test_state_clear_nonexistent(runner, temp_state_file, temp_config):
"""Test clearing state for file that doesn't have state."""
test_file = Path("/test/nonexistent.mkv")
result = runner.invoke(main, [
'--config', str(temp_config),
'state', 'clear', str(test_file)
])
assert result.exit_code == 0
assert "No state found" in result.output
assert "Nothing to clear" in result.output
def test_state_query_empty(runner, temp_state_file, temp_config):
"""Test querying when no files have the status."""
result = runner.invoke(main, [
'--config', str(temp_config),
'state', 'query',
'--status', 'quarantined'
])
assert result.exit_code == 0
assert "No files found" in result.output
def test_state_set_idempotent(runner, temp_state_file, temp_config):
"""Test that setting state multiple times is idempotent."""
test_file = Path("/test/movie.mkv")
# Set state first time
result = runner.invoke(main, [
'--config', str(temp_config),
'state', 'set', str(test_file),
'--status', 'reviewed',
'--reason', 'first check'
])
assert result.exit_code == 0
# Set state second time with different reason
result = runner.invoke(main, [
'--config', str(temp_config),
'state', 'set', str(test_file),
'--status', 'reviewed',
'--reason', 'second check'
])
assert result.exit_code == 0
# Verify the reason was updated
result = runner.invoke(main, [
'--config', str(temp_config),
'state', 'show', str(test_file)
])
assert result.exit_code == 0
assert "second check" in result.output
assert "first check" not in result.output