Files
TJWaterServerBinary/tests/unit/test_metadata_repository_dsn_decrypt.py

120 lines
3.6 KiB
Python

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()