Initial commit: Video Library Manager

- Add core VLM modules (scanner, parser, planner, executor, analysis)
- Add CLI with quarantine, reports, rollback, and state management
- Add comprehensive test suite
- Add project configuration and documentation
- Add .gitignore for Python project
This commit is contained in:
windyboy
2026-02-09 17:43:35 +08:00
commit 1705275e99
38 changed files with 16694 additions and 0 deletions
+343
View File
@@ -0,0 +1,343 @@
"""Integration tests for CLI report commands.
Tests the report commands to ensure they properly wire to the Report Generator.
"""
import json
import csv
from pathlib import Path
from datetime import datetime, timezone
from click.testing import CliRunner
from vlm.cli import main
class TestCLIReports:
"""Test CLI report commands."""
def test_report_inventory_csv(self, tmp_path):
"""Test inventory report generation in CSV format."""
# Create test inventory CSV
inventory_file = tmp_path / "inventory.csv"
with open(inventory_file, 'w', encoding='utf-8') as f:
f.write("# Generated: 2024-01-01T00:00:00\n")
f.write("# Library Root: /test/library\n")
f.write("path,filename,size_bytes,modified_timestamp,category,resolution,codec,duration_seconds,bitrate_kbps\n")
f.write("/test/library/movie/Movie1.mkv,Movie1.mkv,1000000,2024-01-01T00:00:00,movie,1920x1080,h264,7200,5000\n")
f.write("/test/library/series/Show.S01E01.mkv,Show.S01E01.mkv,800000,2024-01-01T00:00:00,series,1280x720,h264,2700,3000\n")
# Run command
runner = CliRunner()
result = runner.invoke(main, ['report', 'inventory', '--input', str(inventory_file), '--format', 'csv'])
# Verify success
assert result.exit_code == 0
assert "Loaded 2 files" in result.output
assert "Movie1.mkv" in result.output
assert "Show.S01E01.mkv" in result.output
def test_report_inventory_json(self, tmp_path):
"""Test inventory report generation in JSON format."""
# Create test inventory CSV
inventory_file = tmp_path / "inventory.csv"
with open(inventory_file, 'w', encoding='utf-8') as f:
f.write("# Generated: 2024-01-01T00:00:00\n")
f.write("# Library Root: /test/library\n")
f.write("path,filename,size_bytes,modified_timestamp,category,resolution,codec,duration_seconds,bitrate_kbps\n")
f.write("/test/library/movie/Movie1.mkv,Movie1.mkv,1000000,2024-01-01T00:00:00,movie,,,\n")
# Run command with output file
output_file = tmp_path / "inventory_report.json"
runner = CliRunner()
result = runner.invoke(main, [
'report', 'inventory',
'--input', str(inventory_file),
'--format', 'json',
'--output', str(output_file)
])
# Verify success
assert result.exit_code == 0
assert output_file.exists()
# Verify JSON content
with open(output_file, 'r') as f:
data = json.load(f)
assert data['metadata']['file_count'] == 1
assert len(data['files']) == 1
assert data['files'][0]['filename'] == 'Movie1.mkv'
def test_report_completeness_text(self, tmp_path):
"""Test completeness report generation in text format."""
# Create test analysis JSON
analysis_file = tmp_path / "analysis.json"
analysis_data = {
"metadata": {
"generated": "2024-01-01T00:00:00",
"source_identities": "identities.json",
"total_movies": 0,
"total_series": 3
},
"completeness": [
{
"series_title": "Breaking Bad",
"season": 1,
"episodes_found": [1, 2, 4],
"episodes_missing": [3]
}
],
"duplicates": []
}
with open(analysis_file, 'w') as f:
json.dump(analysis_data, f)
# Run command
runner = CliRunner()
result = runner.invoke(main, [
'report', 'completeness',
'--input', str(analysis_file),
'--format', 'text'
])
# Verify success
assert result.exit_code == 0
assert "Loaded 1 series with gaps" in result.output
assert "Breaking Bad" in result.output
assert "Episodes missing:" in result.output
def test_report_completeness_json(self, tmp_path):
"""Test completeness report generation in JSON format."""
# Create test analysis JSON
analysis_file = tmp_path / "analysis.json"
analysis_data = {
"metadata": {},
"completeness": [
{
"series_title": "The Wire",
"season": 1,
"episodes_found": [1, 3],
"episodes_missing": [2]
}
],
"duplicates": []
}
with open(analysis_file, 'w') as f:
json.dump(analysis_data, f)
# Run command with output file
output_file = tmp_path / "completeness_report.json"
runner = CliRunner()
result = runner.invoke(main, [
'report', 'completeness',
'--input', str(analysis_file),
'--format', 'json',
'--output', str(output_file)
])
# Verify success
assert result.exit_code == 0
assert output_file.exists()
# Verify JSON content
with open(output_file, 'r') as f:
data = json.load(f)
assert data['metadata']['series_count'] == 1
assert len(data['series']) == 1
assert data['series'][0]['title'] == 'The Wire'
def test_report_duplicates_text(self, tmp_path):
"""Test duplicate report generation in text format."""
# Create test analysis JSON
analysis_file = tmp_path / "analysis.json"
analysis_data = {
"metadata": {},
"completeness": [],
"duplicates": [
{
"identity": {
"type": "movie",
"title": "The Matrix",
"year": 1999
},
"files": [
"/movies/The.Matrix.1999.1080p.mkv",
"/movies/The.Matrix.1999.720p.mkv"
],
"quality_comparison": [
{
"filename": "The.Matrix.1999.1080p.mkv",
"path": "/movies/The.Matrix.1999.1080p.mkv",
"size_bytes": 2000000000,
"resolution": "1920x1080"
},
{
"filename": "The.Matrix.1999.720p.mkv",
"path": "/movies/The.Matrix.1999.720p.mkv",
"size_bytes": 1000000000,
"resolution": "1280x720"
}
]
}
]
}
with open(analysis_file, 'w') as f:
json.dump(analysis_data, f)
# Run command
runner = CliRunner()
result = runner.invoke(main, [
'report', 'duplicates',
'--input', str(analysis_file),
'--format', 'text'
])
# Verify success
assert result.exit_code == 0
assert "Loaded 1 duplicate groups" in result.output
assert "The Matrix (1999)" in result.output
assert "1920x1080" in result.output
def test_report_duplicates_json(self, tmp_path):
"""Test duplicate report generation in JSON format."""
# Create test analysis JSON
analysis_file = tmp_path / "analysis.json"
analysis_data = {
"metadata": {},
"completeness": [],
"duplicates": [
{
"identity": {
"type": "series",
"title": "Breaking Bad",
"season": 1,
"episodes": [1]
},
"files": [
"/series/Breaking.Bad.S01E01.1080p.mkv",
"/series/Breaking.Bad.S01E01.720p.mkv"
],
"quality_comparison": [
{
"filename": "Breaking.Bad.S01E01.1080p.mkv",
"path": "/series/Breaking.Bad.S01E01.1080p.mkv",
"size_bytes": 1500000000
},
{
"filename": "Breaking.Bad.S01E01.720p.mkv",
"path": "/series/Breaking.Bad.S01E01.720p.mkv",
"size_bytes": 800000000
}
]
}
]
}
with open(analysis_file, 'w') as f:
json.dump(analysis_data, f)
# Run command with output file
output_file = tmp_path / "duplicates_report.json"
runner = CliRunner()
result = runner.invoke(main, [
'report', 'duplicates',
'--input', str(analysis_file),
'--format', 'json',
'--output', str(output_file)
])
# Verify success
assert result.exit_code == 0
assert output_file.exists()
# Verify JSON content
with open(output_file, 'r') as f:
data = json.load(f)
assert data['metadata']['duplicate_groups'] == 1
assert len(data['duplicates']) == 1
def test_report_summary(self, tmp_path):
"""Test summary report generation."""
# Create test inventory CSV
inventory_file = tmp_path / "inventory.csv"
with open(inventory_file, 'w', encoding='utf-8') as f:
f.write("# Generated: 2024-01-01T00:00:00\n")
f.write("# Library Root: /test/library\n")
f.write("path,filename,size_bytes,modified_timestamp,category,resolution,codec,duration_seconds,bitrate_kbps\n")
f.write("/test/library/movie/Movie1.mkv,Movie1.mkv,2000000000,2024-01-01T00:00:00,movie,,,\n")
f.write("/test/library/movie/Movie2.mkv,Movie2.mkv,1500000000,2024-01-01T00:00:00,movie,,,\n")
f.write("/test/library/series/Show.S01E01.mkv,Show.S01E01.mkv,1000000000,2024-01-01T00:00:00,series,,,\n")
f.write("/test/library/anime/Anime1.mkv,Anime1.mkv,800000000,2024-01-01T00:00:00,anime,,,\n")
# Run command
runner = CliRunner()
result = runner.invoke(main, [
'report', 'summary',
'--input', str(inventory_file)
])
# Verify success
assert result.exit_code == 0
assert "Loaded 4 files" in result.output
assert "Total Files: 4" in result.output
assert "Movie:" in result.output
assert "Series:" in result.output
assert "Anime:" in result.output
def test_report_summary_with_output_file(self, tmp_path):
"""Test summary report generation with output file."""
# Create test inventory CSV
inventory_file = tmp_path / "inventory.csv"
with open(inventory_file, 'w', encoding='utf-8') as f:
f.write("# Generated: 2024-01-01T00:00:00\n")
f.write("# Library Root: /test/library\n")
f.write("path,filename,size_bytes,modified_timestamp,category,resolution,codec,duration_seconds,bitrate_kbps\n")
f.write("/test/library/movie/Movie1.mkv,Movie1.mkv,1000000,2024-01-01T00:00:00,movie,,,\n")
# Run command with output file
output_file = tmp_path / "summary_report.txt"
runner = CliRunner()
result = runner.invoke(main, [
'report', 'summary',
'--input', str(inventory_file),
'--output', str(output_file)
])
# Verify success
assert result.exit_code == 0
assert output_file.exists()
# Verify file content
with open(output_file, 'r') as f:
content = f.read()
assert "Total Files: 1" in content
assert "Movie:" in content
def test_report_inventory_missing_file(self, tmp_path):
"""Test inventory report with missing input file."""
runner = CliRunner()
result = runner.invoke(main, [
'report', 'inventory',
'--input', str(tmp_path / "nonexistent.csv")
])
# Verify error (Click validates file existence before our code runs)
assert result.exit_code != 0
assert "does not exist" in result.output
def test_report_completeness_missing_file(self, tmp_path):
"""Test completeness report with missing input file."""
runner = CliRunner()
result = runner.invoke(main, [
'report', 'completeness',
'--input', str(tmp_path / "nonexistent.json")
])
# Verify error (Click validates file existence before our code runs)
assert result.exit_code != 0
assert "does not exist" in result.output