Add filter for series with no downloaded episodes
- Added get_series_with_no_episodes() method to AnimeSeriesService - Updated list_anime endpoint to support filter='no_episodes' parameter - Added comprehensive unit tests for the new filtering functionality - All tests passing successfully
This commit is contained in:
191
tests/unit/test_series_filter.py
Normal file
191
tests/unit/test_series_filter.py
Normal file
@@ -0,0 +1,191 @@
|
||||
"""Tests for series filtering functionality."""
|
||||
import pytest
|
||||
from sqlalchemy.ext.asyncio import (
|
||||
AsyncSession,
|
||||
async_sessionmaker,
|
||||
create_async_engine,
|
||||
)
|
||||
|
||||
from src.server.database.models import Base
|
||||
from src.server.database.service import AnimeSeriesService, EpisodeService
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def db_engine():
|
||||
"""Create test database engine."""
|
||||
engine = create_async_engine(
|
||||
"sqlite+aiosqlite:///:memory:",
|
||||
echo=False,
|
||||
)
|
||||
|
||||
async with engine.begin() as conn:
|
||||
await conn.run_sync(Base.metadata.create_all)
|
||||
|
||||
yield engine
|
||||
|
||||
await engine.dispose()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def session_factory(db_engine):
|
||||
"""Create session factory for testing."""
|
||||
return async_sessionmaker(
|
||||
db_engine,
|
||||
class_=AsyncSession,
|
||||
expire_on_commit=False,
|
||||
autoflush=False,
|
||||
autocommit=False,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def async_session(session_factory):
|
||||
"""Create database session for testing."""
|
||||
async with session_factory() as session:
|
||||
yield session
|
||||
await session.rollback()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_series_with_no_episodes_empty_database(
|
||||
async_session: AsyncSession
|
||||
):
|
||||
"""Test that empty database returns empty list."""
|
||||
result = await AnimeSeriesService.get_series_with_no_episodes(
|
||||
async_session
|
||||
)
|
||||
assert result == []
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_series_with_no_episodes_no_downloaded_episodes(
|
||||
async_session: AsyncSession
|
||||
):
|
||||
"""Test that series with no downloaded episodes are returned."""
|
||||
# Create a series with no episodes
|
||||
series1 = await AnimeSeriesService.create(
|
||||
async_session,
|
||||
key="test-series-1",
|
||||
name="Test Series 1",
|
||||
folder="Test Series 1 (2024)",
|
||||
site="https://example.com/test1",
|
||||
)
|
||||
|
||||
# Create a series with undownloaded episodes
|
||||
series2 = await AnimeSeriesService.create(
|
||||
async_session,
|
||||
key="test-series-2",
|
||||
name="Test Series 2",
|
||||
folder="Test Series 2 (2024)",
|
||||
site="https://example.com/test2",
|
||||
)
|
||||
await EpisodeService.create(
|
||||
async_session,
|
||||
series_id=series2.id,
|
||||
season=1,
|
||||
episode_number=1,
|
||||
is_downloaded=False,
|
||||
)
|
||||
|
||||
# Create a series with downloaded episodes (should not be in result)
|
||||
series3 = await AnimeSeriesService.create(
|
||||
async_session,
|
||||
key="test-series-3",
|
||||
name="Test Series 3",
|
||||
folder="Test Series 3 (2024)",
|
||||
site="https://example.com/test3",
|
||||
)
|
||||
await EpisodeService.create(
|
||||
async_session,
|
||||
series_id=series3.id,
|
||||
season=1,
|
||||
episode_number=1,
|
||||
is_downloaded=True,
|
||||
)
|
||||
|
||||
await async_session.commit()
|
||||
|
||||
# Query for series with no downloaded episodes
|
||||
result = await AnimeSeriesService.get_series_with_no_episodes(
|
||||
async_session
|
||||
)
|
||||
|
||||
# Should return series1 and series2 but not series3
|
||||
result_ids = {s.id for s in result}
|
||||
assert series1.id in result_ids
|
||||
assert series2.id in result_ids
|
||||
assert series3.id not in result_ids
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_series_with_no_episodes_mixed_downloads(
|
||||
async_session: AsyncSession
|
||||
):
|
||||
"""Test series with mixed downloaded/undownloaded episodes."""
|
||||
# Create series with some downloaded and some undownloaded episodes
|
||||
series = await AnimeSeriesService.create(
|
||||
async_session,
|
||||
key="test-series-mixed",
|
||||
name="Test Series Mixed",
|
||||
folder="Test Series Mixed (2024)",
|
||||
site="https://example.com/testmixed",
|
||||
)
|
||||
|
||||
# Add downloaded episode
|
||||
await EpisodeService.create(
|
||||
async_session,
|
||||
series_id=series.id,
|
||||
season=1,
|
||||
episode_number=1,
|
||||
is_downloaded=True,
|
||||
)
|
||||
|
||||
# Add undownloaded episode
|
||||
await EpisodeService.create(
|
||||
async_session,
|
||||
series_id=series.id,
|
||||
season=1,
|
||||
episode_number=2,
|
||||
is_downloaded=False,
|
||||
)
|
||||
|
||||
await async_session.commit()
|
||||
|
||||
# Query for series with no downloaded episodes
|
||||
result = await AnimeSeriesService.get_series_with_no_episodes(
|
||||
async_session
|
||||
)
|
||||
|
||||
# Should NOT include series with at least one downloaded episode
|
||||
result_ids = {s.id for s in result}
|
||||
assert series.id not in result_ids
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_series_with_no_episodes_pagination(
|
||||
async_session: AsyncSession
|
||||
):
|
||||
"""Test pagination works correctly."""
|
||||
# Create multiple series without downloaded episodes
|
||||
for i in range(5):
|
||||
await AnimeSeriesService.create(
|
||||
async_session,
|
||||
key=f"test-series-{i}",
|
||||
name=f"Test Series {i}",
|
||||
folder=f"Test Series {i} (2024)",
|
||||
site=f"https://example.com/test{i}",
|
||||
)
|
||||
|
||||
await async_session.commit()
|
||||
|
||||
# Test limit
|
||||
result = await AnimeSeriesService.get_series_with_no_episodes(
|
||||
async_session, limit=3
|
||||
)
|
||||
assert len(result) == 3
|
||||
|
||||
# Test offset
|
||||
result = await AnimeSeriesService.get_series_with_no_episodes(
|
||||
async_session, offset=2, limit=2
|
||||
)
|
||||
assert len(result) == 2
|
||||
Reference in New Issue
Block a user