"""Tests d'intégration API (auth, downloads, favoris, streaming, admin).""" import pytest from httpx import ASGITransport, AsyncClient from app.config import get_settings from app.db import db from app.main import app @pytest.fixture async def client() -> AsyncClient: transport = ASGITransport(app=app) async with AsyncClient(transport=transport, base_url="http://test") as c: yield c @pytest.fixture async def admin_cookies(client: AsyncClient): r = await client.post("/auth/register", data={"username": "admin", "password": "secret123"}) assert r.status_code == 303 return r.cookies async def test_health(client): r = await client.get("/health") assert r.status_code == 200 and r.json() == {"status": "ok"} async def test_pages_require_auth(client): r = await client.get("/", follow_redirects=False) assert r.status_code == 303 and r.headers["location"] == "/login" r = await client.get("/api/downloads", follow_redirects=False) assert r.status_code == 303 async def test_register_login_me(client, admin_cookies): r = await client.get("/auth/me", cookies=admin_cookies) data = r.json() assert data["username"] == "admin" and data["is_admin"] is True async def test_duplicate_username(client): await client.post("/auth/register", data={"username": "toto", "password": "secret123"}) r = await client.post("/auth/register", data={"username": "toto", "password": "secret123"}) assert r.status_code == 409 async def test_enqueue_bad_internal_url(client, admin_cookies): r = await client.post( "/api/downloads", json={"internal_url": "invalide"}, cookies=admin_cookies ) assert r.status_code == 422 async def test_favorites_flow(client, admin_cookies): fav = {"source": "vostfree", "source_id": "1-x", "title": "Frieren"} r = await client.post("/api/favorites", json=fav, cookies=admin_cookies) assert r.status_code == 201 r = await client.post("/api/favorites", json=fav, cookies=admin_cookies) assert r.status_code == 409 r = await client.get("/api/favorites", cookies=admin_cookies) assert r.json()["total"] == 1 fav_id = r.json()["items"][0]["id"] r = await client.delete(f"/api/favorites/{fav_id}", cookies=admin_cookies) assert r.status_code == 200 async def test_stream_range(client, admin_cookies): payload = b"video-bytes" * 1000 path = get_settings().download_dir / "ep1.mp4" path.write_bytes(payload) await db.execute( "INSERT INTO downloads (source_key, video_url, page_url, title, file_path, status, " "total_bytes, downloaded_bytes) VALUES ('k', 'u', 'p', 'Ep 1', 'ep1.mp4', 'done', ?, ?)", (len(payload), len(payload)), ) r = await client.get("/api/stream/1", cookies=admin_cookies) assert r.status_code == 200 and r.content == payload r = await client.get("/api/stream/1", headers={"Range": "bytes=10-19"}, cookies=admin_cookies) assert r.status_code == 206 assert r.content == payload[10:20] assert r.headers["content-range"] == f"bytes 10-19/{len(payload)}" r = await client.get( "/api/stream/1", headers={"Range": "bytes=999999999-"}, cookies=admin_cookies ) assert r.status_code == 416 async def test_admin_required(client): await client.post("/auth/register", data={"username": "admin2", "password": "secret123"}) r = await client.post("/auth/register", data={"username": "user", "password": "secret123"}) user_cookies = r.cookies r = await client.get("/api/admin/users", cookies=user_cookies) assert r.status_code == 403 async def test_admin_user_management(client, admin_cookies): await client.post("/auth/register", data={"username": "user", "password": "secret123"}) r = await client.get("/api/admin/users", cookies=admin_cookies) users = {u["username"]: u["id"] for u in r.json()} assert set(users) == {"admin", "user"} admin_id, user_id = users["admin"], users["user"] r = await client.post(f"/api/admin/users/{user_id}/toggle-active", cookies=admin_cookies) assert r.json() == {"is_active": False} r = await client.post(f"/api/admin/users/{user_id}/toggle-admin", cookies=admin_cookies) assert r.json() == {"is_admin": True} # pas d'auto-modification r = await client.post(f"/api/admin/users/{admin_id}/toggle-admin", cookies=admin_cookies) assert r.status_code == 400 r = await client.delete(f"/api/admin/users/{user_id}", cookies=admin_cookies) assert r.status_code == 200 async def test_source_toggle(client, admin_cookies): r = await client.post( "/api/admin/sources/vostfree/toggle", json={"enabled": False}, cookies=admin_cookies ) assert r.json()["enabled"] is False r = await client.get("/api/sources", cookies=admin_cookies) states = {s["name"]: s["enabled"] for s in r.json()} assert states["vostfree"] is False assert states["french_manga"] is True