media-rip/backend/tests/test_file_serving.py

124 lines
3.9 KiB
Python

"""Tests for cookie auth upload and file serving."""
from __future__ import annotations
from datetime import datetime, timezone
import pytest
import pytest_asyncio
from fastapi import FastAPI
from httpx import ASGITransport, AsyncClient
from app.core.config import AppConfig
from app.core.database import close_db, init_db
from app.middleware.session import SessionMiddleware
from app.routers.cookies import router as cookies_router
from app.routers.files import router as files_router
@pytest_asyncio.fixture()
async def file_client(tmp_path):
"""Client with file serving and cookie upload routers."""
db_path = str(tmp_path / "file_test.db")
dl_dir = tmp_path / "downloads"
dl_dir.mkdir()
config = AppConfig(
server={"db_path": db_path},
downloads={"output_dir": str(dl_dir)},
)
db_conn = await init_db(db_path)
app = FastAPI()
app.add_middleware(SessionMiddleware)
app.include_router(cookies_router, prefix="/api")
app.include_router(files_router, prefix="/api")
app.state.config = config
app.state.db = db_conn
app.state.start_time = datetime.now(timezone.utc)
transport = ASGITransport(app=app)
async with AsyncClient(transport=transport, base_url="http://test") as ac:
yield ac, dl_dir
await close_db(db_conn)
class TestCookieUpload:
"""Cookie auth upload tests."""
@pytest.mark.anyio
async def test_upload_cookies(self, file_client):
client, dl_dir = file_client
cookie_content = b"# Netscape HTTP Cookie File\n.example.com\tTRUE\t/\tFALSE\t0\tSID\tvalue123\n"
resp = await client.post(
"/api/cookies",
files={"file": ("cookies.txt", cookie_content, "text/plain")},
)
assert resp.status_code == 200
data = resp.json()
assert data["status"] == "ok"
assert data["size"] > 0
@pytest.mark.anyio
async def test_upload_normalizes_crlf(self, file_client):
client, dl_dir = file_client
# Windows-style line endings
cookie_content = b"line1\r\nline2\r\nline3\r\n"
resp = await client.post(
"/api/cookies",
files={"file": ("cookies.txt", cookie_content, "text/plain")},
)
assert resp.status_code == 200
@pytest.mark.anyio
async def test_delete_cookies(self, file_client):
client, dl_dir = file_client
# Upload first
await client.post(
"/api/cookies",
files={"file": ("cookies.txt", b"data", "text/plain")},
)
# Delete
resp = await client.delete("/api/cookies")
assert resp.status_code == 200
data = resp.json()
assert data["status"] == "deleted"
@pytest.mark.anyio
async def test_delete_nonexistent_cookies(self, file_client):
client, dl_dir = file_client
resp = await client.delete("/api/cookies")
assert resp.status_code == 200
data = resp.json()
assert data["status"] == "not_found"
class TestFileServing:
"""File download serving tests."""
@pytest.mark.anyio
async def test_serve_existing_file(self, file_client):
client, dl_dir = file_client
# Create a file in the downloads dir
test_file = dl_dir / "video.mp4"
test_file.write_bytes(b"fake video content")
resp = await client.get("/api/downloads/video.mp4")
assert resp.status_code == 200
assert resp.content == b"fake video content"
@pytest.mark.anyio
async def test_missing_file_returns_404(self, file_client):
client, dl_dir = file_client
resp = await client.get("/api/downloads/nonexistent.mp4")
assert resp.status_code == 404
@pytest.mark.anyio
async def test_path_traversal_blocked(self, file_client):
client, dl_dir = file_client
resp = await client.get("/api/downloads/../../../etc/passwd")
assert resp.status_code in (403, 404)