test: CREATE/DROP DATABASE через служебную БД postgres (M-9)
Команды уровня кластера в тестах (создание и удаление временных БД, роль в тесте предвыданной схемы) подключались к рабочей БД armka. Теперь — к служебной БД postgres того же сервера (service_url): тестам не нужна существующая armka. Проверено прогоном с несуществующей БД в OPENBAO__DATABASE__DATABASE_URL. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
87816ef6c9
commit
feaac9cb9a
@ -1,7 +1,9 @@
|
|||||||
"""Помощники интеграционных тестов с PostgreSQL: временная БД внутри того же сервера, что и `OPENBAO__DATABASE`.
|
"""Помощники интеграционных тестов с PostgreSQL: временная БД внутри того же сервера, что и `OPENBAO__DATABASE`.
|
||||||
|
|
||||||
Фикстура `scratch_db_url` создаёт пустую БД `armcore_scratch_<hex>`, отдаёт её SQLAlchemy-URL (драйвер asyncpg) и
|
Фикстура `scratch_db_url` создаёт пустую БД `armcore_scratch_<hex>`, отдаёт её SQLAlchemy-URL (драйвер asyncpg) и
|
||||||
в конце удаляет. Рабочую БД (`armka`) тесты не трогают. Подключается плагином в `conftest.py`.
|
в конце удаляет. Рабочую БД (`armka`) тесты не трогают и от её существования не зависят: `CREATE/DROP DATABASE` и
|
||||||
|
прочие команды уровня кластера идут через служебную БД `postgres` того же сервера (`service_url`). Подключается
|
||||||
|
плагином в `conftest.py`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
@ -25,6 +27,15 @@ def base_url() -> URL:
|
|||||||
return make_url(settings.OPENBAO.DATABASE.DATABASE_URL)
|
return make_url(settings.OPENBAO.DATABASE.DATABASE_URL)
|
||||||
|
|
||||||
|
|
||||||
|
#: Служебная БД PostgreSQL, которая есть на любом сервере: к ней подключаются для команд уровня кластера.
|
||||||
|
SERVICE_DATABASE = "postgres"
|
||||||
|
|
||||||
|
|
||||||
|
def service_url() -> URL:
|
||||||
|
"""SQLAlchemy-URL служебной БД `postgres` на сервере из настроек (те же хост, порт и учётные данные)."""
|
||||||
|
return base_url().set(database=SERVICE_DATABASE)
|
||||||
|
|
||||||
|
|
||||||
def _asyncpg_dsn(url: URL) -> str:
|
def _asyncpg_dsn(url: URL) -> str:
|
||||||
return url.set(drivername="postgresql").render_as_string(hide_password=False)
|
return url.set(drivername="postgresql").render_as_string(hide_password=False)
|
||||||
|
|
||||||
@ -54,18 +65,17 @@ async def alembic_async(url: str, *args: str) -> str:
|
|||||||
|
|
||||||
async def create_scratch_db(name: str) -> URL:
|
async def create_scratch_db(name: str) -> URL:
|
||||||
"""Создать пустую БД `name` на сервере рабочей БД; вернуть SQLAlchemy-URL на неё."""
|
"""Создать пустую БД `name` на сервере рабочей БД; вернуть SQLAlchemy-URL на неё."""
|
||||||
url = base_url()
|
conn = await asyncpg.connect(_asyncpg_dsn(service_url()))
|
||||||
conn = await asyncpg.connect(_asyncpg_dsn(url))
|
|
||||||
try:
|
try:
|
||||||
await conn.execute(f'CREATE DATABASE "{name}"')
|
await conn.execute(f'CREATE DATABASE "{name}"')
|
||||||
finally:
|
finally:
|
||||||
await conn.close()
|
await conn.close()
|
||||||
return url.set(database=name)
|
return base_url().set(database=name)
|
||||||
|
|
||||||
|
|
||||||
async def drop_scratch_db(name: str) -> None:
|
async def drop_scratch_db(name: str) -> None:
|
||||||
"""Удалить БД `name` (закрывая оставшиеся подключения)."""
|
"""Удалить БД `name` (закрывая оставшиеся подключения)."""
|
||||||
conn = await asyncpg.connect(_asyncpg_dsn(base_url()))
|
conn = await asyncpg.connect(_asyncpg_dsn(service_url()))
|
||||||
try:
|
try:
|
||||||
await conn.execute(f'DROP DATABASE IF EXISTS "{name}" WITH (FORCE)')
|
await conn.execute(f'DROP DATABASE IF EXISTS "{name}" WITH (FORCE)')
|
||||||
finally:
|
finally:
|
||||||
|
|||||||
@ -18,7 +18,7 @@ from sqlalchemy.ext.asyncio import AsyncConnection, AsyncEngine, AsyncSession, c
|
|||||||
from src.models import Base
|
from src.models import Base
|
||||||
from src.models.enums import ENUMS
|
from src.models.enums import ENUMS
|
||||||
from tests.pg_helpers import alembic_async as alembic
|
from tests.pg_helpers import alembic_async as alembic
|
||||||
from tests.pg_helpers import base_url, create_scratch_db, drop_scratch_db, run_alembic
|
from tests.pg_helpers import base_url, create_scratch_db, drop_scratch_db, run_alembic, service_url
|
||||||
from tests.test_models_part_01_03 import _check_related_group_member_uniqueness
|
from tests.test_models_part_01_03 import _check_related_group_member_uniqueness
|
||||||
from tests.test_models_part_04_06 import _check_nulls_not_distinct, _make_file_with_job
|
from tests.test_models_part_04_06 import _check_nulls_not_distinct, _make_file_with_job
|
||||||
from tests.test_models_part_07_09 import (
|
from tests.test_models_part_07_09 import (
|
||||||
@ -263,7 +263,7 @@ async def test_upgrade_into_preprovisioned_schema_without_database_create(scratc
|
|||||||
password = uuid.uuid4().hex
|
password = uuid.uuid4().hex
|
||||||
scratch_url = make_url(scratch_db_url)
|
scratch_url = make_url(scratch_db_url)
|
||||||
role_url = scratch_url.set(username=role, password=password).render_as_string(hide_password=False)
|
role_url = scratch_url.set(username=role, password=password).render_as_string(hide_password=False)
|
||||||
server = create_async_engine(base_url(), isolation_level="AUTOCOMMIT")
|
server = create_async_engine(service_url(), isolation_level="AUTOCOMMIT")
|
||||||
scratch = create_async_engine(scratch_db_url, isolation_level="AUTOCOMMIT")
|
scratch = create_async_engine(scratch_db_url, isolation_level="AUTOCOMMIT")
|
||||||
role_created = False
|
role_created = False
|
||||||
try:
|
try:
|
||||||
@ -308,3 +308,10 @@ def test_offline_sql_creates_schema_before_version_table() -> None:
|
|||||||
assert schema_at < output.index("CREATE TABLE dkb.alembic_version")
|
assert schema_at < output.index("CREATE TABLE dkb.alembic_version")
|
||||||
assert schema_at < output.index("CREATE TYPE dkb.entity_type AS ENUM ('ЮЛ', 'ИП')")
|
assert schema_at < output.index("CREATE TYPE dkb.entity_type AS ENUM ('ЮЛ', 'ИП')")
|
||||||
assert f"INSERT INTO dkb.alembic_version (version_num) VALUES ('{HEAD}')" in output
|
assert f"INSERT INTO dkb.alembic_version (version_num) VALUES ('{HEAD}')" in output
|
||||||
|
|
||||||
|
|
||||||
|
def test_cluster_commands_use_service_database() -> None:
|
||||||
|
"""CREATE/DROP DATABASE и роли — через служебную БД `postgres`, а не рабочую `armka` (финальное ревью M-9)."""
|
||||||
|
assert service_url().database == "postgres"
|
||||||
|
base = base_url()
|
||||||
|
assert (service_url().host, service_url().port, service_url().username) == (base.host, base.port, base.username)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user