Add unit test suite with pytest configuration
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
91
tests/unit/test_crypto_service.py
Normal file
91
tests/unit/test_crypto_service.py
Normal file
@@ -0,0 +1,91 @@
|
||||
"""Tests for encryption/decryption in blueprints/email/crypto_service.py."""
|
||||
|
||||
import os
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from cryptography.fernet import Fernet
|
||||
|
||||
|
||||
# Generate a valid key for testing
|
||||
TEST_FERNET_KEY = Fernet.generate_key().decode()
|
||||
|
||||
|
||||
class TestEncryptedTextField:
|
||||
@pytest.fixture
|
||||
def field(self):
|
||||
with patch.dict(os.environ, {"FERNET_KEY": TEST_FERNET_KEY}):
|
||||
from blueprints.email.crypto_service import EncryptedTextField
|
||||
|
||||
return EncryptedTextField()
|
||||
|
||||
def test_encrypt_decrypt_roundtrip(self, field):
|
||||
original = "my secret password"
|
||||
encrypted = field.to_db_value(original, None)
|
||||
decrypted = field.to_python_value(encrypted)
|
||||
assert decrypted == original
|
||||
assert encrypted != original
|
||||
|
||||
def test_none_passthrough(self, field):
|
||||
assert field.to_db_value(None, None) is None
|
||||
assert field.to_python_value(None) is None
|
||||
|
||||
def test_unicode_roundtrip(self, field):
|
||||
original = "Hello 世界 🐱"
|
||||
encrypted = field.to_db_value(original, None)
|
||||
decrypted = field.to_python_value(encrypted)
|
||||
assert decrypted == original
|
||||
|
||||
def test_empty_string_roundtrip(self, field):
|
||||
encrypted = field.to_db_value("", None)
|
||||
decrypted = field.to_python_value(encrypted)
|
||||
assert decrypted == ""
|
||||
|
||||
def test_long_text_roundtrip(self, field):
|
||||
original = "x" * 10000
|
||||
encrypted = field.to_db_value(original, None)
|
||||
decrypted = field.to_python_value(encrypted)
|
||||
assert decrypted == original
|
||||
|
||||
def test_different_encryptions_differ(self, field):
|
||||
"""Fernet includes a timestamp, so two encryptions of the same value differ."""
|
||||
e1 = field.to_db_value("same", None)
|
||||
e2 = field.to_db_value("same", None)
|
||||
assert e1 != e2 # Different ciphertexts
|
||||
assert field.to_python_value(e1) == field.to_python_value(e2) == "same"
|
||||
|
||||
def test_wrong_key_fails(self, field):
|
||||
encrypted = field.to_db_value("secret", None)
|
||||
|
||||
# Create a field with a different key
|
||||
other_key = Fernet.generate_key().decode()
|
||||
with patch.dict(os.environ, {"FERNET_KEY": other_key}):
|
||||
from blueprints.email.crypto_service import EncryptedTextField
|
||||
|
||||
other_field = EncryptedTextField()
|
||||
|
||||
with pytest.raises(Exception):
|
||||
other_field.to_python_value(encrypted)
|
||||
|
||||
|
||||
class TestValidateFernetKey:
|
||||
def test_valid_key(self):
|
||||
with patch.dict(os.environ, {"FERNET_KEY": TEST_FERNET_KEY}):
|
||||
from blueprints.email.crypto_service import validate_fernet_key
|
||||
|
||||
validate_fernet_key() # Should not raise
|
||||
|
||||
def test_missing_key(self):
|
||||
with patch.dict(os.environ, {}, clear=True):
|
||||
os.environ.pop("FERNET_KEY", None)
|
||||
from blueprints.email.crypto_service import validate_fernet_key
|
||||
|
||||
with pytest.raises(ValueError, match="not set"):
|
||||
validate_fernet_key()
|
||||
|
||||
def test_invalid_key(self):
|
||||
with patch.dict(os.environ, {"FERNET_KEY": "not-a-valid-key"}):
|
||||
from blueprints.email.crypto_service import validate_fernet_key
|
||||
|
||||
with pytest.raises(ValueError, match="validation failed"):
|
||||
validate_fernet_key()
|
||||
Reference in New Issue
Block a user