DLO-16: Reduce cli.py from 1073 to 83 lines by registering Click commands from commands/*.py modules DLO-17: Migrate Config to Pydantic BaseModel for validation DLO-18: Split planner.py (826 lines) into orchestration, path rendering, and duplicate handling modules DLO-20: Unify type system — convert 14 dataclasses to Pydantic BaseModel, keep TypedDicts as JSON schema hints Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
181 lines
6.7 KiB
Python
181 lines
6.7 KiB
Python
"""Integration tests for report generation with analysis engine.
|
|
|
|
Tests the complete workflow from analysis to report generation.
|
|
"""
|
|
|
|
import json
|
|
from datetime import datetime, timezone
|
|
from pathlib import Path
|
|
|
|
from vlm.analysis import analyze_series_completeness, detect_duplicates
|
|
from vlm.models import MovieIdentity, SeriesIdentity, VideoFile
|
|
from vlm.reports import (
|
|
generate_completeness_report,
|
|
generate_duplicate_report,
|
|
generate_summary_report,
|
|
)
|
|
|
|
|
|
def _movie(title="Movie", year=2020, **kw):
|
|
return MovieIdentity(
|
|
title=title, year=year, confidence=kw.pop("confidence", 0.9),
|
|
needs_review=kw.pop("needs_review", False),
|
|
original_filename=kw.pop("original_filename", f"{title}.{year}.mkv"),
|
|
**kw,
|
|
)
|
|
|
|
|
|
def _series(title="Show", season=1, episodes=None, **kw):
|
|
if episodes is None:
|
|
episodes = [1]
|
|
return SeriesIdentity(
|
|
title=title, season=season, episodes=episodes,
|
|
confidence=kw.pop("confidence", 0.9),
|
|
needs_review=kw.pop("needs_review", False),
|
|
original_filename=kw.pop(
|
|
"original_filename",
|
|
f"{title.replace(' ', '.')}.S{season:02d}E01.mkv"
|
|
if season is not None
|
|
else f"{title.replace(' ', '.')}.E01.mkv",
|
|
),
|
|
**kw,
|
|
)
|
|
|
|
|
|
def _video(filename="file.mkv", size=1000, category="movie", **kw):
|
|
return VideoFile(
|
|
path=kw.pop("path", Path(f"/tmp/{filename}")),
|
|
filename=filename, size_bytes=size,
|
|
modified_timestamp=kw.pop("modified_timestamp", datetime(2023, 1, 1)),
|
|
category=category, **kw,
|
|
)
|
|
|
|
|
|
class TestReportsIntegration:
|
|
"""Test report generation integrated with analysis engine."""
|
|
|
|
def test_completeness_workflow(self):
|
|
"""Test complete workflow from series analysis to completeness report."""
|
|
# Create test episodes with gaps
|
|
episodes = [
|
|
_series("Breaking Bad", episodes=[1],
|
|
original_filename="Breaking.Bad.S01E01.mkv"),
|
|
_series("Breaking Bad", episodes=[2],
|
|
original_filename="Breaking.Bad.S01E02.mkv"),
|
|
_series("Breaking Bad", episodes=[4],
|
|
original_filename="Breaking.Bad.S01E04.mkv"),
|
|
_series("The Wire", episodes=[1],
|
|
original_filename="The.Wire.S01E01.mkv"),
|
|
_series("The Wire", episodes=[3],
|
|
original_filename="The.Wire.S01E03.mkv"),
|
|
]
|
|
|
|
# Analyze completeness
|
|
analysis = analyze_series_completeness(episodes)
|
|
|
|
# Generate text report
|
|
library_root = Path("/mnt/nas/videos")
|
|
text_report = generate_completeness_report(analysis, "text", library_root)
|
|
|
|
# Verify report contains expected information
|
|
assert "Breaking Bad" in text_report
|
|
assert "The Wire" in text_report
|
|
assert "Episodes missing:" in text_report
|
|
|
|
# Generate JSON report
|
|
json_report = generate_completeness_report(analysis, "json", library_root)
|
|
data = json.loads(json_report)
|
|
|
|
# Verify JSON structure
|
|
assert data["metadata"]["series_count"] == 2
|
|
assert len(data["series"]) == 2
|
|
|
|
def test_duplicate_workflow(self):
|
|
"""Test complete workflow from duplicate detection to duplicate report."""
|
|
# Create test identities and files
|
|
now = datetime.now(timezone.utc)
|
|
identities = [
|
|
_movie("The Matrix", 1999,
|
|
original_filename="The.Matrix.1999.1080p.mkv"),
|
|
_movie("The Matrix", 1999,
|
|
original_filename="The.Matrix.1999.720p.mkv"),
|
|
_movie("Inception", 2010),
|
|
]
|
|
|
|
files = [
|
|
_video("The.Matrix.1999.1080p.mkv", 2_000_000_000,
|
|
modified_timestamp=now, resolution="1920x1080", codec="h264"),
|
|
_video("The.Matrix.1999.720p.mkv", 1_000_000_000,
|
|
modified_timestamp=now, resolution="1280x720", codec="h264"),
|
|
_video("Inception.2010.mkv", 1_500_000_000,
|
|
modified_timestamp=now),
|
|
]
|
|
|
|
# Detect duplicates
|
|
duplicates = detect_duplicates(list(zip(identities, files)))
|
|
|
|
# Generate text report
|
|
library_root = Path("/mnt/nas/videos")
|
|
text_report = generate_duplicate_report(duplicates, "text", library_root)
|
|
|
|
# Verify report contains expected information
|
|
assert "The Matrix (1999)" in text_report
|
|
assert "1920x1080" in text_report
|
|
assert "1280x720" in text_report
|
|
|
|
# Generate JSON report
|
|
json_report = generate_duplicate_report(duplicates, "json", library_root)
|
|
data = json.loads(json_report)
|
|
|
|
# Verify JSON structure
|
|
assert data["metadata"]["duplicate_groups"] == 1
|
|
assert len(data["duplicates"]) == 1
|
|
assert data["duplicates"][0]["file_count"] == 2
|
|
|
|
def test_summary_workflow(self):
|
|
"""Test summary report generation with mixed file types."""
|
|
# Create test files
|
|
now = datetime.now(timezone.utc)
|
|
files = [
|
|
_video("Movie1.mkv", 2_000_000_000, "movie",
|
|
modified_timestamp=now),
|
|
_video("Movie2.mkv", 1_500_000_000, "movie",
|
|
modified_timestamp=now),
|
|
_video("Show.S01E01.mkv", 1_000_000_000, "series",
|
|
modified_timestamp=now),
|
|
_video("Show.S01E02.mkv", 1_000_000_000, "series",
|
|
modified_timestamp=now),
|
|
_video("Anime1.mkv", 800_000_000, "anime",
|
|
modified_timestamp=now),
|
|
]
|
|
|
|
# Generate summary report
|
|
library_root = Path("/mnt/nas/videos")
|
|
report = generate_summary_report(files, library_root)
|
|
|
|
# Verify report contains expected information
|
|
assert "Total Files: 5" in report
|
|
assert "Movie:" in report
|
|
assert "Series:" in report
|
|
assert "Anime:" in report
|
|
assert "Category Breakdown:" in report
|
|
|
|
def test_all_reports_include_metadata(self):
|
|
"""Test that all reports include generation timestamp and library root."""
|
|
library_root = Path("/mnt/nas/videos")
|
|
|
|
# Test completeness report
|
|
completeness_report = generate_completeness_report([], "text", library_root)
|
|
assert "Generated:" in completeness_report
|
|
assert str(library_root) in completeness_report
|
|
|
|
# Test duplicate report
|
|
duplicate_report = generate_duplicate_report([], "text", library_root)
|
|
assert "Generated:" in duplicate_report
|
|
assert str(library_root) in duplicate_report
|
|
|
|
# Test summary report
|
|
summary_report = generate_summary_report([], library_root)
|
|
assert "Generated:" in summary_report
|
|
assert str(library_root) in summary_report
|