This repository has been archived on 2026-02-15. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
breakpilot-pwa/backend/tests/test_consent_client.py
BreakPilot Dev 19855efacc
Some checks failed
Tests / Go Tests (push) Has been cancelled
Tests / Python Tests (push) Has been cancelled
Tests / Integration Tests (push) Has been cancelled
Tests / Go Lint (push) Has been cancelled
Tests / Python Lint (push) Has been cancelled
Tests / Security Scan (push) Has been cancelled
Tests / All Checks Passed (push) Has been cancelled
Security Scanning / Secret Scanning (push) Has been cancelled
Security Scanning / Dependency Vulnerability Scan (push) Has been cancelled
Security Scanning / Go Security Scan (push) Has been cancelled
Security Scanning / Python Security Scan (push) Has been cancelled
Security Scanning / Node.js Security Scan (push) Has been cancelled
Security Scanning / Docker Image Security (push) Has been cancelled
Security Scanning / Security Summary (push) Has been cancelled
CI/CD Pipeline / Go Tests (push) Has been cancelled
CI/CD Pipeline / Python Tests (push) Has been cancelled
CI/CD Pipeline / Website Tests (push) Has been cancelled
CI/CD Pipeline / Linting (push) Has been cancelled
CI/CD Pipeline / Security Scan (push) Has been cancelled
CI/CD Pipeline / Docker Build & Push (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Deploy to Staging (push) Has been cancelled
CI/CD Pipeline / Deploy to Production (push) Has been cancelled
CI/CD Pipeline / CI Summary (push) Has been cancelled
ci/woodpecker/manual/build-ci-image Pipeline was successful
ci/woodpecker/manual/main Pipeline failed
feat: BreakPilot PWA - Full codebase (clean push without large binaries)
All services: admin-v2, studio-v2, website, ai-compliance-sdk,
consent-service, klausur-service, voice-service, and infrastructure.
Large PDFs and compiled binaries excluded via .gitignore.
2026-02-11 13:25:58 +01:00

408 lines
13 KiB
Python

"""
Tests für den Consent Client
"""
import pytest
import jwt
from datetime import datetime, timedelta
from unittest.mock import AsyncMock, patch, MagicMock
import sys
import os
# Add parent directory to path for imports
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from consent_client import (
generate_jwt_token,
generate_demo_token,
DocumentType,
ConsentStatus,
DocumentVersion,
ConsentClient,
JWT_SECRET,
)
class TestJWTTokenGeneration:
"""Tests für JWT Token Generierung"""
def test_generate_jwt_token_default(self):
"""Test JWT generation with default values"""
token = generate_jwt_token()
assert token is not None
assert isinstance(token, str)
assert len(token) > 0
# Decode and verify
decoded = jwt.decode(token, JWT_SECRET, algorithms=["HS256"])
assert "user_id" in decoded
assert decoded["email"] == "demo@breakpilot.app"
assert decoded["role"] == "user"
assert "exp" in decoded
assert "iat" in decoded
def test_generate_jwt_token_custom_values(self):
"""Test JWT generation with custom values"""
user_id = "test-user-123"
email = "test@example.com"
role = "admin"
token = generate_jwt_token(
user_id=user_id,
email=email,
role=role,
expires_hours=48
)
decoded = jwt.decode(token, JWT_SECRET, algorithms=["HS256"])
assert decoded["user_id"] == user_id
assert decoded["email"] == email
assert decoded["role"] == role
def test_generate_jwt_token_expiration(self):
"""Test that token expiration is set correctly"""
token = generate_jwt_token(expires_hours=1)
decoded = jwt.decode(token, JWT_SECRET, algorithms=["HS256"])
exp = datetime.utcfromtimestamp(decoded["exp"])
now = datetime.utcnow()
# Should expire in approximately 1 hour
time_diff = exp - now
assert time_diff.total_seconds() > 3500 # At least 58 minutes
assert time_diff.total_seconds() < 3700 # At most 62 minutes
def test_generate_demo_token(self):
"""Test demo token generation"""
token = generate_demo_token()
decoded = jwt.decode(token, JWT_SECRET, algorithms=["HS256"])
assert decoded["user_id"].startswith("demo-user-")
assert decoded["email"] == "demo@breakpilot.app"
assert decoded["role"] == "user"
def test_tokens_are_unique(self):
"""Test that generated tokens are unique"""
tokens = [generate_demo_token() for _ in range(10)]
assert len(set(tokens)) == 10 # All tokens should be unique
def test_jwt_token_signature(self):
"""Test that token signature is valid"""
token = generate_jwt_token()
# Should not raise exception
jwt.decode(token, JWT_SECRET, algorithms=["HS256"])
# Should raise exception with wrong secret
with pytest.raises(jwt.InvalidSignatureError):
jwt.decode(token, "wrong-secret", algorithms=["HS256"])
class TestDocumentType:
"""Tests für DocumentType Enum"""
def test_document_types(self):
"""Test all document types exist"""
assert DocumentType.TERMS.value == "terms"
assert DocumentType.PRIVACY.value == "privacy"
assert DocumentType.COOKIES.value == "cookies"
assert DocumentType.COMMUNITY.value == "community"
def test_document_type_is_string(self):
"""Test that document types can be used as strings"""
assert str(DocumentType.TERMS) == "DocumentType.TERMS"
assert DocumentType.TERMS.value == "terms"
class TestConsentStatus:
"""Tests für ConsentStatus Dataclass"""
def test_consent_status_basic(self):
"""Test basic ConsentStatus creation"""
status = ConsentStatus(has_consent=True)
assert status.has_consent is True
assert status.current_version_id is None
assert status.consented_version is None
assert status.needs_update is False
assert status.consented_at is None
def test_consent_status_full(self):
"""Test ConsentStatus with all fields"""
status = ConsentStatus(
has_consent=True,
current_version_id="version-123",
consented_version="1.0.0",
needs_update=False,
consented_at="2024-01-01T00:00:00Z"
)
assert status.has_consent is True
assert status.current_version_id == "version-123"
assert status.consented_version == "1.0.0"
assert status.needs_update is False
assert status.consented_at == "2024-01-01T00:00:00Z"
class TestDocumentVersion:
"""Tests für DocumentVersion Dataclass"""
def test_document_version_creation(self):
"""Test DocumentVersion creation"""
version = DocumentVersion(
id="doc-version-123",
document_id="doc-123",
version="1.0.0",
language="de",
title="Test Document",
content="<p>Test content</p>",
summary="Test summary"
)
assert version.id == "doc-version-123"
assert version.document_id == "doc-123"
assert version.version == "1.0.0"
assert version.language == "de"
assert version.title == "Test Document"
assert version.content == "<p>Test content</p>"
assert version.summary == "Test summary"
class TestConsentClient:
"""Tests für ConsentClient"""
def test_client_initialization(self):
"""Test client initialization"""
client = ConsentClient()
# In Docker: consent-service:8081, locally: localhost:8081
assert client.base_url in ("http://localhost:8081", "http://consent-service:8081")
assert "/api/v1" in client.api_url
def test_client_custom_url(self):
"""Test client with custom URL"""
client = ConsentClient(base_url="https://custom.example.com/")
assert client.base_url == "https://custom.example.com"
assert client.api_url == "https://custom.example.com/api/v1"
def test_get_headers(self):
"""Test header generation"""
client = ConsentClient()
token = "test-token-123"
headers = client._get_headers(token)
assert headers["Authorization"] == "Bearer test-token-123"
assert headers["Content-Type"] == "application/json"
class TestConsentClientAsync:
"""Async tests für ConsentClient"""
@pytest.mark.asyncio
async def test_check_consent_success(self):
"""Test successful consent check"""
client = ConsentClient()
mock_response = MagicMock()
mock_response.status_code = 200
mock_response.json.return_value = {
"has_consent": True,
"current_version_id": "version-123",
"consented_version": "1.0.0",
"needs_update": False,
"consented_at": "2024-01-01T00:00:00Z"
}
with patch("httpx.AsyncClient") as mock_client:
mock_instance = AsyncMock()
mock_instance.get.return_value = mock_response
mock_instance.__aenter__.return_value = mock_instance
mock_instance.__aexit__.return_value = None
mock_client.return_value = mock_instance
status = await client.check_consent(
jwt_token="test-token",
document_type=DocumentType.TERMS
)
assert status.has_consent is True
assert status.current_version_id == "version-123"
@pytest.mark.asyncio
async def test_check_consent_not_found(self):
"""Test consent check when user has no consent"""
client = ConsentClient()
mock_response = MagicMock()
mock_response.status_code = 404
with patch("httpx.AsyncClient") as mock_client:
mock_instance = AsyncMock()
mock_instance.get.return_value = mock_response
mock_instance.__aenter__.return_value = mock_instance
mock_instance.__aexit__.return_value = None
mock_client.return_value = mock_instance
status = await client.check_consent(
jwt_token="test-token",
document_type=DocumentType.TERMS
)
assert status.has_consent is False
assert status.needs_update is True
@pytest.mark.asyncio
async def test_check_consent_connection_error(self):
"""Test consent check when service is unavailable"""
import httpx
client = ConsentClient()
with patch("httpx.AsyncClient") as mock_client:
mock_instance = AsyncMock()
mock_instance.get.side_effect = httpx.RequestError("Connection error")
mock_instance.__aenter__.return_value = mock_instance
mock_instance.__aexit__.return_value = None
mock_client.return_value = mock_instance
status = await client.check_consent(
jwt_token="test-token",
document_type=DocumentType.TERMS
)
# Should not block user when service is unavailable
assert status.has_consent is True
assert status.needs_update is False
@pytest.mark.asyncio
async def test_health_check_success(self):
"""Test successful health check"""
client = ConsentClient()
mock_response = MagicMock()
mock_response.status_code = 200
with patch("httpx.AsyncClient") as mock_client:
mock_instance = AsyncMock()
mock_instance.get.return_value = mock_response
mock_instance.__aenter__.return_value = mock_instance
mock_instance.__aexit__.return_value = None
mock_client.return_value = mock_instance
is_healthy = await client.health_check()
assert is_healthy is True
@pytest.mark.asyncio
async def test_health_check_failure(self):
"""Test failed health check"""
import httpx
client = ConsentClient()
with patch("httpx.AsyncClient") as mock_client:
mock_instance = AsyncMock()
mock_instance.get.side_effect = httpx.RequestError("Connection refused")
mock_instance.__aenter__.return_value = mock_instance
mock_instance.__aexit__.return_value = None
mock_client.return_value = mock_instance
is_healthy = await client.health_check()
assert is_healthy is False
@pytest.mark.asyncio
async def test_give_consent_success(self):
"""Test successful consent submission"""
client = ConsentClient()
mock_response = MagicMock()
mock_response.status_code = 201
with patch("httpx.AsyncClient") as mock_client:
mock_instance = AsyncMock()
mock_instance.post.return_value = mock_response
mock_instance.__aenter__.return_value = mock_instance
mock_instance.__aexit__.return_value = None
mock_client.return_value = mock_instance
success = await client.give_consent(
jwt_token="test-token",
document_type="terms",
version_id="version-123",
consented=True
)
assert success is True
@pytest.mark.asyncio
async def test_give_consent_failure(self):
"""Test failed consent submission"""
client = ConsentClient()
mock_response = MagicMock()
mock_response.status_code = 400
with patch("httpx.AsyncClient") as mock_client:
mock_instance = AsyncMock()
mock_instance.post.return_value = mock_response
mock_instance.__aenter__.return_value = mock_instance
mock_instance.__aexit__.return_value = None
mock_client.return_value = mock_instance
success = await client.give_consent(
jwt_token="test-token",
document_type="terms",
version_id="version-123",
consented=True
)
assert success is False
class TestValidation:
"""Tests für Validierungslogik"""
def test_valid_document_types(self):
"""Test that only valid document types are accepted"""
valid_types = ["terms", "privacy", "cookies", "community"]
for doc_type in DocumentType:
assert doc_type.value in valid_types
def test_jwt_expiration_validation(self):
"""Test that expired tokens are rejected"""
# Create token that expired 1 hour ago
expired_payload = {
"user_id": "test-user",
"email": "test@example.com",
"role": "user",
"exp": datetime.utcnow() - timedelta(hours=1),
"iat": datetime.utcnow() - timedelta(hours=2),
}
expired_token = jwt.encode(expired_payload, JWT_SECRET, algorithm="HS256")
with pytest.raises(jwt.ExpiredSignatureError):
jwt.decode(expired_token, JWT_SECRET, algorithms=["HS256"])
# Performance Tests
class TestPerformance:
"""Performance tests"""
def test_token_generation_performance(self):
"""Test that token generation is fast"""
import time
start = time.time()
for _ in range(100):
generate_jwt_token()
elapsed = time.time() - start
# Should generate 100 tokens in less than 1 second
assert elapsed < 1.0, f"Token generation too slow: {elapsed}s for 100 tokens"
if __name__ == "__main__":
pytest.main([__file__, "-v"])