Files
service-finder/backend/tests/e2e_smoke_test.py

282 lines
9.6 KiB
Python
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
E2E Smoke Test for Registration Flow
Performs a complete "Blind Test" of the registration-to-activation flow.
"""
import asyncio
import uuid
import httpx
import json
import time
from datetime import datetime
from typing import Dict, Any, Optional
import sys
# Configuration
API_BASE_URL = "http://sf_api:8000/api/v1"
MAILPIT_API = "http://sf_mailpit:8025/api/v1"
def generate_unique_email() -> str:
"""Generate a unique email for testing."""
timestamp = int(time.time())
random_id = uuid.uuid4().hex[:8]
return f"test_{timestamp}_{random_id}@example.com"
async def call_registration(email: str) -> Dict[str, Any]:
"""Call the registration API endpoint."""
async with httpx.AsyncClient(timeout=30.0) as client:
payload = {
"email": email,
"password": "TestPassword123!",
"first_name": "Test",
"last_name": "User",
"region_code": "HU",
"lang": "hu"
}
print(f"📝 Registering user with email: {email}")
response = await client.post(f"{API_BASE_URL}/auth/register", json=payload)
if response.status_code != 201:
print(f"❌ Registration failed: {response.status_code}")
print(f"Response: {response.text}")
return {"success": False, "error": f"HTTP {response.status_code}"}
data = response.json()
print(f"✅ Registration successful: {data.get('message')}")
print(f" User ID: {data.get('user_id')}")
return {"success": True, "data": data}
async def get_verification_token_from_db(email: str) -> Optional[str]:
"""Get verification token directly from the database."""
import os
import asyncpg
# Database connection parameters from environment
db_host = os.getenv("DB_HOST", "shared-postgres")
db_name = os.getenv("DB_NAME", "service_finder")
db_user = os.getenv("DB_USER", "service_finder_app")
db_password = os.getenv("DB_PASSWORD", "JELSZAVAD")
try:
# Connect to database
conn = await asyncpg.connect(
host=db_host,
database=db_name,
user=db_user,
password=db_password
)
# Get user ID from email
user_row = await conn.fetchrow(
"SELECT id FROM identity.users WHERE email = $1",
email
)
if not user_row:
print(f"❌ User not found in database for email: {email}")
return None
user_id = user_row['id']
# Get verification token
token_row = await conn.fetchrow(
"""SELECT token FROM identity.verification_tokens
WHERE user_id = $1 AND token_type = 'registration'
ORDER BY created_at DESC LIMIT 1""",
user_id
)
await conn.close()
if token_row:
token = str(token_row['token'])
print(f"🔑 Found verification token in DB: {token[:8]}...")
return token
else:
print("❌ No verification token found in database")
return None
except Exception as e:
print(f"❌ Database error: {e}")
return None
async def get_verification_token_from_mailpit(email: str) -> Optional[str]:
"""Try to get verification token from Mailpit API."""
try:
async with httpx.AsyncClient(timeout=10.0) as client:
# Get latest messages
response = await client.get(f"{MAILPIT_API}/messages")
if response.status_code != 200:
print(f"❌ Mailpit API error: {response.status_code}")
return None
messages = response.json().get('messages', [])
# Find message sent to our test email
for msg in messages:
if msg.get('To', [{}])[0].get('Address') == email:
msg_id = msg['ID']
# Get message details
msg_response = await client.get(f"{MAILPIT_API}/message/{msg_id}")
if msg_response.status_code == 200:
msg_data = msg_response.json()
html = msg_data.get('HTML', '')
# Extract token from HTML (look for token= parameter)
import re
token_match = re.search(r'token=([a-f0-9\-]+)', html)
if token_match:
token = token_match.group(1)
print(f"📧 Found verification token in email: {token[:8]}...")
return token
# Also check text body
text = msg_data.get('Text', '')
token_match = re.search(r'token=([a-f0-9\-]+)', text)
if token_match:
token = token_match.group(1)
print(f"📧 Found verification token in email text: {token[:8]}...")
return token
print("❌ No email found in Mailpit for the test address")
return None
except Exception as e:
print(f"❌ Mailpit error: {e}")
return None
async def verify_email(token: str) -> bool:
"""Call the verify-email endpoint."""
async with httpx.AsyncClient(timeout=30.0) as client:
payload = {"token": token}
print(f"🔐 Verifying email with token: {token[:8]}...")
response = await client.post(f"{API_BASE_URL}/auth/verify-email", json=payload)
if response.status_code != 200:
print(f"❌ Email verification failed: {response.status_code}")
print(f"Response: {response.text}")
return False
data = response.json()
print(f"✅ Email verification successful: {data.get('message')}")
return True
async def check_user_activated(email: str) -> bool:
"""Check if user is activated in database."""
import os
import asyncpg
db_host = os.getenv("DB_HOST", "shared-postgres")
db_name = os.getenv("DB_NAME", "service_finder")
db_user = os.getenv("DB_USER", "service_finder_app")
db_password = os.getenv("DB_PASSWORD", "JELSZAVAD")
try:
conn = await asyncpg.connect(
host=db_host,
database=db_name,
user=db_user,
password=db_password
)
user_row = await conn.fetchrow(
"SELECT is_active FROM identity.users WHERE email = $1",
email
)
await conn.close()
if user_row:
is_active = user_row['is_active']
print(f"👤 User activation status: {'ACTIVE' if is_active else 'INACTIVE'}")
return is_active
else:
print("❌ User not found when checking activation")
return False
except Exception as e:
print(f"❌ Database error checking activation: {e}")
return False
async def main():
"""Main test execution."""
print("=" * 60)
print("🚀 Service Finder Registration E2E Smoke Test")
print("=" * 60)
# Generate unique test email
test_email = generate_unique_email()
print(f"📧 Test email: {test_email}")
# Step 1: Register user
print("\n1⃣ Step 1: Registration")
reg_result = await call_registration(test_email)
if not reg_result["success"]:
print("❌ TEST FAILED: Registration failed")
return False
# Wait a moment for email to be sent
print("\n⏳ Waiting 3 seconds for email processing...")
await asyncio.sleep(3)
# Step 2: Get verification token
print("\n2⃣ Step 2: Token Retrieval")
# Try database first (more reliable)
token = await get_verification_token_from_db(test_email)
# If not found in DB, try Mailpit
if not token:
print("⚠️ Token not found in DB, trying Mailpit...")
token = await get_verification_token_from_mailpit(test_email)
if not token:
print("❌ TEST FAILED: Could not retrieve verification token")
return False
# Step 3: Verify email
print("\n3⃣ Step 3: Email Verification")
verify_success = await verify_email(token)
if not verify_success:
print("❌ TEST FAILED: Email verification failed")
return False
# Step 4: Check user activation
print("\n4⃣ Step 4: Activation Verification")
await asyncio.sleep(2) # Give DB time to update
is_active = await check_user_activated(test_email)
if not is_active:
print("❌ TEST FAILED: User not activated after verification")
return False
# Final report
print("\n" + "=" * 60)
print("✅ TEST PASSED: Registration-to-Activation flow is 100% OK")
print("=" * 60)
print(f"Summary:")
print(f" • Test email: {test_email}")
print(f" • Registration: ✅ Success")
print(f" • Token retrieval: ✅ Success")
print(f" • Email verification: ✅ Success")
print(f" • User activation: ✅ Success")
print("=" * 60)
return True
if __name__ == "__main__":
# Install asyncpg if needed
try:
import asyncpg
except ImportError:
print("⚠️ asyncpg not installed. Installing...")
import subprocess
subprocess.check_call([sys.executable, "-m", "pip", "install", "asyncpg"])
import asyncpg
# Run the test
success = asyncio.run(main())
sys.exit(0 if success else 1)