mirror of
https://github.com/xpltdco/media-rip.git
synced 2026-04-03 02:53:58 -06:00
Critical fix: - Dockerfile env var was MEDIARIP__DATABASE__PATH (ignored) — now MEDIARIP__SERVER__DB_PATH DB was landing at /app/mediarip.db (lost on restart) instead of /data/mediarip.db Persistence model: - /downloads → media files (bind mount recommended) - /data → SQLite DB, session cookies, error logs (named volume) - /themes → custom CSS themes (read-only bind mount) - /app/config.yaml → optional YAML config (read-only bind mount) Other changes: - Add server.data_dir config field (default: /data) for explicit session storage - Cookie storage uses data_dir instead of fragile path math from output_dir parent - Lifespan creates data_dir on startup - .dockerignore excludes tests, dev DB, egg-info - docker-compose.yml: inline admin/purge config examples - docker-compose.example.yml: parameterized with env vars - .env.example: session mode, clearer docs - README: Docker volumes table, admin setup docs, full config reference - PROJECT.md: reflects completed v1.0 state - REQUIREMENTS.md: all 26 requirements validated
117 lines
3.9 KiB
Python
117 lines
3.9 KiB
Python
"""Shared test fixtures for the media-rip backend test suite."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
from datetime import datetime, timezone
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
import pytest_asyncio
|
|
from httpx import ASGITransport, AsyncClient
|
|
|
|
from app.core.config import AppConfig
|
|
from app.core.database import close_db, init_db
|
|
from app.core.sse_broker import SSEBroker
|
|
|
|
|
|
@pytest.fixture()
|
|
def tmp_db_path(tmp_path: Path) -> str:
|
|
"""Return a path for a temporary SQLite database."""
|
|
return str(tmp_path / "test.db")
|
|
|
|
|
|
@pytest.fixture()
|
|
def test_config(tmp_path: Path) -> AppConfig:
|
|
"""Return an AppConfig with downloads.output_dir pointing at a temp dir."""
|
|
dl_dir = tmp_path / "downloads"
|
|
dl_dir.mkdir()
|
|
return AppConfig(
|
|
server={"data_dir": str(tmp_path / "data")},
|
|
downloads={"output_dir": str(dl_dir)},
|
|
)
|
|
|
|
|
|
@pytest_asyncio.fixture()
|
|
async def db(tmp_db_path: str):
|
|
"""Yield an initialised async database connection, cleaned up after."""
|
|
conn = await init_db(tmp_db_path)
|
|
yield conn
|
|
await close_db(conn)
|
|
|
|
|
|
@pytest_asyncio.fixture()
|
|
async def broker() -> SSEBroker:
|
|
"""Return an SSEBroker bound to the running event loop."""
|
|
loop = asyncio.get_running_loop()
|
|
return SSEBroker(loop)
|
|
|
|
|
|
@pytest_asyncio.fixture()
|
|
async def client(tmp_path: Path):
|
|
"""Yield an httpx AsyncClient backed by the FastAPI app with temp resources.
|
|
|
|
Manually manages the app lifespan since httpx ASGITransport doesn't
|
|
trigger Starlette lifespan events.
|
|
"""
|
|
from fastapi import FastAPI
|
|
|
|
from app.core.config import AppConfig
|
|
from app.core.database import close_db, init_db
|
|
from app.core.sse_broker import SSEBroker
|
|
from app.middleware.session import SessionMiddleware
|
|
from app.routers.admin import router as admin_router
|
|
from app.routers.cookies import router as cookies_router
|
|
from app.routers.downloads import router as downloads_router
|
|
from app.routers.files import router as files_router
|
|
from app.routers.formats import router as formats_router
|
|
from app.routers.health import router as health_router
|
|
from app.routers.sse import router as sse_router
|
|
from app.routers.system import router as system_router
|
|
from app.routers.themes import router as themes_router
|
|
from app.services.download import DownloadService
|
|
|
|
# Temp paths
|
|
db_path = str(tmp_path / "api_test.db")
|
|
dl_dir = tmp_path / "downloads"
|
|
dl_dir.mkdir()
|
|
|
|
# Build config pointing at temp resources
|
|
config = AppConfig(
|
|
server={"db_path": db_path, "data_dir": str(tmp_path / "data")},
|
|
downloads={"output_dir": str(dl_dir)},
|
|
)
|
|
|
|
# Initialise services (same as app lifespan)
|
|
db_conn = await init_db(db_path)
|
|
loop = asyncio.get_running_loop()
|
|
broker = SSEBroker(loop)
|
|
download_service = DownloadService(config, db_conn, broker, loop)
|
|
|
|
# Build a fresh FastAPI app with routers
|
|
test_app = FastAPI(title="media.rip()")
|
|
test_app.add_middleware(SessionMiddleware)
|
|
test_app.include_router(admin_router, prefix="/api")
|
|
test_app.include_router(cookies_router, prefix="/api")
|
|
test_app.include_router(downloads_router, prefix="/api")
|
|
test_app.include_router(files_router, prefix="/api")
|
|
test_app.include_router(formats_router, prefix="/api")
|
|
test_app.include_router(health_router, prefix="/api")
|
|
test_app.include_router(sse_router, prefix="/api")
|
|
test_app.include_router(system_router, prefix="/api")
|
|
test_app.include_router(themes_router, prefix="/api")
|
|
|
|
# Wire state manually
|
|
test_app.state.config = config
|
|
test_app.state.db = db_conn
|
|
test_app.state.broker = broker
|
|
test_app.state.download_service = download_service
|
|
test_app.state.start_time = datetime.now(timezone.utc)
|
|
|
|
transport = ASGITransport(app=test_app)
|
|
async with AsyncClient(transport=transport, base_url="http://test") as ac:
|
|
yield ac
|
|
|
|
# Teardown
|
|
download_service.shutdown()
|
|
await close_db(db_conn)
|