import json from pathlib import Path from httpx import AsyncClient import pytest from collections.abc import AsyncGenerator from typing import Any from sqlalchemy.ext.asyncio import ( AsyncSession, AsyncEngine, async_sessionmaker, ) from advanced_alchemy.base import UUIDAuditBase from chitai.database import models as m from chitai import services from chitai.database.config import config @pytest.fixture(autouse=True) def _patch_db( engine: AsyncEngine, sessionmaker: async_sessionmaker[AsyncSession], monkeypatch: pytest.MonkeyPatch, ) -> None: monkeypatch.setattr(config, "session_maker", sessionmaker) monkeypatch.setattr(config, "engine_instance", engine) @pytest.fixture(autouse=True) async def _seed_db( engine: AsyncEngine, sessionmaker: async_sessionmaker[AsyncSession], raw_users: list[m.User | dict[str, Any]], raw_libraries: list[m.Library | dict[str, Any]], ) -> AsyncGenerator[None, None]: """Populate test database with. Args: engine: The SQLAlchemy engine instance. sessionmaker: The SQLAlchemy sessionmaker factory. raw_users: Test users to add to the database raw_teams: Test teams to add to the database """ metadata = UUIDAuditBase.registry.metadata async with engine.begin() as conn: await conn.run_sync(metadata.drop_all) await conn.run_sync(metadata.create_all) async with services.UserService.new(sessionmaker()) as users_service: await users_service.create_many(raw_users, auto_commit=True) async with services.LibraryService.new(sessionmaker()) as library_service: await library_service.create_many(raw_libraries, auto_commit=True) yield @pytest.fixture async def populated_authenticated_client( authenticated_client: AsyncClient, books_to_upload: list[tuple[Path, dict]] ) -> AsyncClient: # Upload books for path, data in books_to_upload: await upload_book_via_api(authenticated_client, data, path) await create_bookshelf( authenticated_client, {"title": "Favourites", "library_id": 1} ) await add_books_to_bookshelf(authenticated_client, 1, [1, 2]) return authenticated_client @pytest.fixture def books_to_upload() -> list[tuple[Path, dict]]: return [ ( Path( "tests/data_files/The Adventures of Sherlock Holmes - Arthur Conan Doyle.epub" ), { "library_id": "1", "title": "The Adventures of Sherlock Holmes", "description": "Some description...", "authors": "Arthur Conan Doyle", "publisher": "Some Publisher", "tags": "Mystery", "edition": "2", "language": "en", "pages": "300", "series": "Sherlock Holmes", "series_position": "1", "identifiers": json.dumps({"isbn-10": "1234567890"}), }, ), ( Path("tests/data_files/Frankenstein - Mary Shelley.epub"), { "library_id": "1", "title": "Frankenstein", "description": "Some description...", "authors": "Mary Shelley", "tags": "Mystery", "edition": "1", "language": "en", "pages": "250", }, ), ( Path("tests/data_files/A Tale of Two Cities - Charles Dickens.epub"), { "library_id": "1", "title": "A Tale of Two Cities", "description": "Some description...", "authors": "Charles Dickens", "tags": "Classic", "edition": "1", "language": "en", "pages": "500", }, ), ] async def upload_book_via_api( client: AsyncClient, book_data: dict, book_file_path: Path ) -> None: with book_file_path.open("rb") as file: files = {"files": file} response = await client.post("/books?library_id=1", data=book_data, files=files) assert response.status_code == 201 async def create_bookshelf(client: AsyncClient, shelf_data: dict) -> None: response = await client.post("/shelves", json=shelf_data) assert response.status_code == 201 async def add_books_to_bookshelf( client: AsyncClient, shelf_id: int, book_ids: list[int] ) -> None: query_params = "" for id in book_ids: query_params += f"book_ids={id}&" response = await client.post( f"/shelves/{shelf_id}/books", params={"book_ids": book_ids} ) assert response.status_code == 201