ShiErFeng/shierfeng-fastapi-backend/tests/cli/runtime/test_db.py
2026-07-24 09:43:10 +08:00

145 lines
3.8 KiB
Python

from pathlib import Path
import pytest
from cli.runtime.base import RuntimeEnvironmentService
from cli.runtime.db import DatabaseRuntimeService
from cli.runtime.db.gateway import DatabaseInfrastructureGateway
from cli.runtime.db.support import DatabaseRevisionSupport
class FakeRuntimeEnvironment(RuntimeEnvironmentService):
"""
模拟运行时环境服务。
"""
@staticmethod
def get_backend_dir() -> str:
"""
返回固定后端目录。
:return: 固定目录
"""
return '/tmp/shierfeng-backend'
def test_database_revision_support_serializes_revision() -> None:
"""
校验数据库迁移版本支持对象会序列化 Alembic revision。
:return: None
"""
class FakeRevision:
"""
模拟 Alembic revision 对象。
"""
revision = '202605110001'
down_revision = ('202605100001',)
branch_labels = {'main'}
dependencies = None
doc = ' demo revision '
path = Path('/tmp/revision.py')
support = DatabaseRevisionSupport(DatabaseInfrastructureGateway(), FakeRuntimeEnvironment())
payload = support.serialize_revision(FakeRevision())
assert payload == {
'revision': '202605110001',
'downRevisions': ['202605100001'],
'branchLabels': ['main'],
'dependsOn': [],
'doc': 'demo revision',
'path': '/tmp/revision.py',
}
def test_database_runtime_service_upgrade_dry_run_returns_command_payload() -> None:
"""
校验数据库运行时 dry-run 升级会返回命令预览结果。
:return: None
"""
service = DatabaseRuntimeService(runtime_environment=FakeRuntimeEnvironment())
payload = service.upgrade_database('head', dry_run=True)
assert payload['ok'] is True
assert payload['dryRun'] is True
assert payload['cwd'] == '/tmp/shierfeng-backend'
assert payload['command'] == ['alembic', '-c', '/tmp/shierfeng-backend/alembic.ini', 'upgrade', 'head']
@pytest.mark.asyncio
async def test_database_runtime_service_ping_database_returns_failure() -> None:
"""
校验数据库运行时在连接异常时会返回失败结果。
:return: None
"""
class FakeConnection:
"""
模拟数据库连接对象。
"""
async def __aenter__(self) -> 'FakeConnection':
raise RuntimeError('db boom')
async def __aexit__(
self,
exc_type: type[BaseException] | None,
exc: BaseException | None,
tb: object,
) -> None:
del exc_type, exc, tb
class FakeEngine:
"""
模拟数据库引擎。
"""
@staticmethod
def connect() -> FakeConnection:
"""
返回模拟连接对象。
:return: 模拟连接对象
"""
return FakeConnection()
@staticmethod
async def dispose() -> None:
"""
释放引擎资源。
:return: None
"""
gateway = DatabaseInfrastructureGateway()
service = DatabaseRuntimeService(
runtime_environment=FakeRuntimeEnvironment(),
infrastructure_gateway=gateway,
)
def _fake_get_async_db_engine_factory() -> object:
def _factory(*, echo: bool = False) -> FakeEngine:
del echo
return FakeEngine()
return _factory
def _fake_get_sqlalchemy_text() -> object:
return lambda sql: sql
object.__setattr__(gateway, 'get_async_db_engine_factory', _fake_get_async_db_engine_factory)
object.__setattr__(gateway, 'get_sqlalchemy_text', _fake_get_sqlalchemy_text)
payload = await service.ping_database()
assert payload['ok'] is False
assert payload['message'] == '数据库连接失败'
assert 'db boom' in payload['error']