From ead24d9f284cdd7e555663b854e016f1cedabfa8 Mon Sep 17 00:00:00 2001 From: pi3c Date: Tue, 30 Jan 2024 23:11:40 +0300 Subject: [PATCH] =?UTF-8?q?FIXDOCKER=20=D0=B8=20typehint=20=D0=B2=20=D1=82?= =?UTF-8?q?=D0=B5=D1=81=D1=82=D0=B0=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 14 +++++------- README.md | 6 ++++- compose_app.yml | 45 ++++++++++++++++++++++++++++++++++++++ compose_test.yml | 45 ++++++++++++++++++++++++++++++++++++++ docker-compose.yml | 44 ------------------------------------- example.env | 6 ++--- fastfood/config.py | 9 ++++---- scripts/db_prepare.sql | 1 - scripts/migrate_and_run.sh | 5 ----- scripts/testing.sh | 2 -- tests/conftest.py | 8 +++---- tests/test_api.py | 12 ++++++++-- tests/test_crud.py | 10 ++++++--- 13 files changed, 130 insertions(+), 77 deletions(-) create mode 100644 compose_app.yml create mode 100644 compose_test.yml delete mode 100644 docker-compose.yml delete mode 100644 scripts/db_prepare.sql delete mode 100644 scripts/migrate_and_run.sh delete mode 100644 scripts/testing.sh diff --git a/Dockerfile b/Dockerfile index dd0953e..634d067 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,15 +1,13 @@ FROM python:3.10-slim -RUN mkdir /fastfood - -WORKDIR /fastfood - -COPY . . - RUN pip install poetry RUN poetry config virtualenvs.create false -RUN poetry install +RUN mkdir -p /usr/src/fastfood -RUN chmod a+x scripts/*.sh +WORKDIR /usr/src/fastfood + +COPY . . + +RUN poetry install diff --git a/README.md b/README.md index 82f5913..23f342d 100644 --- a/README.md +++ b/README.md @@ -98,7 +98,11 @@ Fastapi веб приложение реализующее api для общеп где и соответвтовали POSTGRES_DB и POSTGRES_USER в файле `.env` Создайте и запустите образы -> `$ docker-compose up -d --build` +Запуск FAstAPI приложения +> `$ docker-compose -f compose_app.yml up -d` + +Запуск тестов +> `$ docker-compose -f compose_test.yml up` После успешного запуска образов документация по API будет доступна по адресу http://localhost:8000 diff --git a/compose_app.yml b/compose_app.yml new file mode 100644 index 0000000..2028dc8 --- /dev/null +++ b/compose_app.yml @@ -0,0 +1,45 @@ +version: "3.8" +services: + db: + container_name: pgdb + + image: postgres:15.1-alpine + + env_file: + - .env + + environment: + POSTGRES_DB: ${POSTGRES_DB} + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + + ports: + - 6432:5432 + + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"] + interval: 10s + timeout: 5s + retries: 5 + + + app: + container_name: fastfood_app + + build: + context: . + + env_file: + - .env + + ports: + - 8000:8000 + + depends_on: + db: + condition: service_healthy + + restart: always + + command: /bin/bash -c 'poetry run python /usr/src/fastfood/manage.py --run-test-server' + diff --git a/compose_test.yml b/compose_test.yml new file mode 100644 index 0000000..bd471be --- /dev/null +++ b/compose_test.yml @@ -0,0 +1,45 @@ +version: "3.8" +services: + db: + container_name: pgdb_test + + image: postgres:15.1-alpine + + env_file: + - .env + + environment: + POSTGRES_DB: "${POSTGRES_DB}_test" + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + + ports: + - 6432:5432 + + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}_test"] + interval: 10s + timeout: 5s + retries: 5 + + + app: + container_name: fastfood_app_test + + build: + context: . + + env_file: + - .env + + ports: + - 8000:8000 + + depends_on: + db: + condition: service_healthy + + restart: always + + command: /bin/bash -c 'poetry run pytest -vv' + diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 2e8e8e2..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,44 +0,0 @@ -version: "3.8" -services: - - db: - image: postgres:15.1-alpine - env_file: - - .env - container_name: pgdatabase - ports: - - 6432:5432 - healthcheck: - test: ["CMD-SHELL", "pg_isready -U postgres -d postgres"] - interval: 10s - timeout: 5s - retries: 5 - volumes: - - ./scripts/db_prepare.sql:/docker-entrypoint-initdb.d/db_prepare.sql - - app: - build: - context: . - container_name: fastfood_app - env_file: - - .env - command: ["/fastfood/scripts/migrate_and_run.sh"] - ports: - - 8000:8000 - depends_on: - db: - condition: service_healthy - restart: always - - tests: - build: - context: . - container_name: tests - env_file: - - .env - depends_on: - db: - condition: service_healthy - app: - condition: service_started - command: ["/fastfood/scripts/testing.sh"] diff --git a/example.env b/example.env index 918c6ed..0e3b1ff 100644 --- a/example.env +++ b/example.env @@ -1,5 +1,5 @@ DB_HOST=db DB_PORT=5432 -POSTGRES_USER=postges -POSTGRES_PASSWORD=postgres -POSTGRES_DB=fastfood_db \ No newline at end of file +POSTGRES_USER=testuser +POSTGRES_PASSWORD=test +POSTGRES_DB=fastfood_db diff --git a/fastfood/config.py b/fastfood/config.py index 208f480..8f24f89 100644 --- a/fastfood/config.py +++ b/fastfood/config.py @@ -4,9 +4,10 @@ from pydantic_settings import BaseSettings, SettingsConfigDict class Settings(BaseSettings): DB_HOST: str = "db" DB_PORT: int = 5432 - POSTGRES_DB: str = "fastfod_db" - POSTGRES_PASSWORD: str = "postgres" - POSTGRES_USER: str = "postgres" + POSTGRES_DB: str = "fastfood_db" + POSTGRES_PASSWORD: str = "test" + POSTGRES_USER: str = "testuser" + POSTGRES_DB_TEST: str = "fastfood_db_test" @property def DATABASE_URL_asyncpg(self): @@ -27,7 +28,7 @@ class Settings(BaseSettings): return ( "postgresql+asyncpg://" f"{self.POSTGRES_USER}:{self.POSTGRES_PASSWORD}" - f"@{self.DB_HOST}:{self.DB_PORT}/{self.POSTGRES_DB}_test" + f"@{self.DB_HOST}:{self.DB_PORT}/{self.POSTGRES_DB_TEST}" ) model_config = SettingsConfigDict(env_file=".env") diff --git a/scripts/db_prepare.sql b/scripts/db_prepare.sql deleted file mode 100644 index fa300f9..0000000 --- a/scripts/db_prepare.sql +++ /dev/null @@ -1 +0,0 @@ -CREATE DATABASE fastfood_db_test WITH OWNER postgres; diff --git a/scripts/migrate_and_run.sh b/scripts/migrate_and_run.sh deleted file mode 100644 index c882983..0000000 --- a/scripts/migrate_and_run.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash -# -# Тут можно выполнить миграции или дополнительные перед запуском приложения -# -poetry run python manage.py --run-test-server diff --git a/scripts/testing.sh b/scripts/testing.sh deleted file mode 100644 index d075b46..0000000 --- a/scripts/testing.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -poetry run pytest -vv diff --git a/tests/conftest.py b/tests/conftest.py index c867b4c..18a4d88 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -21,7 +21,7 @@ async_session_maker = async_sessionmaker( ) -@pytest.fixture(scope="session") +@pytest.fixture(scope="session", autouse=True) def event_loop(): try: loop = asyncio.get_event_loop() @@ -32,7 +32,7 @@ def event_loop(): @pytest_asyncio.fixture(scope="function", autouse=True) -async def db_init(): +async def db_init(event_loop): async with async_engine.begin() as conn: await conn.run_sync(Base.metadata.drop_all) await conn.run_sync(Base.metadata.create_all) @@ -47,7 +47,7 @@ async def get_test_session() -> AsyncGenerator[AsyncSession, None]: @pytest.fixture(scope="session") -def app() -> Generator[FastAPI, None, None]: +def app(event_loop) -> Generator[FastAPI, None, None]: app: FastAPI = create_app() app.dependency_overrides[get_async_session] = get_test_session yield app @@ -63,6 +63,6 @@ async def client(app) -> AsyncGenerator[AsyncClient, None]: @pytest_asyncio.fixture(scope="function") -async def asession() -> AsyncGenerator[AsyncSession, None]: +async def asession(event_loop) -> AsyncGenerator[AsyncSession, None]: async with async_session_maker() as session: yield session diff --git a/tests/test_api.py b/tests/test_api.py index aefc633..a627f10 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -47,7 +47,11 @@ class TestBaseCrud: return response.status_code, response.json() @staticmethod - async def get(ac: AsyncClient, menu: dict, submenu: dict) -> Tuple[int, dict]: + async def get( + ac: AsyncClient, + menu: dict, + submenu: dict, + ) -> Tuple[int, dict]: """Получение меню по id""" response: Response = await ac.get( f"/{menu.get('id')}/submenus/{submenu.get('id')}", @@ -55,7 +59,11 @@ class TestBaseCrud: return response.status_code, response.json() @staticmethod - async def write(ac: AsyncClient, menu: dict, submenu: dict) -> Tuple[int, dict]: + async def write( + ac: AsyncClient, + menu: dict, + submenu: dict, + ) -> Tuple[int, dict]: """создания меню""" response: Response = await ac.post( f"/{menu.get('id')}/submenus/", diff --git a/tests/test_crud.py b/tests/test_crud.py index 60a8f04..b4bc383 100644 --- a/tests/test_crud.py +++ b/tests/test_crud.py @@ -48,7 +48,7 @@ async def test_menu(asession: AsyncSession) -> None: async def test_submenu(asession: AsyncSession) -> None: async with asession: # Создаем меню напрямую - menu: Menu = Menu(title="SomeMenu", description="SomeDescription") + menu = Menu(title="SomeMenu", description="SomeDescription") asession.add(menu) await asession.commit() await asession.refresh(menu) @@ -69,14 +69,18 @@ async def test_submenu(asession: AsyncSession) -> None: # Проверяем подменю req_submenu = await SubMenuCrud.get_submenu_item( - menu_id, submenu.id, asession, + menu_id, + submenu.id, + asession, ) assert submenu == req_submenu # Обновляем меню submenu.title = "UpdatedSubmenu" req_submenu = await SubMenuCrud.update_submenu_item( - submenu_id, menubaseschema.model_validate(submenu), asession, + submenu_id, + menubaseschema.model_validate(submenu), + asession, ) assert submenu == req_submenu.scalar_one_or_none()