Files
gh-christianlouis-inboxconv…/backend/tests/unit/test_security_extended.py
copilot-swe-agent[bot] d66af4d8ef feat: Milestone 1 Security & Infrastructure improvements
- Add Dependabot configuration for pip, npm, GitHub Actions, Docker
- Update copilot instructions to require TODO.md and CHANGELOG.md updates
- Add unit tests for middleware, security, app factory, and schemas (125 total tests, 57% coverage)
- Update TODO.md with current progress
- Update CHANGELOG.md with all changes

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
Agent-Logs-Url: https://github.com/christianlouis/pop_puller_to_gmail/sessions/2f15a52f-6812-4586-9d5c-a144226c842b
2026-03-23 11:23:37 +00:00

189 lines
6.4 KiB
Python

"""
Unit tests for extended security module functionality.
"""
from datetime import timedelta
from app.core.security import (
create_access_token,
create_refresh_token,
decode_token,
generate_random_token,
encrypt_credential,
decrypt_credential,
CredentialEncryption,
)
class TestAccessToken:
"""Test JWT access token creation and decoding"""
def test_create_access_token_with_custom_expiry(self):
"""Test creating an access token with custom expiry"""
data = {"sub": "test@example.com"}
token = create_access_token(data, expires_delta=timedelta(hours=1))
assert isinstance(token, str)
assert token.count(".") == 2
def test_decode_valid_access_token(self):
"""Test decoding a valid access token"""
data = {"sub": "user123"}
token = create_access_token(data)
payload = decode_token(token)
assert payload is not None
assert payload["sub"] == "user123"
assert payload["type"] == "access"
def test_decode_invalid_token_returns_none(self):
"""Test that decoding an invalid token returns None"""
result = decode_token("invalid.token.string")
assert result is None
def test_decode_empty_token_returns_none(self):
"""Test that decoding an empty string returns None"""
result = decode_token("")
assert result is None
def test_access_token_contains_type(self):
"""Test that access token payload contains type 'access'"""
data = {"sub": "user@example.com"}
token = create_access_token(data)
payload = decode_token(token)
assert payload is not None
assert payload["type"] == "access"
def test_access_token_contains_expiry(self):
"""Test that access token payload contains expiry"""
data = {"sub": "user@example.com"}
token = create_access_token(data)
payload = decode_token(token)
assert payload is not None
assert "exp" in payload
class TestRefreshToken:
"""Test JWT refresh token creation and decoding"""
def test_create_refresh_token(self):
"""Test refresh token creation"""
data = {"sub": "test@example.com"}
token = create_refresh_token(data)
assert isinstance(token, str)
assert token.count(".") == 2
def test_decode_refresh_token(self):
"""Test decoding a valid refresh token"""
data = {"sub": "user456"}
token = create_refresh_token(data)
payload = decode_token(token)
assert payload is not None
assert payload["sub"] == "user456"
assert payload["type"] == "refresh"
def test_refresh_token_different_from_access(self):
"""Test that refresh and access tokens are different"""
data = {"sub": "test@example.com"}
access = create_access_token(data)
refresh = create_refresh_token(data)
assert access != refresh
class TestRandomToken:
"""Test random token generation"""
def test_generate_random_token_default_length(self):
"""Test generating a random token with default length"""
token = generate_random_token()
assert isinstance(token, str)
assert len(token) > 0
def test_generate_random_token_custom_length(self):
"""Test generating a random token with custom length"""
token = generate_random_token(64)
assert isinstance(token, str)
assert len(token) > 0
def test_random_tokens_are_unique(self):
"""Test that generated tokens are unique"""
tokens = {generate_random_token() for _ in range(10)}
assert len(tokens) == 10
class TestGlobalEncryptionFunctions:
"""Test global encryption convenience functions"""
def test_encrypt_credential_returns_string(self):
"""Test that encrypt_credential returns a non-empty string"""
encrypted = encrypt_credential("my-password")
assert isinstance(encrypted, str)
assert len(encrypted) > 0
def test_decrypt_credential_roundtrip(self):
"""Test encrypt/decrypt roundtrip with global functions"""
original = "super-secret-password-123"
encrypted = encrypt_credential(original)
decrypted = decrypt_credential(encrypted)
assert decrypted == original
def test_encrypt_credential_is_not_plaintext(self):
"""Test that encrypted credential differs from plaintext"""
password = "my-password"
encrypted = encrypt_credential(password)
assert encrypted != password
class TestCredentialEncryptionEdgeCases:
"""Test edge cases in credential encryption"""
def test_encrypt_empty_string(self):
"""Test encrypting an empty string"""
encryptor = CredentialEncryption(user_id=1)
encrypted = encryptor.encrypt("")
decrypted = encryptor.decrypt(encrypted)
assert decrypted == ""
def test_encrypt_long_string(self):
"""Test encrypting a very long string"""
long_password = "a" * 10000
encryptor = CredentialEncryption(user_id=1)
encrypted = encryptor.encrypt(long_password)
decrypted = encryptor.decrypt(encrypted)
assert decrypted == long_password
def test_encrypt_special_characters(self):
"""Test encrypting a string with special characters"""
special = "p@$$w0rd!#%^&*()_+-=[]{}|;':\",./<>?"
encryptor = CredentialEncryption(user_id=1)
encrypted = encryptor.encrypt(special)
decrypted = encryptor.decrypt(encrypted)
assert decrypted == special
def test_encrypt_unicode(self):
"""Test encrypting unicode characters"""
unicode_str = "密码テスト🔒"
encryptor = CredentialEncryption(user_id=1)
encrypted = encryptor.encrypt(unicode_str)
decrypted = encryptor.decrypt(encrypted)
assert decrypted == unicode_str
def test_custom_key(self):
"""Test encryption with a custom key"""
key = "custom-encryption-key-that-is-at-least-32-chars-long"
encryptor = CredentialEncryption(key=key, user_id=1)
encrypted = encryptor.encrypt("test-data")
decrypted = encryptor.decrypt(encrypted)
assert decrypted == "test-data"
def test_system_salt_without_user_id(self):
"""Test encryption with system salt (no user_id)"""
encryptor = CredentialEncryption()
encrypted = encryptor.encrypt("system-data")
decrypted = encryptor.decrypt(encrypted)
assert decrypted == "system-data"