Add now-playing and upcoming movie endpoints
CI / lint (push) Canceled after 0s
CI / typecheck (push) Canceled after 0s
CI / unit-tests (push) Canceled after 0s
CI / integration-tests (push) Canceled after 0s
CI / container-build (push) Canceled after 0s
CI / dependency-audit (push) Canceled after 0s
CI / lint (push) Canceled after 0s
CI / typecheck (push) Canceled after 0s
CI / unit-tests (push) Canceled after 0s
CI / integration-tests (push) Canceled after 0s
CI / container-build (push) Canceled after 0s
CI / dependency-audit (push) Canceled after 0s
Buildable now that release_dates exists: GET /movies/now-playing and /movies/upcoming filter on release_type="theatrical" per region, windowed around today. A movie can carry more than one theatrical-type row in the same region (limited release then wide release both map to "theatrical" per normalizer._RELEASE_TYPE_MAP) -- collapsed to one row per movie via a grouped subquery rather than joining ReleaseDate directly, which would duplicate the movie in the page and inflate the total count. Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
567b4ef068
commit
74ebad379a
@@ -0,0 +1,158 @@
|
||||
"""Integration tests for GET /api/v1/movies/now-playing and .../upcoming
|
||||
(section 17, additive): buildable now that release_dates exists (see
|
||||
test_release_dates.py) -- theatrical release dates per region, windowed
|
||||
around today. Mirrors test_movie_browse.py's fake-provider seeding
|
||||
pattern."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import date, timedelta
|
||||
from typing import Any
|
||||
|
||||
import pytest
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.services import movie_service
|
||||
from app.services.ingestion.movie_ingestion import ingest_movie
|
||||
from tests.integration.test_movie_ingestion import FakeTMDbProvider
|
||||
|
||||
_TODAY = date.today()
|
||||
|
||||
|
||||
def _release_dates_block(country: str, release_date: date, release_type_code: int) -> dict[str, Any]:
|
||||
return {
|
||||
"results": [
|
||||
{
|
||||
"iso_3166_1": country,
|
||||
"release_dates": [
|
||||
{
|
||||
"certification": "",
|
||||
"release_date": f"{release_date.isoformat()}T00:00:00.000Z",
|
||||
"type": release_type_code,
|
||||
}
|
||||
],
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
def _movie_payload(**overrides: Any) -> dict[str, Any]:
|
||||
base = {"id": 9000, "title": "Untitled", "status": "Released", "popularity": 10.0}
|
||||
base.update(overrides)
|
||||
return base
|
||||
|
||||
|
||||
async def _seed(db_session: AsyncSession) -> None:
|
||||
movies = [
|
||||
# In the now-playing window (released 10 days ago, theatrical).
|
||||
_movie_payload(
|
||||
id=9201,
|
||||
title="Currently Showing",
|
||||
release_dates=_release_dates_block("US", _TODAY - timedelta(days=10), 3),
|
||||
),
|
||||
# Outside the now-playing window (too old).
|
||||
_movie_payload(
|
||||
id=9202,
|
||||
title="Long Gone",
|
||||
release_dates=_release_dates_block("US", _TODAY - timedelta(days=200), 3),
|
||||
),
|
||||
# Upcoming (future theatrical release).
|
||||
_movie_payload(
|
||||
id=9203,
|
||||
title="Coming Soon",
|
||||
release_dates=_release_dates_block("US", _TODAY + timedelta(days=14), 3),
|
||||
),
|
||||
# Theatrical release in a different region only -- must not leak
|
||||
# into the default US-region results.
|
||||
_movie_payload(
|
||||
id=9204,
|
||||
title="GB Exclusive",
|
||||
release_dates=_release_dates_block("GB", _TODAY - timedelta(days=5), 3),
|
||||
),
|
||||
# A digital-only release near today -- must not count as "now
|
||||
# playing" (which is theatrical-only).
|
||||
_movie_payload(
|
||||
id=9205,
|
||||
title="Streaming Only",
|
||||
release_dates=_release_dates_block("US", _TODAY - timedelta(days=2), 4),
|
||||
),
|
||||
]
|
||||
for payload in movies:
|
||||
await ingest_movie(db_session, FakeTMDbProvider(payload), str(payload["id"]))
|
||||
await db_session.commit()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_now_playing_returns_recent_theatrical_releases_only(db_session: AsyncSession):
|
||||
await _seed(db_session)
|
||||
|
||||
out = await movie_service.list_now_playing(db_session, region="US", page=1, page_size=20)
|
||||
|
||||
assert [m.title for m in out.results] == ["Currently Showing"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_upcoming_returns_future_theatrical_releases_only(db_session: AsyncSession):
|
||||
await _seed(db_session)
|
||||
|
||||
out = await movie_service.list_upcoming(db_session, region="US", page=1, page_size=20)
|
||||
|
||||
assert [m.title for m in out.results] == ["Coming Soon"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_now_playing_respects_region(db_session: AsyncSession):
|
||||
await _seed(db_session)
|
||||
|
||||
out = await movie_service.list_now_playing(db_session, region="GB", page=1, page_size=20)
|
||||
|
||||
assert [m.title for m in out.results] == ["GB Exclusive"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_now_playing_ignores_movies_with_only_a_theatrical_release_outside_the_window(
|
||||
db_session: AsyncSession,
|
||||
):
|
||||
await _seed(db_session)
|
||||
|
||||
out = await movie_service.list_now_playing(db_session, region="US", page=1, page_size=20)
|
||||
|
||||
assert "Long Gone" not in [m.title for m in out.results]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_now_playing_dedupes_a_movie_with_multiple_theatrical_dates(db_session: AsyncSession):
|
||||
"""A limited release followed by a wide release both map to
|
||||
release_type="theatrical" (see normalizer._RELEASE_TYPE_MAP) -- a movie
|
||||
with two such rows in the same region must appear exactly once."""
|
||||
|
||||
payload = _movie_payload(
|
||||
id=9301,
|
||||
title="Limited Then Wide",
|
||||
release_dates={
|
||||
"results": [
|
||||
{
|
||||
"iso_3166_1": "US",
|
||||
"release_dates": [
|
||||
{
|
||||
"certification": "",
|
||||
"release_date": f"{(_TODAY - timedelta(days=20)).isoformat()}T00:00:00.000Z",
|
||||
"type": 2,
|
||||
},
|
||||
{
|
||||
"certification": "",
|
||||
"release_date": f"{(_TODAY - timedelta(days=5)).isoformat()}T00:00:00.000Z",
|
||||
"type": 3,
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
},
|
||||
)
|
||||
await ingest_movie(db_session, FakeTMDbProvider(payload), "9301")
|
||||
await db_session.commit()
|
||||
|
||||
out = await movie_service.list_now_playing(db_session, region="US", page=1, page_size=20)
|
||||
|
||||
assert [m.title for m in out.results] == ["Limited Then Wide"]
|
||||
assert out.total_results == 1
|
||||
Reference in New Issue
Block a user