diff --git a/fastapp/Dockerfile b/fastapp/Dockerfile index c6c0684..f913c58 100644 --- a/fastapp/Dockerfile +++ b/fastapp/Dockerfile @@ -1,4 +1,7 @@ FROM python:3.11.4-slim-bullseye as prod +RUN apt-get update && apt-get install -y \ + gcc \ + && rm -rf /var/lib/apt/lists/* RUN pip install poetry==1.8.2 @@ -13,6 +16,10 @@ WORKDIR /app/src # Installing requirements RUN --mount=type=cache,target=/tmp/poetry_cache poetry install --only main +# Removing gcc +RUN apt-get purge -y \ + gcc \ + && rm -rf /var/lib/apt/lists/* # Copying actuall application COPY . /app/src/ diff --git a/fastapp/README.md b/fastapp/README.md index 0fbd905..bba4e66 100644 --- a/fastapp/README.md +++ b/fastapp/README.md @@ -116,6 +116,12 @@ docker-compose down ``` For running tests on your local machine. +1. you need to start a database. + +I prefer doing it with docker: +``` +docker run -p "5432:5432" -e "POSTGRES_PASSWORD=fastapp" -e "POSTGRES_USER=fastapp" -e "POSTGRES_DB=fastapp" postgres:16.3-bullseye +``` 2. Run the pytest. diff --git a/fastapp/docker-compose.yml b/fastapp/docker-compose.yml index db84d7c..3a59537 100644 --- a/fastapp/docker-compose.yml +++ b/fastapp/docker-compose.yml @@ -7,11 +7,32 @@ services: restart: always env_file: - .env + depends_on: + db: + condition: service_healthy environment: FASTAPP_HOST: 0.0.0.0 - FASTAPP_DB_FILE: /db_data/db.sqlite3 + FASTAPP_DB_HOST: fastapp-db + FASTAPP_DB_PORT: 5432 + FASTAPP_DB_USER: fastapp + FASTAPP_DB_PASS: fastapp + FASTAPP_DB_BASE: fastapp + + db: + image: postgres:16.3-bullseye + hostname: fastapp-db + environment: + POSTGRES_PASSWORD: "fastapp" + POSTGRES_USER: "fastapp" + POSTGRES_DB: "fastapp" volumes: - - fastapp-db-data:/db_data/ + - fastapp-db-data:/var/lib/postgresql/data + restart: always + healthcheck: + test: pg_isready -U fastapp + interval: 2s + timeout: 3s + retries: 40 diff --git a/fastapp/fastapp/db/dao/__init__.py b/fastapp/fastapp/db/dao/__init__.py new file mode 100644 index 0000000..db62a0a --- /dev/null +++ b/fastapp/fastapp/db/dao/__init__.py @@ -0,0 +1 @@ +"""DAO classes.""" diff --git a/fastapp/fastapp/db/dao/dummy_dao.py b/fastapp/fastapp/db/dao/dummy_dao.py new file mode 100644 index 0000000..3ebd842 --- /dev/null +++ b/fastapp/fastapp/db/dao/dummy_dao.py @@ -0,0 +1,50 @@ +from typing import List, Optional + +from fastapi import Depends +from sqlalchemy import select +from sqlalchemy.ext.asyncio import AsyncSession + +from fastapp.db.dependencies import get_db_session +from fastapp.db.models.dummy_model import DummyModel + + +class DummyDAO: + """Class for accessing dummy table.""" + + def __init__(self, session: AsyncSession = Depends(get_db_session)) -> None: + self.session = session + + async def create_dummy_model(self, name: str) -> None: + """ + Add single dummy to session. + + :param name: name of a dummy. + """ + self.session.add(DummyModel(name=name)) + + async def get_all_dummies(self, limit: int, offset: int) -> List[DummyModel]: + """ + Get all dummy models with limit/offset pagination. + + :param limit: limit of dummies. + :param offset: offset of dummies. + :return: stream of dummies. + """ + raw_dummies = await self.session.execute( + select(DummyModel).limit(limit).offset(offset), + ) + + return list(raw_dummies.scalars().fetchall()) + + async def filter(self, name: Optional[str] = None) -> List[DummyModel]: + """ + Get specific dummy model. + + :param name: name of dummy instance. + :return: dummy models. + """ + query = select(DummyModel) + if name: + query = query.where(DummyModel.name == name) + rows = await self.session.execute(query) + return list(rows.scalars().fetchall()) diff --git a/fastapp/fastapp/db/models/dummy_model.py b/fastapp/fastapp/db/models/dummy_model.py new file mode 100644 index 0000000..5869eb1 --- /dev/null +++ b/fastapp/fastapp/db/models/dummy_model.py @@ -0,0 +1,13 @@ +from sqlalchemy.orm import Mapped, mapped_column +from sqlalchemy.sql.sqltypes import String + +from fastapp.db.base import Base + + +class DummyModel(Base): + """Model for demo purpose.""" + + __tablename__ = "dummy_model" + + id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) + name: Mapped[str] = mapped_column(String(length=200)) diff --git a/fastapp/fastapp/db/utils.py b/fastapp/fastapp/db/utils.py index 470fbf9..a7a0d50 100644 --- a/fastapp/fastapp/db/utils.py +++ b/fastapp/fastapp/db/utils.py @@ -1,13 +1,44 @@ -from pathlib import Path +from sqlalchemy import text +from sqlalchemy.engine import make_url +from sqlalchemy.ext.asyncio import create_async_engine from fastapp.settings import settings async def create_database() -> None: """Create a database.""" + db_url = make_url(str(settings.db_url.with_path("/postgres"))) + engine = create_async_engine(db_url, isolation_level="AUTOCOMMIT") + + async with engine.connect() as conn: + database_existance = await conn.execute( + text( + f"SELECT 1 FROM pg_database WHERE datname='{settings.db_base}'", # noqa: S608 + ), + ) + database_exists = database_existance.scalar() == 1 + + if database_exists: + await drop_database() + + async with engine.connect() as conn: + await conn.execute( + text( + f'CREATE DATABASE "{settings.db_base}" ENCODING "utf8" TEMPLATE template1', # noqa: E501 + ), + ) async def drop_database() -> None: """Drop current database.""" - if settings.db_file.exists(): - Path(settings.db_file).unlink() + db_url = make_url(str(settings.db_url.with_path("/postgres"))) + engine = create_async_engine(db_url, isolation_level="AUTOCOMMIT") + async with engine.connect() as conn: + disc_users = ( + "SELECT pg_terminate_backend(pg_stat_activity.pid) " # noqa: S608 + "FROM pg_stat_activity " + f"WHERE pg_stat_activity.datname = '{settings.db_base}' " + "AND pid <> pg_backend_pid();" + ) + await conn.execute(text(disc_users)) + await conn.execute(text(f'DROP DATABASE "{settings.db_base}"')) diff --git a/fastapp/fastapp/settings.py b/fastapp/fastapp/settings.py index 8f3e439..45e0c17 100644 --- a/fastapp/fastapp/settings.py +++ b/fastapp/fastapp/settings.py @@ -39,7 +39,11 @@ class Settings(BaseSettings): log_level: LogLevel = LogLevel.INFO # Variables for the database - db_file: Path = TEMP_DIR / "db.sqlite3" + db_host: str = "localhost" + db_port: int = 5432 + db_user: str = "fastapp" + db_pass: str = "fastapp" + db_base: str = "admin" db_echo: bool = False @property @@ -49,7 +53,14 @@ def db_url(self) -> URL: :return: database URL. """ - return URL.build(scheme="sqlite+aiosqlite", path=f"///{self.db_file}") + return URL.build( + scheme="postgresql+asyncpg", + host=self.db_host, + port=self.db_port, + user=self.db_user, + password=self.db_pass, + path=f"/{self.db_base}", + ) model_config = SettingsConfigDict( env_file=".env", diff --git a/fastapp/fastapp/web/api/dummy/__init__.py b/fastapp/fastapp/web/api/dummy/__init__.py new file mode 100644 index 0000000..925db5b --- /dev/null +++ b/fastapp/fastapp/web/api/dummy/__init__.py @@ -0,0 +1,5 @@ +"""Dummy model API.""" + +from fastapp.web.api.dummy.views import router + +__all__ = ["router"] diff --git a/fastapp/fastapp/web/api/dummy/schema.py b/fastapp/fastapp/web/api/dummy/schema.py new file mode 100644 index 0000000..247c5d1 --- /dev/null +++ b/fastapp/fastapp/web/api/dummy/schema.py @@ -0,0 +1,20 @@ +from pydantic import BaseModel, ConfigDict + + +class DummyModelDTO(BaseModel): + """ + DTO for dummy models. + + It returned when accessing dummy models from the API. + """ + + id: int + name: str + + model_config = ConfigDict(from_attributes=True) + + +class DummyModelInputDTO(BaseModel): + """DTO for creating new dummy model.""" + + name: str diff --git a/fastapp/fastapp/web/api/dummy/views.py b/fastapp/fastapp/web/api/dummy/views.py new file mode 100644 index 0000000..0b8bbfa --- /dev/null +++ b/fastapp/fastapp/web/api/dummy/views.py @@ -0,0 +1,41 @@ +from typing import List + +from fastapi import APIRouter +from fastapi.param_functions import Depends + +from fastapp.db.dao.dummy_dao import DummyDAO +from fastapp.db.models.dummy_model import DummyModel +from fastapp.web.api.dummy.schema import DummyModelDTO, DummyModelInputDTO + +router = APIRouter() + + +@router.get("/", response_model=List[DummyModelDTO]) +async def get_dummy_models( + limit: int = 10, + offset: int = 0, + dummy_dao: DummyDAO = Depends(), +) -> List[DummyModel]: + """ + Retrieve all dummy objects from the database. + + :param limit: limit of dummy objects, defaults to 10. + :param offset: offset of dummy objects, defaults to 0. + :param dummy_dao: DAO for dummy models. + :return: list of dummy objects from database. + """ + return await dummy_dao.get_all_dummies(limit=limit, offset=offset) + + +@router.put("/") +async def create_dummy_model( + new_dummy_object: DummyModelInputDTO, + dummy_dao: DummyDAO = Depends(), +) -> None: + """ + Creates dummy model in the database. + + :param new_dummy_object: new dummy model item. + :param dummy_dao: DAO for dummy models. + """ + await dummy_dao.create_dummy_model(name=new_dummy_object.name) diff --git a/fastapp/fastapp/web/api/router.py b/fastapp/fastapp/web/api/router.py index 883b5b9..49af071 100644 --- a/fastapp/fastapp/web/api/router.py +++ b/fastapp/fastapp/web/api/router.py @@ -1,7 +1,8 @@ from fastapi.routing import APIRouter -from fastapp.web.api import echo, monitoring +from fastapp.web.api import dummy, echo, monitoring api_router = APIRouter() api_router.include_router(monitoring.router) api_router.include_router(echo.router, prefix="/echo", tags=["echo"]) +api_router.include_router(dummy.router, prefix="/dummy", tags=["dummy"]) diff --git a/fastapp/poetry.lock b/fastapp/poetry.lock index e5a9bfd..8ee14b1 100644 --- a/fastapp/poetry.lock +++ b/fastapp/poetry.lock @@ -1,23 +1,5 @@ # This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. -[[package]] -name = "aiosqlite" -version = "0.20.0" -description = "asyncio bridge to the standard sqlite3 module" -optional = false -python-versions = ">=3.8" -files = [ - {file = "aiosqlite-0.20.0-py3-none-any.whl", hash = "sha256:36a1deaca0cac40ebe32aac9977a6e2bbc7f5189f23f4a54d5908986729e5bd6"}, - {file = "aiosqlite-0.20.0.tar.gz", hash = "sha256:6d35c8c256637f4672f843c31021464090805bf925385ac39473fb16eaaca3d7"}, -] - -[package.dependencies] -typing_extensions = ">=4.0" - -[package.extras] -dev = ["attribution (==1.7.0)", "black (==24.2.0)", "coverage[toml] (==7.4.1)", "flake8 (==7.0.0)", "flake8-bugbear (==24.2.6)", "flit (==3.9.0)", "mypy (==1.8.0)", "ufmt (==2.3.0)", "usort (==1.0.8.post1)"] -docs = ["sphinx (==7.2.6)", "sphinx-mdinclude (==0.5.3)"] - [[package]] name = "annotated-types" version = "0.7.0" @@ -51,6 +33,74 @@ doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21.0b1)"] trio = ["trio (>=0.26.1)"] +[[package]] +name = "async-timeout" +version = "5.0.1" +description = "Timeout context manager for asyncio programs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c"}, + {file = "async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3"}, +] + +[[package]] +name = "asyncpg" +version = "0.29.0" +description = "An asyncio PostgreSQL driver" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "asyncpg-0.29.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72fd0ef9f00aeed37179c62282a3d14262dbbafb74ec0ba16e1b1864d8a12169"}, + {file = "asyncpg-0.29.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:52e8f8f9ff6e21f9b39ca9f8e3e33a5fcdceaf5667a8c5c32bee158e313be385"}, + {file = "asyncpg-0.29.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9e6823a7012be8b68301342ba33b4740e5a166f6bbda0aee32bc01638491a22"}, + {file = "asyncpg-0.29.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:746e80d83ad5d5464cfbf94315eb6744222ab00aa4e522b704322fb182b83610"}, + {file = "asyncpg-0.29.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ff8e8109cd6a46ff852a5e6bab8b0a047d7ea42fcb7ca5ae6eaae97d8eacf397"}, + {file = "asyncpg-0.29.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:97eb024685b1d7e72b1972863de527c11ff87960837919dac6e34754768098eb"}, + {file = "asyncpg-0.29.0-cp310-cp310-win32.whl", hash = "sha256:5bbb7f2cafd8d1fa3e65431833de2642f4b2124be61a449fa064e1a08d27e449"}, + {file = "asyncpg-0.29.0-cp310-cp310-win_amd64.whl", hash = "sha256:76c3ac6530904838a4b650b2880f8e7af938ee049e769ec2fba7cd66469d7772"}, + {file = "asyncpg-0.29.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4900ee08e85af01adb207519bb4e14b1cae8fd21e0ccf80fac6aa60b6da37b4"}, + {file = "asyncpg-0.29.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a65c1dcd820d5aea7c7d82a3fdcb70e096f8f70d1a8bf93eb458e49bfad036ac"}, + {file = "asyncpg-0.29.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b52e46f165585fd6af4863f268566668407c76b2c72d366bb8b522fa66f1870"}, + {file = "asyncpg-0.29.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc600ee8ef3dd38b8d67421359779f8ccec30b463e7aec7ed481c8346decf99f"}, + {file = "asyncpg-0.29.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:039a261af4f38f949095e1e780bae84a25ffe3e370175193174eb08d3cecab23"}, + {file = "asyncpg-0.29.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6feaf2d8f9138d190e5ec4390c1715c3e87b37715cd69b2c3dfca616134efd2b"}, + {file = "asyncpg-0.29.0-cp311-cp311-win32.whl", hash = "sha256:1e186427c88225ef730555f5fdda6c1812daa884064bfe6bc462fd3a71c4b675"}, + {file = "asyncpg-0.29.0-cp311-cp311-win_amd64.whl", hash = "sha256:cfe73ffae35f518cfd6e4e5f5abb2618ceb5ef02a2365ce64f132601000587d3"}, + {file = "asyncpg-0.29.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6011b0dc29886ab424dc042bf9eeb507670a3b40aece3439944006aafe023178"}, + {file = "asyncpg-0.29.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b544ffc66b039d5ec5a7454667f855f7fec08e0dfaf5a5490dfafbb7abbd2cfb"}, + {file = "asyncpg-0.29.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d84156d5fb530b06c493f9e7635aa18f518fa1d1395ef240d211cb563c4e2364"}, + {file = "asyncpg-0.29.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54858bc25b49d1114178d65a88e48ad50cb2b6f3e475caa0f0c092d5f527c106"}, + {file = "asyncpg-0.29.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bde17a1861cf10d5afce80a36fca736a86769ab3579532c03e45f83ba8a09c59"}, + {file = "asyncpg-0.29.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:37a2ec1b9ff88d8773d3eb6d3784dc7e3fee7756a5317b67f923172a4748a175"}, + {file = "asyncpg-0.29.0-cp312-cp312-win32.whl", hash = "sha256:bb1292d9fad43112a85e98ecdc2e051602bce97c199920586be83254d9dafc02"}, + {file = "asyncpg-0.29.0-cp312-cp312-win_amd64.whl", hash = "sha256:2245be8ec5047a605e0b454c894e54bf2ec787ac04b1cb7e0d3c67aa1e32f0fe"}, + {file = "asyncpg-0.29.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0009a300cae37b8c525e5b449233d59cd9868fd35431abc470a3e364d2b85cb9"}, + {file = "asyncpg-0.29.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5cad1324dbb33f3ca0cd2074d5114354ed3be2b94d48ddfd88af75ebda7c43cc"}, + {file = "asyncpg-0.29.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:012d01df61e009015944ac7543d6ee30c2dc1eb2f6b10b62a3f598beb6531548"}, + {file = "asyncpg-0.29.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:000c996c53c04770798053e1730d34e30cb645ad95a63265aec82da9093d88e7"}, + {file = "asyncpg-0.29.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e0bfe9c4d3429706cf70d3249089de14d6a01192d617e9093a8e941fea8ee775"}, + {file = "asyncpg-0.29.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:642a36eb41b6313ffa328e8a5c5c2b5bea6ee138546c9c3cf1bffaad8ee36dd9"}, + {file = "asyncpg-0.29.0-cp38-cp38-win32.whl", hash = "sha256:a921372bbd0aa3a5822dd0409da61b4cd50df89ae85150149f8c119f23e8c408"}, + {file = "asyncpg-0.29.0-cp38-cp38-win_amd64.whl", hash = "sha256:103aad2b92d1506700cbf51cd8bb5441e7e72e87a7b3a2ca4e32c840f051a6a3"}, + {file = "asyncpg-0.29.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5340dd515d7e52f4c11ada32171d87c05570479dc01dc66d03ee3e150fb695da"}, + {file = "asyncpg-0.29.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e17b52c6cf83e170d3d865571ba574577ab8e533e7361a2b8ce6157d02c665d3"}, + {file = "asyncpg-0.29.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f100d23f273555f4b19b74a96840aa27b85e99ba4b1f18d4ebff0734e78dc090"}, + {file = "asyncpg-0.29.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48e7c58b516057126b363cec8ca02b804644fd012ef8e6c7e23386b7d5e6ce83"}, + {file = "asyncpg-0.29.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f9ea3f24eb4c49a615573724d88a48bd1b7821c890c2effe04f05382ed9e8810"}, + {file = "asyncpg-0.29.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8d36c7f14a22ec9e928f15f92a48207546ffe68bc412f3be718eedccdf10dc5c"}, + {file = "asyncpg-0.29.0-cp39-cp39-win32.whl", hash = "sha256:797ab8123ebaed304a1fad4d7576d5376c3a006a4100380fb9d517f0b59c1ab2"}, + {file = "asyncpg-0.29.0-cp39-cp39-win_amd64.whl", hash = "sha256:cce08a178858b426ae1aa8409b5cc171def45d4293626e7aa6510696d46decd8"}, + {file = "asyncpg-0.29.0.tar.gz", hash = "sha256:d1c49e1f44fffafd9a55e1a9b101590859d881d639ea2922516f5d9c512d354e"}, +] + +[package.dependencies] +async-timeout = {version = ">=4.0.3", markers = "python_version < \"3.12.0\""} + +[package.extras] +docs = ["Sphinx (>=5.3.0,<5.4.0)", "sphinx-rtd-theme (>=1.2.2)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] +test = ["flake8 (>=6.1,<7.0)", "uvloop (>=0.15.3)"] + [[package]] name = "black" version = "24.10.0" @@ -2137,4 +2187,4 @@ propcache = ">=0.2.0" [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "39fb595b0f3024b4b1fbd595bca5cf66bbf31310d47aa91652e3e95fac8bcc91" +content-hash = "f68eb5dd33fb05ad0d9f79fa3ca9ad633ecf7afe54f6e2f7216e90732ffc8663" diff --git a/fastapp/pyproject.toml b/fastapp/pyproject.toml index 5bd2882..70513fa 100644 --- a/fastapp/pyproject.toml +++ b/fastapp/pyproject.toml @@ -20,7 +20,7 @@ pydantic-settings = "^2" yarl = "^1" ujson = "^5.10.0" SQLAlchemy = {version = "^2.0.31", extras = ["asyncio"]} -aiosqlite = "^0.20.0" +asyncpg = {version = "^0.29.0", extras = ["sa"]} httptools = "^0.6.1" pymongo = "^4.8.0" @@ -62,7 +62,7 @@ filterwarnings = [ ] env = [ "FASTAPP_ENVIRONMENT=pytest", - "FASTAPP_DB_FILE=test_db.sqlite3", + "FASTAPP_DB_BASE=fastapp_test", ] [tool.ruff] @@ -145,7 +145,7 @@ enable_routers = "True" enable_kafka = "None" enable_loguru = "None" traefik_labels = "None" -add_dummy = "None" +add_dummy = "True" orm = "sqlalchemy" self_hosted_swagger = "None" prometheus_enabled = "None" diff --git a/fastapp/tests/test_dummy.py b/fastapp/tests/test_dummy.py new file mode 100644 index 0000000..c7644ab --- /dev/null +++ b/fastapp/tests/test_dummy.py @@ -0,0 +1,45 @@ +import uuid + +import pytest +from fastapi import FastAPI +from httpx import AsyncClient +from sqlalchemy.ext.asyncio import AsyncSession +from starlette import status + +from fastapp.db.dao.dummy_dao import DummyDAO + + +@pytest.mark.anyio +async def test_creation( + fastapi_app: FastAPI, + client: AsyncClient, + dbsession: AsyncSession, +) -> None: + """Tests dummy instance creation.""" + url = fastapi_app.url_path_for("create_dummy_model") + test_name = uuid.uuid4().hex + response = await client.put(url, json={"name": test_name}) + assert response.status_code == status.HTTP_200_OK + dao = DummyDAO(dbsession) + + instances = await dao.filter(name=test_name) + assert instances[0].name == test_name + + +@pytest.mark.anyio +async def test_getting( + fastapi_app: FastAPI, + client: AsyncClient, + dbsession: AsyncSession, +) -> None: + """Tests dummy instance retrieval.""" + dao = DummyDAO(dbsession) + test_name = uuid.uuid4().hex + await dao.create_dummy_model(name=test_name) + url = fastapi_app.url_path_for("get_dummy_models") + response = await client.get(url) + dummies = response.json() + + assert response.status_code == status.HTTP_200_OK + assert len(dummies) == 1 + assert dummies[0]["name"] == test_name