for AsyncEngine created in function scope, close and clean-up pooled connections adding await engine.dispose()

This commit is contained in:
kuba 2022-02-02 12:55:26 +01:00
parent f505a1eb05
commit 9b34191069
7 changed files with 29 additions and 38 deletions

View File

@ -10,9 +10,9 @@ build: ## Build project with compose
up: ## Run project with compose up: ## Run project with compose
docker-compose up --remove-orphans docker-compose up --remove-orphans
.PHONY: down .PHONY: clean
down: ## Reset project containers with compose clean: ## Clean Reset project containers with compose
docker-compose down docker-compose down -v --remove-orphans
.PHONY: lock .PHONY: lock
lock: ## Refresh pipfile.lock lock: ## Refresh pipfile.lock
@ -24,7 +24,7 @@ requirements: ## Refresh requirements.txt from pipfile.lock
.PHONY: test .PHONY: test
test: ## Run project tests test: ## Run project tests
docker-compose run --rm app pytest docker-compose -f docker-compose.yml -f docker-compose.test.yml run --rm app pytest
.PHONY: safety .PHONY: safety
safety: ## Check project and dependencies with safety https://github.com/pyupio/safety safety: ## Check project and dependencies with safety https://github.com/pyupio/safety

9
Pipfile.lock generated
View File

@ -1,7 +1,7 @@
{ {
"_meta": { "_meta": {
"hash": { "hash": {
"sha256": "628f478121edd35aa12ccc88be545920a333b3a9b3f427de1feb55d26f2bffc7" "sha256": "285d79d1ea6cc1173f0db1f9369ab590f6287d79c63c50e0cc086f52ef8289f9"
}, },
"pipfile-spec": 6, "pipfile-spec": 6,
"requires": { "requires": {
@ -637,6 +637,9 @@
"version": "==1.2.0" "version": "==1.2.0"
}, },
"sqlalchemy": { "sqlalchemy": {
"extras": [
"asyncio"
],
"hashes": [ "hashes": [
"sha256:05fa14f279d43df68964ad066f653193187909950aa0163320b728edfc400167", "sha256:05fa14f279d43df68964ad066f653193187909950aa0163320b728edfc400167",
"sha256:0ddc5e5ccc0160e7ad190e5c61eb57560f38559e22586955f205e537cda26034", "sha256:0ddc5e5ccc0160e7ad190e5c61eb57560f38559e22586955f205e537cda26034",
@ -889,7 +892,7 @@
"sha256:1fe43e3e9acf3a7c0f6b88f5338cad37044d2f156c43cb6b080b5f9da8a76f06", "sha256:1fe43e3e9acf3a7c0f6b88f5338cad37044d2f156c43cb6b080b5f9da8a76f06",
"sha256:20fa2a8ca2decac50116edb42e6af0a1253ef639ad79941249b840531889c65a" "sha256:20fa2a8ca2decac50116edb42e6af0a1253ef639ad79941249b840531889c65a"
], ],
"markers": "python_version >= '3.6' and python_version < '4.0'", "markers": "python_version >= '3.6' and python_version < '4'",
"version": "==1.3.2" "version": "==1.3.2"
}, },
"icecream": { "icecream": {
@ -1260,7 +1263,7 @@
"sha256:000ca7f471a233c2251c6c7023ee85305721bfdf18621ebff4fd17a8653427ed", "sha256:000ca7f471a233c2251c6c7023ee85305721bfdf18621ebff4fd17a8653427ed",
"sha256:0e7c33d9a63e7ddfcb86780aac87befc2fbddf46c58dbb487e0855f7ceec283c" "sha256:0e7c33d9a63e7ddfcb86780aac87befc2fbddf46c58dbb487e0855f7ceec283c"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4.0'", "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
"version": "==1.26.8" "version": "==1.26.8"
}, },
"wcwidth": { "wcwidth": {

11
docker-compose.test.yml Normal file
View File

@ -0,0 +1,11 @@
version: '3'
services:
app:
environment:
- SQL_DB=testdb
db:
environment:
- POSTGRES_USER=${SQL_USER}
- SQL_DB=testdb

View File

@ -1,4 +1,5 @@
[pytest] [pytest]
asyncio_mode=strict
python_files = tests.py test_*.py *_tests.py python_files = tests.py test_*.py *_tests.py
addopts = --cov=. addopts = --cov=.
--cov-report html:htmlcov --cov-report html:htmlcov

View File

@ -1,14 +1,8 @@
import pytest import pytest
from httpx import AsyncClient from httpx import AsyncClient
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.pool import NullPool
from the_app import config
from the_app.database import get_db
from the_app.main import app from the_app.main import app
from the_app.models.base import Base from the_app.models.base import Base
from the_app.database import engine
@pytest.fixture( @pytest.fixture(
@ -20,32 +14,13 @@ def anyio_backend(request):
return request.param return request.param
global_settings = config.get_settings()
url = global_settings.asyncpg_test_url
engine = create_async_engine(url, poolclass=NullPool, future=True)
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 def start_db():
async with engine.begin() as conn: async with engine.begin() as conn:
await conn.run_sync(Base.metadata.drop_all) await conn.run_sync(Base.metadata.drop_all)
await conn.run_sync(Base.metadata.create_all) await conn.run_sync(Base.metadata.create_all)
# for AsyncEngine created in function scope, close and
# clean-up pooled connections
await engine.dispose()
@pytest.fixture @pytest.fixture

View File

@ -31,9 +31,7 @@ class Settings(BaseSettings):
pg_pass: str = os.getenv("POSTGRES_PASSWORD", "") pg_pass: str = os.getenv("POSTGRES_PASSWORD", "")
pg_host: str = os.getenv("SQL_HOST", "") pg_host: str = os.getenv("SQL_HOST", "")
pg_database: str = os.getenv("SQL_DB", "") 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_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}"
jwt_secret_key: str = os.getenv("SECRET_KEY", "") jwt_secret_key: str = os.getenv("SECRET_KEY", "")
jwt_algorithm: str = os.getenv("ALGORITHM", "") jwt_algorithm: str = os.getenv("ALGORITHM", "")

View File

@ -31,3 +31,6 @@ async def get_db() -> AsyncGenerator:
raise ex raise ex
finally: finally:
await session.close() await session.close()
# for AsyncEngine created in function scope, close and
# clean-up pooled connections
await engine.dispose()