Initial Aurora Core 0.1 implementation
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
Production-grade media metadata backend per docs/aurora-core-spec.md: Aurora-owned UUIDv7 identity system, normalized movie/TV/people/credits schema, provider abstraction with a TMDb adapter, an idempotent async ingestion pipeline (normalize -> match -> upsert -> provenance), a Postgres FTS/trigram search layer, a stateless FastAPI v1 API with Redis caching/rate-limiting, arq workers, dev/prod Docker Compose topologies, unit + integration + load tests, CI, and architecture/ database/API/ERD documentation. Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
"""Integration tests for the movie ingestion pipeline (section 15): a fake
|
||||
provider stands in for TMDb so these run without network access, while
|
||||
still exercising the real normalize -> match -> upsert -> provenance 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.models.credit import Credit
|
||||
from app.models.genre import Genre
|
||||
from app.models.movie import Movie
|
||||
from app.models.provider import ProviderRecord
|
||||
from app.providers.base import MetadataProvider
|
||||
from app.services.ingestion.movie_ingestion import ingest_movie
|
||||
from tests.unit.test_normalizer import FAKE_MOVIE
|
||||
|
||||
|
||||
class FakeTMDbProvider(MetadataProvider):
|
||||
slug = "tmdb"
|
||||
name = "The Movie Database (TMDb)"
|
||||
|
||||
def __init__(self, payload: dict[str, Any]) -> None:
|
||||
self._payload = payload
|
||||
|
||||
async def get_movie(self, external_id: str) -> dict[str, Any]:
|
||||
return self._payload
|
||||
|
||||
async def get_series(self, external_id: str) -> dict[str, Any]:
|
||||
raise NotImplementedError
|
||||
|
||||
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 search_movie(self, query: str) -> list[dict[str, Any]]:
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ingest_movie_creates_normalized_record(db_session: AsyncSession):
|
||||
provider = FakeTMDbProvider(FAKE_MOVIE)
|
||||
|
||||
movie_id = await ingest_movie(db_session, provider, "603")
|
||||
await db_session.commit()
|
||||
|
||||
movie = await db_session.get(Movie, movie_id)
|
||||
assert movie is not None
|
||||
assert movie.title == "The Matrix"
|
||||
assert movie.runtime_minutes == 136
|
||||
|
||||
genres = (await db_session.execute(select(Genre.name))).scalars().all()
|
||||
assert "Action" in genres
|
||||
|
||||
credits_ = (
|
||||
(await db_session.execute(select(Credit).where(Credit.entity_id == movie_id)))
|
||||
.scalars()
|
||||
.all()
|
||||
)
|
||||
assert any(c.character == "Neo" for c in credits_)
|
||||
|
||||
record = (
|
||||
await db_session.execute(
|
||||
select(ProviderRecord).where(ProviderRecord.external_id == "603")
|
||||
)
|
||||
).scalar_one()
|
||||
assert record.aurora_entity_id == movie_id
|
||||
assert record.match_confidence.value == "new"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ingest_movie_is_idempotent(db_session: AsyncSession):
|
||||
"""Re-running ingestion for the same provider + external_id must update
|
||||
the same Aurora movie, never create a duplicate (section 2: zero
|
||||
duplicate ingestion records)."""
|
||||
|
||||
provider = FakeTMDbProvider(FAKE_MOVIE)
|
||||
|
||||
first_id = await ingest_movie(db_session, provider, "603")
|
||||
await db_session.commit()
|
||||
|
||||
updated_payload = {**FAKE_MOVIE, "title": "The Matrix (Updated Title)"}
|
||||
second_provider = FakeTMDbProvider(updated_payload)
|
||||
second_id = await ingest_movie(db_session, second_provider, "603")
|
||||
await db_session.commit()
|
||||
|
||||
assert first_id == second_id
|
||||
|
||||
all_movies = (await db_session.execute(select(Movie))).scalars().all()
|
||||
assert len(all_movies) == 1
|
||||
assert all_movies[0].title == "The Matrix (Updated Title)"
|
||||
|
||||
credits_ = (
|
||||
(await db_session.execute(select(Credit).where(Credit.entity_id == first_id)))
|
||||
.scalars()
|
||||
.all()
|
||||
)
|
||||
# Credits are replaced wholesale on re-sync, not duplicated.
|
||||
assert len(credits_) == 2 # 1 cast + 1 crew from FAKE_MOVIE
|
||||
Reference in New Issue
Block a user