145 lines
3.8 KiB
Python
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']
|