- Add series context helpers: prepare_series_context, get_series_by_key, filter_series_by_missing_episodes - Update module docstring with identifier convention documentation - Add unit tests for new series context helper functions - Update infrastructure.md with template helpers documentation - Mark Phase 4 (API Layer) as complete
244 lines
8.1 KiB
Python
244 lines
8.1 KiB
Python
"""
|
|
Tests for template helper utilities.
|
|
|
|
This module tests the template helper functions including series context
|
|
preparation using `key` as the primary identifier.
|
|
"""
|
|
from unittest.mock import Mock
|
|
|
|
import pytest
|
|
|
|
from src.server.utils.template_helpers import (
|
|
filter_series_by_missing_episodes,
|
|
get_base_context,
|
|
get_series_by_key,
|
|
list_available_templates,
|
|
prepare_series_context,
|
|
validate_template_exists,
|
|
)
|
|
|
|
|
|
class TestTemplateHelpers:
|
|
"""Test template helper utilities."""
|
|
|
|
def test_get_base_context(self):
|
|
"""Test that base context is created correctly."""
|
|
request = Mock()
|
|
context = get_base_context(request, "Test Title")
|
|
|
|
assert "request" in context
|
|
assert context["request"] == request
|
|
assert context["title"] == "Test Title"
|
|
assert context["app_name"] == "Aniworld Download Manager"
|
|
assert context["version"] == "1.0.0"
|
|
|
|
def test_get_base_context_default_title(self):
|
|
"""Test that default title is used."""
|
|
request = Mock()
|
|
context = get_base_context(request)
|
|
|
|
assert context["title"] == "Aniworld"
|
|
|
|
def test_validate_template_exists_true(self):
|
|
"""Test template validation for existing template."""
|
|
# index.html should exist
|
|
exists = validate_template_exists("index.html")
|
|
assert exists is True
|
|
|
|
def test_validate_template_exists_false(self):
|
|
"""Test template validation for non-existing template."""
|
|
exists = validate_template_exists("nonexistent.html")
|
|
assert exists is False
|
|
|
|
def test_list_available_templates(self):
|
|
"""Test listing available templates."""
|
|
templates = list_available_templates()
|
|
|
|
# Should be a list
|
|
assert isinstance(templates, list)
|
|
|
|
# Should contain at least the main templates
|
|
expected_templates = [
|
|
"index.html",
|
|
"login.html",
|
|
"setup.html",
|
|
"queue.html",
|
|
"error.html"
|
|
]
|
|
for expected in expected_templates:
|
|
assert expected in templates, (
|
|
f"{expected} not found in templates list"
|
|
)
|
|
|
|
def test_list_available_templates_only_html(self):
|
|
"""Test that only HTML files are listed."""
|
|
templates = list_available_templates()
|
|
|
|
for template in templates:
|
|
assert template.endswith(".html")
|
|
|
|
@pytest.mark.parametrize("template_name", [
|
|
"index.html",
|
|
"login.html",
|
|
"setup.html",
|
|
"queue.html",
|
|
"error.html"
|
|
])
|
|
def test_all_required_templates_exist(self, template_name):
|
|
"""Test that all required templates exist."""
|
|
assert validate_template_exists(template_name), \
|
|
f"Required template {template_name} does not exist"
|
|
|
|
|
|
class TestSeriesContextHelpers:
|
|
"""Test series context helper functions.
|
|
|
|
These tests verify that series helpers use `key` as the primary
|
|
identifier following the project's identifier convention.
|
|
"""
|
|
|
|
def test_prepare_series_context_uses_key(self):
|
|
"""Test that prepare_series_context uses key as primary identifier."""
|
|
series_data = [
|
|
{
|
|
"key": "attack-on-titan",
|
|
"name": "Attack on Titan",
|
|
"folder": "Attack on Titan (2013)",
|
|
},
|
|
{
|
|
"key": "one-piece",
|
|
"name": "One Piece",
|
|
"folder": "One Piece (1999)",
|
|
},
|
|
]
|
|
prepared = prepare_series_context(series_data)
|
|
|
|
assert len(prepared) == 2
|
|
# Verify key is present and used
|
|
assert prepared[0]["key"] in ("attack-on-titan", "one-piece")
|
|
assert all("key" in item for item in prepared)
|
|
assert all("folder" in item for item in prepared)
|
|
|
|
def test_prepare_series_context_sorts_by_name(self):
|
|
"""Test that series are sorted by name by default."""
|
|
series_data = [
|
|
{"key": "z-series", "name": "Zebra Anime", "folder": "z"},
|
|
{"key": "a-series", "name": "Alpha Anime", "folder": "a"},
|
|
]
|
|
prepared = prepare_series_context(series_data, sort_by="name")
|
|
|
|
assert prepared[0]["name"] == "Alpha Anime"
|
|
assert prepared[1]["name"] == "Zebra Anime"
|
|
|
|
def test_prepare_series_context_sorts_by_key(self):
|
|
"""Test that series can be sorted by key."""
|
|
series_data = [
|
|
{"key": "z-series", "name": "Zebra", "folder": "z"},
|
|
{"key": "a-series", "name": "Alpha", "folder": "a"},
|
|
]
|
|
prepared = prepare_series_context(series_data, sort_by="key")
|
|
|
|
assert prepared[0]["key"] == "a-series"
|
|
assert prepared[1]["key"] == "z-series"
|
|
|
|
def test_prepare_series_context_empty_list(self):
|
|
"""Test prepare_series_context with empty list."""
|
|
prepared = prepare_series_context([])
|
|
assert prepared == []
|
|
|
|
def test_prepare_series_context_skips_missing_key(self):
|
|
"""Test that items without key are skipped with warning."""
|
|
series_data = [
|
|
{"key": "valid-series", "name": "Valid", "folder": "valid"},
|
|
{"name": "No Key", "folder": "nokey"}, # Missing key
|
|
]
|
|
prepared = prepare_series_context(series_data)
|
|
|
|
assert len(prepared) == 1
|
|
assert prepared[0]["key"] == "valid-series"
|
|
|
|
def test_prepare_series_context_preserves_extra_fields(self):
|
|
"""Test that extra fields are preserved."""
|
|
series_data = [
|
|
{
|
|
"key": "test",
|
|
"name": "Test",
|
|
"folder": "test",
|
|
"missing_episodes": {"1": [1, 2]},
|
|
"site": "aniworld.to",
|
|
}
|
|
]
|
|
prepared = prepare_series_context(series_data)
|
|
|
|
assert prepared[0]["missing_episodes"] == {"1": [1, 2]}
|
|
assert prepared[0]["site"] == "aniworld.to"
|
|
|
|
def test_get_series_by_key_found(self):
|
|
"""Test finding a series by key."""
|
|
series_data = [
|
|
{"key": "attack-on-titan", "name": "Attack on Titan"},
|
|
{"key": "one-piece", "name": "One Piece"},
|
|
]
|
|
result = get_series_by_key(series_data, "attack-on-titan")
|
|
|
|
assert result is not None
|
|
assert result["name"] == "Attack on Titan"
|
|
|
|
def test_get_series_by_key_not_found(self):
|
|
"""Test that None is returned when key not found."""
|
|
series_data = [
|
|
{"key": "attack-on-titan", "name": "Attack on Titan"},
|
|
]
|
|
result = get_series_by_key(series_data, "non-existent")
|
|
|
|
assert result is None
|
|
|
|
def test_get_series_by_key_empty_list(self):
|
|
"""Test get_series_by_key with empty list."""
|
|
result = get_series_by_key([], "any-key")
|
|
assert result is None
|
|
|
|
def test_filter_series_by_missing_episodes(self):
|
|
"""Test filtering series with missing episodes."""
|
|
series_data = [
|
|
{
|
|
"key": "has-missing",
|
|
"name": "Has Missing",
|
|
"missing_episodes": {"1": [1, 2, 3]},
|
|
},
|
|
{
|
|
"key": "no-missing",
|
|
"name": "No Missing",
|
|
"missing_episodes": {},
|
|
},
|
|
{
|
|
"key": "empty-seasons",
|
|
"name": "Empty Seasons",
|
|
"missing_episodes": {"1": [], "2": []},
|
|
},
|
|
]
|
|
filtered = filter_series_by_missing_episodes(series_data)
|
|
|
|
assert len(filtered) == 1
|
|
assert filtered[0]["key"] == "has-missing"
|
|
|
|
def test_filter_series_by_missing_episodes_empty(self):
|
|
"""Test filter with empty list."""
|
|
filtered = filter_series_by_missing_episodes([])
|
|
assert filtered == []
|
|
|
|
def test_filter_preserves_key_identifier(self):
|
|
"""Test that filter preserves key as identifier."""
|
|
series_data = [
|
|
{
|
|
"key": "test-series",
|
|
"folder": "Test Series (2020)",
|
|
"name": "Test",
|
|
"missing_episodes": {"1": [1]},
|
|
}
|
|
]
|
|
filtered = filter_series_by_missing_episodes(series_data)
|
|
|
|
assert filtered[0]["key"] == "test-series"
|
|
assert filtered[0]["folder"] == "Test Series (2020)"
|