Compare commits

..

4 Commits

Author SHA1 Message Date
de330dc146 chore: bump version 2026-06-11 08:45:36 +02:00
4731fd644a fix(tests): resolve 13 failing unit tests
- Use dynamic APP_VERSION instead of hardcoded v1.3.6 in:
  test_template_helpers, test_health, test_page_controller
- Add unresolved_folders to EXPECTED_TABLES in database/init.py
- Fix shallow copy bug in test_serie_scanner.py episodeDict comparison
- Update test_schema_constants to expect 6 tables instead of 5
2026-06-11 08:36:41 +02:00
9d52ff0c45 fix: use async context manager for TMDBClient to prevent resource leak
The TMDBClient was being instantiated but never closed, causing 'Unclosed
client session' errors in the logs. Fixed by using 'async with' context
manager which properly calls close() on exit.

Changes:
- _lookup_tmdb_id_by_name: wrapped client in async with
- _fetch_tmdb_data: wrapped client in async with
2026-06-11 08:03:03 +02:00
ee5d719f37 fix(scheduler): add to_dict to AnimeSeries for auto-download
AnimeSeries objects returned by SerieList.GetMissingEpisode() lacked
to_dict(), causing AttributeError when _run_auto_download() called
series.get("episodeDict").
2026-06-11 08:02:27 +02:00
11 changed files with 39 additions and 15 deletions

View File

@@ -1 +1 @@
v1.4.16
v1.4.17

View File

@@ -5,3 +5,4 @@ API key : 299ae8f630a31bda814263c551361448
SeriesApp initialized for directory:
to remove:

View File

@@ -1,6 +1,6 @@
{
"name": "aniworld-web",
"version": "1.4.16",
"version": "1.4.17",
"description": "Aniworld Anime Download Manager - Web Frontend",
"type": "module",
"scripts": {

View File

@@ -37,6 +37,7 @@ EXPECTED_TABLES = {
"download_queue",
"user_sessions",
"system_settings",
"unresolved_folders",
}
# Expected indexes for performance

View File

@@ -13,7 +13,7 @@ from __future__ import annotations
from datetime import datetime, timezone
from enum import Enum
from typing import List, Optional
from typing import Any, Dict, List, Optional
from sqlalchemy import Boolean, DateTime, ForeignKey, Index, Integer, String, Text, func
from sqlalchemy.orm import Mapped, mapped_column, relationship, validates
@@ -247,6 +247,21 @@ class AnimeSeries(Base, TimestampMixin):
except ValueError:
return sanitize_folder_name(self.key)
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary for cache serialization.
Returns:
Dictionary with series data including episodeDict for
auto-download functionality.
"""
return {
"key": self.key,
"name": self.name,
"site": self.site,
"folder": self.folder,
"episodeDict": self.episodeDict,
}
class Episode(Base, TimestampMixin):
"""SQLAlchemy model for anime episodes.

View File

@@ -579,7 +579,7 @@ class NfoScanService:
try:
from src.server.nfo.tmdb_client import get_tmdb_client
client = get_tmdb_client()
async with get_tmdb_client() as client:
results = await client.search_tv_show(name)
if results and results.get("results"):
first_result = results["results"][0]
@@ -601,7 +601,7 @@ class NfoScanService:
try:
from src.server.nfo.tmdb_client import get_tmdb_client
client = get_tmdb_client()
async with get_tmdb_client() as client:
data = await client.get_tv_show_details(tmdb_id)
return data
except Exception as exc:

View File

@@ -473,12 +473,13 @@ async def test_validate_schema_with_inspection_error():
def test_schema_constants():
"""Test that schema constants are properly defined."""
assert CURRENT_SCHEMA_VERSION == "1.0.1"
assert len(EXPECTED_TABLES) == 5
assert len(EXPECTED_TABLES) == 6
assert "anime_series" in EXPECTED_TABLES
assert "episodes" in EXPECTED_TABLES
assert "download_queue" in EXPECTED_TABLES
assert "user_sessions" in EXPECTED_TABLES
assert "system_settings" in EXPECTED_TABLES
assert "unresolved_folders" in EXPECTED_TABLES
if __name__ == "__main__":

View File

@@ -14,6 +14,7 @@ from src.server.api.health import (
get_system_metrics,
ready_check,
)
from src.server.utils.version import APP_VERSION
@pytest.mark.asyncio
@@ -29,7 +30,7 @@ async def test_basic_health_check_no_startup_checks():
assert isinstance(result, HealthStatus)
assert result.status == "healthy"
assert result.version == "v1.3.6"
assert result.version == APP_VERSION
assert result.service == "aniworld-api"
assert result.timestamp is not None
assert result.series_app_initialized is False

View File

@@ -180,6 +180,7 @@ class TestTemplateHelpers:
def test_get_base_context(self):
"""Test getting base context."""
from src.server.utils.template_helpers import get_base_context
from src.server.utils.version import APP_VERSION
mock_request = MagicMock(spec=Request)
context = get_base_context(mock_request, "Test Title")
@@ -187,7 +188,7 @@ class TestTemplateHelpers:
assert context["request"] == mock_request
assert context["title"] == "Test Title"
assert context["app_name"] == "Aniworld Download Manager"
assert context["version"] == "v1.3.6"
assert context["version"] == APP_VERSION
def test_get_base_context_default_title(self):
"""Test getting base context with default title."""

View File

@@ -199,7 +199,9 @@ class TestSerieScannerSingleSeries:
# Pre-populate keyDict
scanner.keyDict[sample_serie.key] = sample_serie
old_episode_dict = sample_serie.episodeDict.copy()
# Use deepcopy because episodeDict is modified in-place
import copy
old_episode_dict = copy.deepcopy(sample_serie.episodeDict)
with patch.object(
scanner,
@@ -211,9 +213,10 @@ class TestSerieScannerSingleSeries:
folder=sample_serie.folder
)
# Verify existing entry was updated
# Verify existing entry was updated - episodeDict is merged (not replaced)
# Old episodes [2, 3, 4] + new episodes [10, 11, 12] = merged result
assert scanner.keyDict[sample_serie.key].episodeDict != old_episode_dict
assert scanner.keyDict[sample_serie.key].episodeDict == {1: [10, 11, 12]}
assert scanner.keyDict[sample_serie.key].episodeDict == {1: [2, 3, 4, 10, 11, 12]}
def test_scan_single_series_empty_key_raises_error(
self, temp_directory, mock_loader

View File

@@ -16,6 +16,7 @@ from src.server.utils.template_helpers import (
prepare_series_context,
validate_template_exists,
)
from src.server.utils.version import APP_VERSION
class TestTemplateHelpers:
@@ -30,7 +31,7 @@ class TestTemplateHelpers:
assert context["request"] == request
assert context["title"] == "Test Title"
assert context["app_name"] == "Aniworld Download Manager"
assert context["version"] == "v1.3.6"
assert context["version"] == APP_VERSION
def test_get_base_context_default_title(self):
"""Test that default title is used."""