282 lines
9.6 KiB
Python
282 lines
9.6 KiB
Python
#!/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) |