263 lines
9.4 KiB
Python
263 lines
9.4 KiB
Python
"""Tests for the I/O layer module."""
|
|||
|
|
|
||
|
|
import json
|
||
|
|
from datetime import datetime, timezone
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
import pytest
|
||
|
|
|
||
|
|
from vlm.io import (
|
||
|
|
_video_file_from_record,
|
||
|
|
load_identities_json,
|
||
|
|
save_identities_json,
|
||
|
|
)
|
||
|
|
from vlm.models import VideoFile
|
||
|
|
|
||
|
|
|
||
|
|
class TestVideoFileFromRecord:
|
||
|
|
"""Tests for _video_file_from_record function."""
|
||
|
|
|
||
|
|
def test_video_file_from_record_with_metadata_v2(self):
|
||
|
|
"""Test loading VideoFile from v2 record with embedded metadata."""
|
||
|
|
record = {
|
||
|
|
"path": "/library/movie/The Matrix (1999).mkv",
|
||
|
|
"filename": "The Matrix (1999).mkv",
|
||
|
|
"category": "movie",
|
||
|
|
"title": "The Matrix",
|
||
|
|
"year": 1999,
|
||
|
|
"confidence": 0.9,
|
||
|
|
"needs_review": False,
|
||
|
|
"video_metadata": {
|
||
|
|
"size_bytes": 1234567890,
|
||
|
|
"modified_timestamp": "2024-01-01T10:00:00+00:00",
|
||
|
|
"resolution": "1920x1080",
|
||
|
|
"codec": "h264",
|
||
|
|
"duration_seconds": 7200.5,
|
||
|
|
"bitrate_kbps": 5000,
|
||
|
|
},
|
||
|
|
}
|
||
|
|
|
||
|
|
vf = _video_file_from_record(record)
|
||
|
|
|
||
|
|
assert vf.path == Path("/library/movie/The Matrix (1999).mkv")
|
||
|
|
assert vf.filename == "The Matrix (1999).mkv"
|
||
|
|
assert vf.category == "movie"
|
||
|
|
assert vf.size_bytes == 1234567890
|
||
|
|
assert vf.resolution == "1920x1080"
|
||
|
|
assert vf.codec == "h264"
|
||
|
|
assert vf.duration_seconds == 7200.5
|
||
|
|
assert vf.bitrate_kbps == 5000
|
||
|
|
assert isinstance(vf.modified_timestamp, datetime)
|
||
|
|
|
||
|
|
def test_video_file_from_record_without_metadata_v1(self):
|
||
|
|
"""Test loading VideoFile from v1 record without metadata (backward compatibility)."""
|
||
|
|
record = {
|
||
|
|
"path": "/library/movie/Inception (2010).mkv",
|
||
|
|
"filename": "Inception (2010).mkv",
|
||
|
|
"category": "movie",
|
||
|
|
"title": "Inception",
|
||
|
|
"year": 2010,
|
||
|
|
"confidence": 0.9,
|
||
|
|
"needs_review": False,
|
||
|
|
}
|
||
|
|
|
||
|
|
vf = _video_file_from_record(record)
|
||
|
|
|
||
|
|
assert vf.path == Path("/library/movie/Inception (2010).mkv")
|
||
|
|
assert vf.filename == "Inception (2010).mkv"
|
||
|
|
assert vf.category == "movie"
|
||
|
|
# v1 defaults
|
||
|
|
assert vf.size_bytes == 0
|
||
|
|
assert vf.resolution is None
|
||
|
|
assert vf.codec is None
|
||
|
|
assert vf.duration_seconds is None
|
||
|
|
assert vf.bitrate_kbps is None
|
||
|
|
assert isinstance(vf.modified_timestamp, datetime)
|
||
|
|
|
||
|
|
def test_video_file_from_record_partial_metadata(self):
|
||
|
|
"""Test loading VideoFile with partial metadata (some fields missing)."""
|
||
|
|
record = {
|
||
|
|
"path": "/library/series/Breaking Bad S01E01.mkv",
|
||
|
|
"filename": "Breaking Bad S01E01.mkv",
|
||
|
|
"category": "series",
|
||
|
|
"title": "Breaking Bad",
|
||
|
|
"season": 1,
|
||
|
|
"episodes": [1],
|
||
|
|
"confidence": 0.9,
|
||
|
|
"needs_review": False,
|
||
|
|
"video_metadata": {
|
||
|
|
"size_bytes": 987654321,
|
||
|
|
"modified_timestamp": "2024-02-01T15:30:00+00:00",
|
||
|
|
"resolution": "1280x720",
|
||
|
|
# codec missing
|
||
|
|
# duration_seconds missing
|
||
|
|
# bitrate_kbps missing
|
||
|
|
},
|
||
|
|
}
|
||
|
|
|
||
|
|
vf = _video_file_from_record(record)
|
||
|
|
|
||
|
|
assert vf.size_bytes == 987654321
|
||
|
|
assert vf.resolution == "1280x720"
|
||
|
|
assert vf.codec is None
|
||
|
|
assert vf.duration_seconds is None
|
||
|
|
assert vf.bitrate_kbps is None
|
||
|
|
|
||
|
|
|
||
|
|
class TestIdentitiesJsonVersioning:
|
||
|
|
"""Tests for identities.json schema versioning."""
|
||
|
|
|
||
|
|
def test_load_identities_v2_with_metadata(self, tmp_path):
|
||
|
|
"""Test loading v2 identities.json with video metadata."""
|
||
|
|
identities_file = tmp_path / "identities.json"
|
||
|
|
data = {
|
||
|
|
"vlm_schema_version": "2.0",
|
||
|
|
"metadata": {
|
||
|
|
"generated": "2024-01-01T10:00:00",
|
||
|
|
"source_inventory": "inventory.csv",
|
||
|
|
"total_files": 1,
|
||
|
|
},
|
||
|
|
"movies": [
|
||
|
|
{
|
||
|
|
"path": "/library/movie/Test (2024).mkv",
|
||
|
|
"filename": "Test (2024).mkv",
|
||
|
|
"category": "movie",
|
||
|
|
"title": "Test",
|
||
|
|
"year": 2024,
|
||
|
|
"confidence": 0.9,
|
||
|
|
"needs_review": False,
|
||
|
|
"video_metadata": {
|
||
|
|
"size_bytes": 1000000,
|
||
|
|
"modified_timestamp": "2024-01-01T00:00:00+00:00",
|
||
|
|
"resolution": "1920x1080",
|
||
|
|
"codec": "h264",
|
||
|
|
"duration_seconds": 3600.0,
|
||
|
|
"bitrate_kbps": 2500,
|
||
|
|
},
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"series": [],
|
||
|
|
"anime": [],
|
||
|
|
"other": [],
|
||
|
|
}
|
||
|
|
|
||
|
|
save_identities_json(data, identities_file)
|
||
|
|
loaded = load_identities_json(identities_file)
|
||
|
|
|
||
|
|
assert loaded["vlm_schema_version"] == "2.0"
|
||
|
|
assert len(loaded["movies"]) == 1
|
||
|
|
assert "video_metadata" in loaded["movies"][0]
|
||
|
|
assert loaded["movies"][0]["video_metadata"]["size_bytes"] == 1000000
|
||
|
|
|
||
|
|
def test_load_identities_v1_backward_compatibility(self, tmp_path):
|
||
|
|
"""Test loading v1 identities.json without version field (backward compatibility)."""
|
||
|
|
identities_file = tmp_path / "identities.json"
|
||
|
|
data = {
|
||
|
|
# No vlm_schema_version field (v1)
|
||
|
|
"metadata": {
|
||
|
|
"generated": "2024-01-01T10:00:00",
|
||
|
|
"source_inventory": "inventory.csv",
|
||
|
|
"total_files": 1,
|
||
|
|
},
|
||
|
|
"movies": [
|
||
|
|
{
|
||
|
|
"path": "/library/movie/Old (2023).mkv",
|
||
|
|
"filename": "Old (2023).mkv",
|
||
|
|
"category": "movie",
|
||
|
|
"title": "Old",
|
||
|
|
"year": 2023,
|
||
|
|
"confidence": 0.9,
|
||
|
|
"needs_review": False,
|
||
|
|
# No video_metadata field
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"series": [],
|
||
|
|
"anime": [],
|
||
|
|
"other": [],
|
||
|
|
}
|
||
|
|
|
||
|
|
save_identities_json(data, identities_file)
|
||
|
|
loaded = load_identities_json(identities_file)
|
||
|
|
|
||
|
|
# v1 files don't have version field
|
||
|
|
assert "vlm_schema_version" not in loaded or loaded.get("vlm_schema_version") is None
|
||
|
|
assert len(loaded["movies"]) == 1
|
||
|
|
assert "video_metadata" not in loaded["movies"][0]
|
||
|
|
|
||
|
|
|
||
|
|
class TestIdentitiesJsonRoundTrip:
|
||
|
|
"""Tests for identities.json save/load round-trip."""
|
||
|
|
|
||
|
|
def test_save_and_load_v2_identities(self, tmp_path):
|
||
|
|
"""Test saving and loading v2 identities with metadata."""
|
||
|
|
identities_file = tmp_path / "identities_v2.json"
|
||
|
|
original_data = {
|
||
|
|
"vlm_schema_version": "2.0",
|
||
|
|
"metadata": {
|
||
|
|
"generated": "2024-02-13T12:00:00",
|
||
|
|
"source_inventory": "test_inventory.csv",
|
||
|
|
"total_files": 2,
|
||
|
|
},
|
||
|
|
"movies": [
|
||
|
|
{
|
||
|
|
"path": "/library/movie/Movie1 (2024).mkv",
|
||
|
|
"filename": "Movie1 (2024).mkv",
|
||
|
|
"category": "movie",
|
||
|
|
"title": "Movie1",
|
||
|
|
"year": 2024,
|
||
|
|
"confidence": 0.9,
|
||
|
|
"needs_review": False,
|
||
|
|
"video_metadata": {
|
||
|
|
"size_bytes": 2000000000,
|
||
|
|
"modified_timestamp": "2024-02-10T08:00:00+00:00",
|
||
|
|
"resolution": "3840x2160",
|
||
|
|
"codec": "hevc",
|
||
|
|
"duration_seconds": 7200.0,
|
||
|
|
"bitrate_kbps": 8000,
|
||
|
|
},
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"series": [
|
||
|
|
{
|
||
|
|
"path": "/library/series/Show S01E01.mkv",
|
||
|
|
"filename": "Show S01E01.mkv",
|
||
|
|
"category": "series",
|
||
|
|
"title": "Show",
|
||
|
|
"season": 1,
|
||
|
|
"episodes": [1],
|
||
|
|
"confidence": 0.9,
|
||
|
|
"needs_review": False,
|
||
|
|
"video_metadata": {
|
||
|
|
"size_bytes": 500000000,
|
||
|
|
"modified_timestamp": "2024-02-11T10:00:00+00:00",
|
||
|
|
"resolution": "1920x1080",
|
||
|
|
"codec": "h264",
|
||
|
|
"duration_seconds": 2700.0,
|
||
|
|
"bitrate_kbps": 3000,
|
||
|
|
},
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"anime": [],
|
||
|
|
"other": [],
|
||
|
|
}
|
||
|
|
|
||
|
|
save_identities_json(original_data, identities_file)
|
||
|
|
loaded_data = load_identities_json(identities_file)
|
||
|
|
|
||
|
|
# Verify structure
|
||
|
|
assert loaded_data["vlm_schema_version"] == "2.0"
|
||
|
|
assert len(loaded_data["movies"]) == 1
|
||
|
|
assert len(loaded_data["series"]) == 1
|
||
|
|
|
||
|
|
# Verify movie metadata preserved
|
||
|
|
movie = loaded_data["movies"][0]
|
||
|
|
assert movie["video_metadata"]["size_bytes"] == 2000000000
|
||
|
|
assert movie["video_metadata"]["resolution"] == "3840x2160"
|
||
|
|
assert movie["video_metadata"]["codec"] == "hevc"
|
||
|
|
|
||
|
|
# Verify series metadata preserved
|
||
|
|
series = loaded_data["series"][0]
|
||
|
|
assert series["video_metadata"]["size_bytes"] == 500000000
|
||
|
|
assert series["video_metadata"]["resolution"] == "1920x1080"
|
||
|
|
assert series["video_metadata"]["codec"] == "h264"
|