"""Integration tests for release-dates/certifications (section 17, additive): TMDb's release_dates/content_ratings blocks were already being fetched via append_to_response, but dropped at the normalizer step -- every downstream layer (ingestion write, repository read, schema, service) was unbuilt. Mirrors test_movie_ingestion.py/test_series_ingestion.py's fake provider pattern to exercise the full write -> read path against PostgreSQL.""" from __future__ import annotations from typing import Any import pytest from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.core.ids import aurora_id from app.models.language_country import Certification from app.models.movie import ReleaseDate from app.providers.base import MetadataProvider from app.services import movie_service, tv_service from app.services.ingestion.movie_ingestion import ingest_movie from app.services.ingestion.series_ingestion import ingest_series from tests.unit.test_normalizer import FAKE_MOVIE, FAKE_SERIES class FakeTMDbProvider(MetadataProvider): slug = "tmdb" name = "The Movie Database (TMDb)" def __init__(self, movie_payload: dict[str, Any] | None = None, series_payload: dict[str, Any] | None = None) -> None: self._movie_payload = movie_payload self._series_payload = series_payload async def get_movie(self, external_id: str) -> dict[str, Any]: assert self._movie_payload is not None return self._movie_payload async def get_series(self, external_id: str) -> dict[str, Any]: assert self._series_payload is not None return self._series_payload async def get_season(self, series_external_id: str, season_number: int) -> dict[str, Any]: raise NotImplementedError async def get_person(self, external_id: str) -> dict[str, Any]: raise NotImplementedError async def get_collection(self, external_id: str) -> dict[str, Any]: raise NotImplementedError async def search_movie(self, query: str) -> list[dict[str, Any]]: raise NotImplementedError @pytest.mark.asyncio async def test_ingest_movie_stores_release_dates_and_certifications(db_session: AsyncSession): movie_id = await ingest_movie(db_session, FakeTMDbProvider(movie_payload=FAKE_MOVIE), "603") await db_session.commit() rows = ( (await db_session.execute(select(ReleaseDate).where(ReleaseDate.entity_id == movie_id))) .scalars() .all() ) assert len(rows) == 4 us_theatrical = next(r for r in rows if r.country_code == "US" and r.release_type == "theatrical") cert = await db_session.get(Certification, us_theatrical.certification_id) assert cert.system == "MPA" assert cert.rating == "R" @pytest.mark.asyncio async def test_ingest_movie_reingestion_replaces_release_dates(db_session: AsyncSession): """Wholesale replace on re-sync, same as credits/videos -- re-running ingestion must not accumulate duplicate release-date rows.""" provider = FakeTMDbProvider(movie_payload=FAKE_MOVIE) movie_id = await ingest_movie(db_session, provider, "603") await db_session.commit() movie_id_again = await ingest_movie(db_session, provider, "603") await db_session.commit() assert movie_id == movie_id_again rows = ( (await db_session.execute(select(ReleaseDate).where(ReleaseDate.entity_id == movie_id))) .scalars() .all() ) assert len(rows) == 4 @pytest.mark.asyncio async def test_movie_detail_surfaces_certification_and_release_dates(db_session: AsyncSession): movie_id = await ingest_movie(db_session, FakeTMDbProvider(movie_payload=FAKE_MOVIE), "603") await db_session.commit() detail = await movie_service.get_movie_detail(db_session, aurora_id("movie", str(movie_id))) assert detail.certification == "R" # US MPA rating, the default badge assert len(detail.release_dates) == 4 us_theatrical = next( rd for rd in detail.release_dates if rd.country_code == "US" and rd.release_type == "theatrical" ) assert us_theatrical.certification.system == "MPA" assert us_theatrical.certification.rating == "R" @pytest.mark.asyncio async def test_ingest_series_anchors_content_ratings_to_first_air_date(db_session: AsyncSession): series_id = await ingest_series(db_session, FakeTMDbProvider(series_payload=FAKE_SERIES), "1396") await db_session.commit() rows = ( (await db_session.execute(select(ReleaseDate).where(ReleaseDate.entity_id == series_id))) .scalars() .all() ) assert len(rows) == 2 assert all(r.release_type == "tv" for r in rows) assert all(str(r.release_date) == "2008-01-20" for r in rows) @pytest.mark.asyncio async def test_series_detail_surfaces_certification(db_session: AsyncSession): series_id = await ingest_series(db_session, FakeTMDbProvider(series_payload=FAKE_SERIES), "1396") await db_session.commit() detail = await tv_service.get_series_detail(db_session, aurora_id("series", str(series_id))) assert detail.certification == "TV-MA" assert len(detail.release_dates) == 2