diff --git a/.env b/.env index bf639e9..8fc775b 100644 --- a/.env +++ b/.env @@ -1,8 +1,9 @@ PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1 -POSTGRES_DB=devdb -POSTGRES_TEST_DB=testdb -POSTGRES_HOST=db -POSTGRES_USER=user +SQL_DB=devdb +SQL_TEST_DB=testdb +SQL_HOST=db +SQL_USER=user +SQL_PASS=secret diff --git a/.secrets b/.secrets deleted file mode 100644 index d06b563..0000000 --- a/.secrets +++ /dev/null @@ -1 +0,0 @@ -POSTGRES_PASS=secret \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 23087f6..36df9be 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,7 +20,6 @@ WORKDIR /app COPY ./the_app/ /app/ COPY ./tests/ /app/ COPY .env /app/ -COPY .secrets /app/ RUN set -ex && bash -c "eval $(grep 'PYTHONDONTWRITEBYTECODE' .env)" RUN set -ex && bash -c "eval $(grep 'PYTHONUNBUFFERED' .env)" diff --git a/Makefile b/Makefile index 3c5b5b1..3a3c982 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ build: ## Build project with compose .PHONY: up up: ## Run project with compose - docker-compose up + docker-compose up --remove-orphans .PHONY: down down: ## Reset project containers with compose @@ -24,7 +24,7 @@ requirements: ## Refresh requirements.txt from pipfile.lock .PHONY: test test: ## Run project tests - docker-compose run --rm web pytest + docker-compose run --rm app pytest .PHONY: lint lint: ## Linter project code. diff --git a/db/create.sql b/db/create.sql index 1376f64..2cd7a40 100644 --- a/db/create.sql +++ b/db/create.sql @@ -1,4 +1,4 @@ DROP DATABASE IF EXISTS devdb; CREATE DATABASE devdb; -DROP DATABASE IF EXISTS devdb; -CREATE DATABASE devdb; +DROP DATABASE IF EXISTS testdb; +CREATE DATABASE testdb; diff --git a/docker-compose.yml b/docker-compose.yml index 654f7c2..ffd2153 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,7 +4,6 @@ services: app: build: . env_file: - - .secrets - .env command: bash -c " uvicorn the_app.main:app @@ -17,19 +16,30 @@ services: ports: - 8080:8080 depends_on: - - db + db: + condition: service_healthy db: build: context: ./db dockerfile: Dockerfile + volumes: + - postgres_data:/var/lib/postgresql/data env_file: - - .secrets - .env ports: - 5432:5432 environment: - - POSTGRES_USER=${POSTGRES_USER} - - POSTGRES_PASSWORD=${POSTGRES_PASS} - - POSTGRES_DB=${POSTGRES_DB} - - POSTGRES_HOST_AUTH_METHOD=trust \ No newline at end of file + - POSTGRES_USER=${SQL_USER} + - POSTGRES_PASSWORD=${SQL_PASS} + healthcheck: + test: + [ + "CMD-SHELL", "pg_isready -d $POSTGRES_DB -U $POSTGRES_USER" + ] + interval: 5s + timeout: 5s + retries: 5 + +volumes: + postgres_data: \ No newline at end of file diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..0261ac6 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,5 @@ +[pytest] +python_files = tests.py test_*.py *_tests.py +addopts = --cov=. + --cov-report html:htmlcov + --cov-report term-missing diff --git a/tests/api/__init__.py b/tests/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/api/test_stuff.py b/tests/api/test_stuff.py new file mode 100644 index 0000000..0e703dd --- /dev/null +++ b/tests/api/test_stuff.py @@ -0,0 +1,10 @@ +import pytest +from fastapi import status +from httpx import AsyncClient + +# decorate all tests with @pytest.mark.asyncio +pytestmark = pytest.mark.asyncio + + +async def test_add_stuff(client: AsyncClient): + assert True diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..8682e7d --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,49 @@ +import pytest +from httpx import AsyncClient + +from sqlalchemy.ext.asyncio import AsyncSession +from sqlalchemy.ext.asyncio import create_async_engine +from sqlalchemy.orm import sessionmaker +from sqlalchemy.exc import SQLAlchemyError + +from the_app.main import app +from the_app import config +from the_app.database import get_db, engine +from the_app.models.base import Base + +global_settings = config.get_settings() +url = global_settings.asyncpg_test_url +engine = create_async_engine(url) + +async_session = sessionmaker(engine, expire_on_commit=False, class_=AsyncSession) + + +async def get_test_db(): + session = async_session() + try: + yield session + await session.commit() + except SQLAlchemyError as ex: + await session.rollback() + raise ex + finally: + await session.close() + +app.dependency_overrides[get_db] = get_test_db + + +async def start_db(): + async with engine.begin() as conn: + await conn.run_sync(Base.metadata.drop_all) + await conn.run_sync(Base.metadata.create_all) + + +@pytest.fixture +async def client() -> AsyncClient: + async with AsyncClient( + app=app, + base_url="http://testserver", + headers={"Content-Type": "application/json"}, + ) as client: + await start_db() + yield client diff --git a/the_app/config.py b/the_app/config.py index 7d9d342..466cfed 100644 --- a/the_app/config.py +++ b/the_app/config.py @@ -26,11 +26,11 @@ class Settings(BaseSettings): """ - pg_user: str = os.getenv("POSTGRES_USER", "") - pg_pass: str = os.getenv("POSTGRES_PASS", "") - pg_host: str = os.getenv("POSTGRES_HOST", "") - pg_database: str = os.getenv("POSTGRES_DB", "") - pg_test_database: str = os.getenv("POSTGRES_TEST_DB", "") + pg_user: str = os.getenv("SQL_USER", "") + pg_pass: str = os.getenv("SQL_PASS", "") + pg_host: str = os.getenv("SQL_HOST", "") + pg_database: str = os.getenv("SQL_DB", "") + pg_test_database: str = os.getenv("SQL_TEST_DB", "") asyncpg_url: str = f"postgresql+asyncpg://{pg_user}:{pg_pass}@{pg_host}:5432/{pg_database}" asyncpg_test_url: str = f"postgresql+asyncpg://{pg_user}:{pg_pass}@{pg_host}:5432/{pg_test_database}"