chrysopedia/backend/tests/test_video_detail.py
jlightner 87cb667848 test: Added GET /videos/{video_id} and GET /videos/{video_id}/transcrip…
- "backend/routers/videos.py"
- "backend/schemas.py"
- "backend/tests/test_video_detail.py"

GSD-Task: S01/T01
2026-04-03 23:42:43 +00:00

133 lines
4.5 KiB
Python

"""Tests for video detail and transcript endpoints.
Covers:
- GET /api/v1/videos/{video_id} — single video with creator info
- GET /api/v1/videos/{video_id}/transcript — ordered segments
"""
import uuid
import pytest
import pytest_asyncio
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker
from models import ContentType, Creator, ProcessingStatus, SourceVideo, TranscriptSegment
@pytest_asyncio.fixture()
async def video_with_creator(db_engine):
"""Create a Creator + SourceVideo pair. Returns dict with IDs."""
factory = async_sessionmaker(db_engine, class_=AsyncSession, expire_on_commit=False)
async with factory() as session:
creator = Creator(
name="TestProducer",
slug="testproducer",
folder_name="TestProducer",
genres=["electronic"],
)
session.add(creator)
await session.flush()
video = SourceVideo(
creator_id=creator.id,
filename="synth-tutorial.mp4",
file_path="TestProducer/synth-tutorial.mp4",
duration_seconds=720,
content_type=ContentType.tutorial,
processing_status=ProcessingStatus.complete,
)
session.add(video)
await session.flush()
result = {
"creator_id": creator.id,
"creator_name": creator.name,
"creator_slug": creator.slug,
"video_id": video.id,
}
await session.commit()
return result
@pytest_asyncio.fixture()
async def video_with_segments(db_engine, video_with_creator):
"""Add transcript segments to the video. Returns video_with_creator + segment count."""
factory = async_sessionmaker(db_engine, class_=AsyncSession, expire_on_commit=False)
async with factory() as session:
segments = [
TranscriptSegment(
source_video_id=video_with_creator["video_id"],
start_time=float(i * 10),
end_time=float(i * 10 + 9),
text=f"Segment {i} text content.",
segment_index=i,
)
for i in range(5)
]
session.add_all(segments)
await session.commit()
return {**video_with_creator, "segment_count": 5}
# ── Video Detail ─────────────────────────────────────────────────────────────
@pytest.mark.asyncio
async def test_get_video_detail_success(client, video_with_creator):
vid = video_with_creator["video_id"]
resp = await client.get(f"/api/v1/videos/{vid}")
assert resp.status_code == 200
data = resp.json()
assert data["id"] == str(vid)
assert data["filename"] == "synth-tutorial.mp4"
assert data["creator_name"] == "TestProducer"
assert data["creator_slug"] == "testproducer"
# video_url is always None for now
assert data["video_url"] is None
@pytest.mark.asyncio
async def test_get_video_detail_404(client):
fake_id = uuid.uuid4()
resp = await client.get(f"/api/v1/videos/{fake_id}")
assert resp.status_code == 404
assert "not found" in resp.json()["detail"].lower()
# ── Transcript ───────────────────────────────────────────────────────────────
@pytest.mark.asyncio
async def test_get_transcript_success(client, video_with_segments):
vid = video_with_segments["video_id"]
resp = await client.get(f"/api/v1/videos/{vid}/transcript")
assert resp.status_code == 200
data = resp.json()
assert data["video_id"] == str(vid)
assert data["total"] == 5
assert len(data["segments"]) == 5
# Verify ordering by segment_index
indices = [s["segment_index"] for s in data["segments"]]
assert indices == [0, 1, 2, 3, 4]
@pytest.mark.asyncio
async def test_get_transcript_404(client):
fake_id = uuid.uuid4()
resp = await client.get(f"/api/v1/videos/{fake_id}/transcript")
assert resp.status_code == 404
@pytest.mark.asyncio
async def test_get_transcript_empty(client, video_with_creator):
"""Video exists but has no segments — returns empty list."""
vid = video_with_creator["video_id"]
resp = await client.get(f"/api/v1/videos/{vid}/transcript")
assert resp.status_code == 200
data = resp.json()
assert data["video_id"] == str(vid)
assert data["total"] == 0
assert data["segments"] == []