From a6eeea6c1fd3a93dbd48ca362c679c1be9ffaf5f Mon Sep 17 00:00:00 2001 From: Thomas Peetz Date: Thu, 17 Apr 2025 02:21:14 +0200 Subject: [PATCH] add testing to fastapi --- .gitignore | 1 + fastapi/app/main.py | 3 +- fastapi/app/models/comics/__init__.py | 0 fastapi/app/models/comics/artist.py | 36 +++++++++++++++++++ fastapi/app/models/comics/comic.py | 46 ++++++++++++++++++++++++ fastapi/app/models/{comic.py => tysc.py} | 5 ++- fastapi/app/routers/comic.py | 46 +++++++++++++++++++----- fastapi/app/routers/tysc.py | 20 +++++++++++ fastapi/app/tests/__init__.py | 0 fastapi/app/tests/test_main.py | 10 ++++++ fastapi/requirements.txt | 3 +- 11 files changed, 156 insertions(+), 14 deletions(-) create mode 100644 fastapi/app/models/comics/__init__.py create mode 100644 fastapi/app/models/comics/artist.py create mode 100644 fastapi/app/models/comics/comic.py rename fastapi/app/models/{comic.py => tysc.py} (51%) create mode 100644 fastapi/app/routers/tysc.py create mode 100644 fastapi/app/tests/__init__.py create mode 100644 fastapi/app/tests/test_main.py diff --git a/.gitignore b/.gitignore index 5a473da..3c3b11f 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ kontor-schema/env kontor-schema/kontor_schema.egg-info kontor-gui/.pdm-python kontor-gui/dist +fastapi/.coverage diff --git a/fastapi/app/main.py b/fastapi/app/main.py index d4ff75c..1c51f0b 100644 --- a/fastapi/app/main.py +++ b/fastapi/app/main.py @@ -1,11 +1,12 @@ from fastapi import FastAPI -from .routers import comic, media +from .routers import comic, media, tysc app = FastAPI() app.include_router(comic.router) app.include_router(media.router) +app.include_router(tysc.router) @app.get("/") def read_root(): diff --git a/fastapi/app/models/comics/__init__.py b/fastapi/app/models/comics/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/fastapi/app/models/comics/artist.py b/fastapi/app/models/comics/artist.py new file mode 100644 index 0000000..2d0868a --- /dev/null +++ b/fastapi/app/models/comics/artist.py @@ -0,0 +1,36 @@ +from typing import List, Dict +from uuid import UUID + +from pydantic import BaseModel + +from app.schema import Artist + + +class ArtistCreation(BaseModel): + name: str + +class ArtistResponse(BaseModel): + id: UUID + name: str + +class ArtistDetailResponse(BaseModel): + id: UUID + name: str + works: Dict[str, List[str]] + +def get_artist_details(artist: Artist) -> ArtistDetailResponse: + works = {} + for work in artist.comic_works: + work_type = work.work_type.name + comic_title = work.comic.title + if work_type in works: + works[work_type].append(comic_title) + else: + works[work_type] = [comic_title] + response = ArtistDetailResponse( + id=artist.id, + name=artist.name, + works=works + ) + return response + diff --git a/fastapi/app/models/comics/comic.py b/fastapi/app/models/comics/comic.py new file mode 100644 index 0000000..3737ad2 --- /dev/null +++ b/fastapi/app/models/comics/comic.py @@ -0,0 +1,46 @@ +from typing import List, Dict +from uuid import UUID + +from pydantic import BaseModel + +from app.schema import Comic + + +class ComicResponse(BaseModel): + id: UUID + title: str + completed: bool + +class ComicDetailsResponse(BaseModel): + id: UUID + title: str + completed : bool + current_order : bool + publisher: str + volumes: List[str] + works: Dict[str, List[str]] + + +def get_comic_details(comic: Comic) -> ComicDetailsResponse | None: + volumes = [] + for volume in comic.volumes: + volumes.append(volume.name) + works = {} + for work in comic.comic_works: + work_type = work.work_type.name + artist_name = work.artist.name + if work_type in works: + works[work_type].append(artist_name) + else: + works[work_type] = [artist_name] + response = ComicDetailsResponse( + id=comic.id, + title=comic.title, + completed=(comic.completed == 1), + current_order=(comic.current_order == 1), + publisher=comic.publisher.name, + volumes=volumes, + works=works + ) + return response + diff --git a/fastapi/app/models/comic.py b/fastapi/app/models/tysc.py similarity index 51% rename from fastapi/app/models/comic.py rename to fastapi/app/models/tysc.py index 7d45f41..9eac5c6 100644 --- a/fastapi/app/models/comic.py +++ b/fastapi/app/models/tysc.py @@ -3,8 +3,7 @@ from uuid import UUID from pydantic import BaseModel -class ComicResponse(BaseModel): +class SportResponse(BaseModel): id: UUID - title: str - completed: bool + name: str diff --git a/fastapi/app/routers/comic.py b/fastapi/app/routers/comic.py index 217e9f9..efd9457 100644 --- a/fastapi/app/routers/comic.py +++ b/fastapi/app/routers/comic.py @@ -1,11 +1,11 @@ from uuid import UUID - -from fastapi import APIRouter, Depends, HTTPException +from typing import List +from fastapi import APIRouter, HTTPException, status from sqlalchemy import select -from sqlalchemy.orm import Session -from app.models.comic import ComicResponse -from app.schema import Comic, get_db, SessionDep +from app.models.comics.comic import ComicResponse, ComicDetailsResponse, get_comic_details +from app.models.comics.artist import ArtistCreation, ArtistDetailResponse, ArtistResponse, get_artist_details +from app.schema import Comic, SessionDep, Artist router = APIRouter( prefix="/comic", @@ -22,11 +22,39 @@ def get_all_comics(db: SessionDep) -> list[ComicResponse]: results.append(ComicResponse(id=comic.id, title=comic.title, completed=(comic.completed == 1))) return results - -@router.get("/comics/{comic_id}") -def get_comic(comic_id: UUID, db: SessionDep) -> ComicResponse: +@router.get("/comics/{comic_id}", response_model=ComicDetailsResponse) +def get_comic(comic_id: UUID, db: SessionDep) -> ComicDetailsResponse: comic = db.get(Comic, comic_id) if comic is None: raise HTTPException(status_code=404, detail="Comic could not be found") - response: ComicResponse = ComicResponse(id=comic_id, title=comic.title, completed=comic.completed) + response: ComicDetailsResponse = get_comic_details(comic) return response + +@router.get("/artists", response_model=List[ArtistResponse]) +def get_all_artists(db: SessionDep) -> List[ArtistResponse]: + results: List[ArtistResponse] = [] + artists = db.query(Artist).all() + for artist in artists: + results.append(ArtistResponse(id=artist.id, name=artist.name)) + return results + +@router.get("/artists/{artist_id}", response_model=ArtistDetailResponse) +def get_artist(artist_id: UUID, db: SessionDep) -> ArtistDetailResponse: + artist = db.get(Artist, artist_id) + if artist is None: + raise HTTPException(status_code=404, detail="Artist could not be found") + response: ArtistDetailResponse = get_artist_details(artist) + return response + +@router.post("/artists", status_code=status.HTTP_201_CREATED) +def add_artist(db: SessionDep, artist_creation: ArtistCreation) -> ArtistResponse: + artist: Artist = Artist() + setattr(artist, "name", artist_creation.name) + try: + db.add(artist) + db.commit() + except: + raise HTTPException(status_code=409, detail="Artist already added") + response = ArtistResponse(id=artist.id, name=artist.name) + return response + diff --git a/fastapi/app/routers/tysc.py b/fastapi/app/routers/tysc.py new file mode 100644 index 0000000..d5dad69 --- /dev/null +++ b/fastapi/app/routers/tysc.py @@ -0,0 +1,20 @@ +from typing import List +from fastapi import APIRouter + +from app.models.tysc import SportResponse +from app.schema import Sport, SessionDep + +router = APIRouter( + prefix="/tysc", + tags=["tysc"], + responses={404: {"description": "Not found"}}, +) + +@router.get("/sports") +def get_all_sports(db: SessionDep) -> List[SportResponse]: + results: list[SportResponse] = [] + sports = db.query(Sport).all() + for sport in sports: + results.append(SportResponse(id=sport.id, name=sport.name)) + return results + diff --git a/fastapi/app/tests/__init__.py b/fastapi/app/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/fastapi/app/tests/test_main.py b/fastapi/app/tests/test_main.py new file mode 100644 index 0000000..6e714d4 --- /dev/null +++ b/fastapi/app/tests/test_main.py @@ -0,0 +1,10 @@ +from fastapi.testclient import TestClient + +from app.main import app + +client = TestClient(app) + +def test_get_authors(): + response = client.get("/comic/artists") + assert response.status_code == 200 + assert len(response.json()) == 5 diff --git a/fastapi/requirements.txt b/fastapi/requirements.txt index 9c7bc24..551f12f 100644 --- a/fastapi/requirements.txt +++ b/fastapi/requirements.txt @@ -7,4 +7,5 @@ beautifulsoup4 sqlmodel requests fastapi[standard] - +httpx==0.24.1 +pytest==7.4.0