Task 9: Clean up legacy code - Added deprecation warnings to Serie.save_to_file() and load_from_file() - Updated infrastructure.md with Data Storage section documenting: - SQLite database as primary storage - Legacy file storage as deprecated - Data migration process - Added deprecation warning tests for Serie class - Updated existing tests to handle new warnings - All 1012 tests pass (872 unit + 55 API + 85 integration)
323 lines
11 KiB
Python
323 lines
11 KiB
Python
"""
|
|
Unit tests for Serie class to verify key validation and identifier usage.
|
|
"""
|
|
|
|
import json
|
|
import os
|
|
import tempfile
|
|
|
|
import pytest
|
|
|
|
from src.core.entities.series import Serie
|
|
|
|
|
|
class TestSerieValidation:
|
|
"""Test Serie class validation logic."""
|
|
|
|
def test_serie_creation_with_valid_key(self):
|
|
"""Test creating Serie with valid key."""
|
|
serie = Serie(
|
|
key="attack-on-titan",
|
|
name="Attack on Titan",
|
|
site="https://aniworld.to/anime/stream/attack-on-titan",
|
|
folder="Attack on Titan (2013)",
|
|
episodeDict={1: [1, 2, 3], 2: [1, 2]}
|
|
)
|
|
|
|
assert serie.key == "attack-on-titan"
|
|
assert serie.name == "Attack on Titan"
|
|
assert serie.site == "https://aniworld.to/anime/stream/attack-on-titan"
|
|
assert serie.folder == "Attack on Titan (2013)"
|
|
assert serie.episodeDict == {1: [1, 2, 3], 2: [1, 2]}
|
|
|
|
def test_serie_creation_with_empty_key_raises_error(self):
|
|
"""Test that creating Serie with empty key raises ValueError."""
|
|
with pytest.raises(ValueError, match="key cannot be None or empty"):
|
|
Serie(
|
|
key="",
|
|
name="Test Series",
|
|
site="https://example.com",
|
|
folder="Test Folder",
|
|
episodeDict={1: [1]}
|
|
)
|
|
|
|
def test_serie_creation_with_whitespace_key_raises_error(self):
|
|
"""Test that creating Serie with whitespace-only key raises error."""
|
|
with pytest.raises(ValueError, match="key cannot be None or empty"):
|
|
Serie(
|
|
key=" ",
|
|
name="Test Series",
|
|
site="https://example.com",
|
|
folder="Test Folder",
|
|
episodeDict={1: [1]}
|
|
)
|
|
|
|
def test_serie_key_is_stripped(self):
|
|
"""Test that Serie key is stripped of whitespace."""
|
|
serie = Serie(
|
|
key=" attack-on-titan ",
|
|
name="Attack on Titan",
|
|
site="https://example.com",
|
|
folder="Attack on Titan (2013)",
|
|
episodeDict={1: [1]}
|
|
)
|
|
|
|
assert serie.key == "attack-on-titan"
|
|
|
|
def test_serie_key_setter_with_valid_value(self):
|
|
"""Test setting key property with valid value."""
|
|
serie = Serie(
|
|
key="initial-key",
|
|
name="Test",
|
|
site="https://example.com",
|
|
folder="Test Folder",
|
|
episodeDict={1: [1]}
|
|
)
|
|
|
|
serie.key = "new-key"
|
|
assert serie.key == "new-key"
|
|
|
|
def test_serie_key_setter_with_empty_value_raises_error(self):
|
|
"""Test that setting key to empty string raises ValueError."""
|
|
serie = Serie(
|
|
key="initial-key",
|
|
name="Test",
|
|
site="https://example.com",
|
|
folder="Test Folder",
|
|
episodeDict={1: [1]}
|
|
)
|
|
|
|
with pytest.raises(ValueError, match="key cannot be None or empty"):
|
|
serie.key = ""
|
|
|
|
def test_serie_key_setter_with_whitespace_raises_error(self):
|
|
"""Test that setting key to whitespace raises ValueError."""
|
|
serie = Serie(
|
|
key="initial-key",
|
|
name="Test",
|
|
site="https://example.com",
|
|
folder="Test Folder",
|
|
episodeDict={1: [1]}
|
|
)
|
|
|
|
with pytest.raises(ValueError, match="key cannot be None or empty"):
|
|
serie.key = " "
|
|
|
|
def test_serie_key_setter_strips_whitespace(self):
|
|
"""Test that key setter strips whitespace."""
|
|
serie = Serie(
|
|
key="initial-key",
|
|
name="Test",
|
|
site="https://example.com",
|
|
folder="Test Folder",
|
|
episodeDict={1: [1]}
|
|
)
|
|
|
|
serie.key = " new-key "
|
|
assert serie.key == "new-key"
|
|
|
|
|
|
class TestSerieProperties:
|
|
"""Test Serie class properties and methods."""
|
|
|
|
def test_serie_str_representation(self):
|
|
"""Test string representation of Serie."""
|
|
serie = Serie(
|
|
key="test-key",
|
|
name="Test Series",
|
|
site="https://example.com",
|
|
folder="Test Folder",
|
|
episodeDict={1: [1, 2]}
|
|
)
|
|
|
|
str_repr = str(serie)
|
|
assert "key='test-key'" in str_repr
|
|
assert "name='Test Series'" in str_repr
|
|
assert "folder='Test Folder'" in str_repr
|
|
|
|
def test_serie_to_dict(self):
|
|
"""Test conversion of Serie to dictionary."""
|
|
serie = Serie(
|
|
key="test-key",
|
|
name="Test Series",
|
|
site="https://example.com",
|
|
folder="Test Folder",
|
|
episodeDict={1: [1, 2], 2: [1, 2, 3]}
|
|
)
|
|
|
|
data = serie.to_dict()
|
|
|
|
assert data["key"] == "test-key"
|
|
assert data["name"] == "Test Series"
|
|
assert data["site"] == "https://example.com"
|
|
assert data["folder"] == "Test Folder"
|
|
assert "1" in data["episodeDict"]
|
|
assert data["episodeDict"]["1"] == [1, 2]
|
|
|
|
def test_serie_from_dict(self):
|
|
"""Test creating Serie from dictionary."""
|
|
data = {
|
|
"key": "test-key",
|
|
"name": "Test Series",
|
|
"site": "https://example.com",
|
|
"folder": "Test Folder",
|
|
"episodeDict": {"1": [1, 2], "2": [1, 2, 3]}
|
|
}
|
|
|
|
serie = Serie.from_dict(data)
|
|
|
|
assert serie.key == "test-key"
|
|
assert serie.name == "Test Series"
|
|
assert serie.folder == "Test Folder"
|
|
assert serie.episodeDict == {1: [1, 2], 2: [1, 2, 3]}
|
|
|
|
def test_serie_save_and_load_from_file(self):
|
|
"""Test saving and loading Serie from file."""
|
|
import warnings
|
|
|
|
serie = Serie(
|
|
key="test-key",
|
|
name="Test Series",
|
|
site="https://example.com",
|
|
folder="Test Folder",
|
|
episodeDict={1: [1, 2, 3]}
|
|
)
|
|
|
|
# Create temporary file
|
|
with tempfile.NamedTemporaryFile(
|
|
mode='w',
|
|
delete=False,
|
|
suffix='.json'
|
|
) as f:
|
|
temp_filename = f.name
|
|
|
|
try:
|
|
# Suppress deprecation warnings for this test
|
|
with warnings.catch_warnings():
|
|
warnings.simplefilter("ignore", DeprecationWarning)
|
|
|
|
# Save to file
|
|
serie.save_to_file(temp_filename)
|
|
|
|
# Load from file
|
|
loaded_serie = Serie.load_from_file(temp_filename)
|
|
|
|
# Verify all properties match
|
|
assert loaded_serie.key == serie.key
|
|
assert loaded_serie.name == serie.name
|
|
assert loaded_serie.site == serie.site
|
|
assert loaded_serie.folder == serie.folder
|
|
assert loaded_serie.episodeDict == serie.episodeDict
|
|
finally:
|
|
# Cleanup
|
|
if os.path.exists(temp_filename):
|
|
os.remove(temp_filename)
|
|
|
|
def test_serie_folder_is_mutable(self):
|
|
"""Test that folder property can be changed (it's metadata only)."""
|
|
serie = Serie(
|
|
key="test-key",
|
|
name="Test",
|
|
site="https://example.com",
|
|
folder="Old Folder",
|
|
episodeDict={1: [1]}
|
|
)
|
|
|
|
serie.folder = "New Folder"
|
|
assert serie.folder == "New Folder"
|
|
# Key should remain unchanged
|
|
assert serie.key == "test-key"
|
|
|
|
|
|
class TestSerieDocumentation:
|
|
"""Test that Serie class has proper documentation."""
|
|
|
|
def test_serie_class_has_docstring(self):
|
|
"""Test that Serie class has a docstring."""
|
|
assert Serie.__doc__ is not None
|
|
assert "unique identifier" in Serie.__doc__.lower()
|
|
|
|
def test_key_property_has_docstring(self):
|
|
"""Test that key property has descriptive docstring."""
|
|
assert Serie.key.fget.__doc__ is not None
|
|
assert "unique" in Serie.key.fget.__doc__.lower()
|
|
assert "identifier" in Serie.key.fget.__doc__.lower()
|
|
|
|
def test_folder_property_has_docstring(self):
|
|
"""Test that folder property documents it's metadata only."""
|
|
assert Serie.folder.fget.__doc__ is not None
|
|
assert "metadata" in Serie.folder.fget.__doc__.lower()
|
|
assert "not used for lookups" in Serie.folder.fget.__doc__.lower()
|
|
|
|
|
|
class TestSerieDeprecationWarnings:
|
|
"""Test deprecation warnings for file-based methods."""
|
|
|
|
def test_save_to_file_raises_deprecation_warning(self):
|
|
"""Test save_to_file() raises deprecation warning."""
|
|
import warnings
|
|
|
|
serie = Serie(
|
|
key="test-key",
|
|
name="Test Series",
|
|
site="https://example.com",
|
|
folder="Test Folder",
|
|
episodeDict={1: [1, 2, 3]}
|
|
)
|
|
|
|
with tempfile.NamedTemporaryFile(
|
|
mode='w', suffix='.json', delete=False
|
|
) as temp_file:
|
|
temp_filename = temp_file.name
|
|
|
|
try:
|
|
with warnings.catch_warnings(record=True) as w:
|
|
warnings.simplefilter("always")
|
|
serie.save_to_file(temp_filename)
|
|
|
|
# Check deprecation warning was raised
|
|
assert len(w) == 1
|
|
assert issubclass(w[0].category, DeprecationWarning)
|
|
assert "deprecated" in str(w[0].message).lower()
|
|
assert "save_to_file" in str(w[0].message)
|
|
finally:
|
|
if os.path.exists(temp_filename):
|
|
os.remove(temp_filename)
|
|
|
|
def test_load_from_file_raises_deprecation_warning(self):
|
|
"""Test load_from_file() raises deprecation warning."""
|
|
import warnings
|
|
|
|
serie = Serie(
|
|
key="test-key",
|
|
name="Test Series",
|
|
site="https://example.com",
|
|
folder="Test Folder",
|
|
episodeDict={1: [1, 2, 3]}
|
|
)
|
|
|
|
with tempfile.NamedTemporaryFile(
|
|
mode='w', suffix='.json', delete=False
|
|
) as temp_file:
|
|
temp_filename = temp_file.name
|
|
|
|
try:
|
|
# Save first (suppress warning for this)
|
|
with warnings.catch_warnings():
|
|
warnings.simplefilter("ignore")
|
|
serie.save_to_file(temp_filename)
|
|
|
|
# Now test loading
|
|
with warnings.catch_warnings(record=True) as w:
|
|
warnings.simplefilter("always")
|
|
Serie.load_from_file(temp_filename)
|
|
|
|
# Check deprecation warning was raised
|
|
assert len(w) == 1
|
|
assert issubclass(w[0].category, DeprecationWarning)
|
|
assert "deprecated" in str(w[0].message).lower()
|
|
assert "load_from_file" in str(w[0].message)
|
|
finally:
|
|
if os.path.exists(temp_filename):
|
|
os.remove(temp_filename)
|