添加数据库加密处理的单元测试
This commit is contained in:
119
tests/unit/test_metadata_repository_dsn_decrypt.py
Normal file
119
tests/unit/test_metadata_repository_dsn_decrypt.py
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
import asyncio
|
||||||
|
from types import SimpleNamespace
|
||||||
|
from unittest.mock import AsyncMock
|
||||||
|
from uuid import uuid4
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from cryptography.fernet import InvalidToken
|
||||||
|
|
||||||
|
from app.infra.repositories.metadata_repository import MetadataRepository
|
||||||
|
|
||||||
|
|
||||||
|
class _DummyResult:
|
||||||
|
def __init__(self, record):
|
||||||
|
self._record = record
|
||||||
|
|
||||||
|
def scalar_one_or_none(self):
|
||||||
|
return self._record
|
||||||
|
|
||||||
|
|
||||||
|
class _DummyEncryptor:
|
||||||
|
def __init__(self, decrypted=None, raise_invalid_token=False):
|
||||||
|
self._decrypted = decrypted
|
||||||
|
self._raise_invalid_token = raise_invalid_token
|
||||||
|
self.encrypted_values = []
|
||||||
|
|
||||||
|
def decrypt(self, _value):
|
||||||
|
if self._raise_invalid_token:
|
||||||
|
raise InvalidToken()
|
||||||
|
return self._decrypted
|
||||||
|
|
||||||
|
def _build_record(dsn_encrypted: str):
|
||||||
|
return SimpleNamespace(
|
||||||
|
project_id=uuid4(),
|
||||||
|
db_role="biz_data",
|
||||||
|
db_type="postgresql",
|
||||||
|
dsn_encrypted=dsn_encrypted,
|
||||||
|
pool_min_size=1,
|
||||||
|
pool_max_size=5,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_invalid_token_with_plaintext_dsn_value_raises_clear_error(monkeypatch):
|
||||||
|
record = _build_record("postgresql://user:p@ss@localhost:5432/db")
|
||||||
|
session = SimpleNamespace(
|
||||||
|
execute=None,
|
||||||
|
commit=None,
|
||||||
|
)
|
||||||
|
session.execute = AsyncMock(return_value=_DummyResult(record))
|
||||||
|
session.commit = AsyncMock()
|
||||||
|
encryptor = _DummyEncryptor(raise_invalid_token=True)
|
||||||
|
repo = MetadataRepository(session)
|
||||||
|
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"app.infra.repositories.metadata_repository.is_database_encryption_configured",
|
||||||
|
lambda: True,
|
||||||
|
)
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"app.infra.repositories.metadata_repository.get_database_encryptor",
|
||||||
|
lambda: encryptor,
|
||||||
|
)
|
||||||
|
|
||||||
|
with pytest.raises(
|
||||||
|
ValueError,
|
||||||
|
match="DATABASE_ENCRYPTION_KEY mismatch or invalid dsn_encrypted value",
|
||||||
|
):
|
||||||
|
asyncio.run(repo.get_project_db_routing(record.project_id, "biz_data"))
|
||||||
|
session.commit.assert_not_awaited()
|
||||||
|
|
||||||
|
|
||||||
|
def test_invalid_token_with_non_dsn_value_raises_clear_error(monkeypatch):
|
||||||
|
record = _build_record("gAAAAABinvalidciphertext")
|
||||||
|
session = SimpleNamespace(
|
||||||
|
execute=None,
|
||||||
|
commit=None,
|
||||||
|
)
|
||||||
|
session.execute = AsyncMock(return_value=_DummyResult(record))
|
||||||
|
session.commit = AsyncMock()
|
||||||
|
repo = MetadataRepository(session)
|
||||||
|
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"app.infra.repositories.metadata_repository.is_database_encryption_configured",
|
||||||
|
lambda: True,
|
||||||
|
)
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"app.infra.repositories.metadata_repository.get_database_encryptor",
|
||||||
|
lambda: _DummyEncryptor(raise_invalid_token=True),
|
||||||
|
)
|
||||||
|
|
||||||
|
with pytest.raises(
|
||||||
|
ValueError,
|
||||||
|
match="DATABASE_ENCRYPTION_KEY mismatch or invalid dsn_encrypted value",
|
||||||
|
):
|
||||||
|
asyncio.run(repo.get_project_db_routing(record.project_id, "biz_data"))
|
||||||
|
session.commit.assert_not_awaited()
|
||||||
|
|
||||||
|
|
||||||
|
def test_encrypted_dsn_decrypts_without_migration(monkeypatch):
|
||||||
|
record = _build_record("encrypted-value")
|
||||||
|
session = SimpleNamespace(
|
||||||
|
execute=None,
|
||||||
|
commit=None,
|
||||||
|
)
|
||||||
|
session.execute = AsyncMock(return_value=_DummyResult(record))
|
||||||
|
session.commit = AsyncMock()
|
||||||
|
repo = MetadataRepository(session)
|
||||||
|
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"app.infra.repositories.metadata_repository.is_database_encryption_configured",
|
||||||
|
lambda: True,
|
||||||
|
)
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"app.infra.repositories.metadata_repository.get_database_encryptor",
|
||||||
|
lambda: _DummyEncryptor(decrypted="postgresql://u:p@ss@host/db"),
|
||||||
|
)
|
||||||
|
|
||||||
|
routing = asyncio.run(repo.get_project_db_routing(record.project_id, "biz_data"))
|
||||||
|
|
||||||
|
assert routing.dsn == "postgresql://u:p%40ss@host/db"
|
||||||
|
session.commit.assert_not_awaited()
|
||||||
Reference in New Issue
Block a user