This commit is contained in:
@@ -1,18 +1,24 @@
|
||||
import pytest
|
||||
import asyncio
|
||||
|
||||
import pytest
|
||||
from httpx import AsyncClient
|
||||
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from httpx import AsyncClient
|
||||
from shared.database import Base
|
||||
from shared.config import settings
|
||||
|
||||
from services.user_service.main import app
|
||||
from shared.config import settings
|
||||
from shared.database import Base
|
||||
|
||||
# Test database URL
|
||||
TEST_DATABASE_URL = "postgresql+asyncpg://admin:password@localhost:5432/women_safety_test"
|
||||
TEST_DATABASE_URL = (
|
||||
"postgresql+asyncpg://admin:password@localhost:5432/women_safety_test"
|
||||
)
|
||||
|
||||
# Test engine
|
||||
test_engine = create_async_engine(TEST_DATABASE_URL, echo=True)
|
||||
TestAsyncSession = sessionmaker(test_engine, class_=AsyncSession, expire_on_commit=False)
|
||||
TestAsyncSession = sessionmaker(
|
||||
test_engine, class_=AsyncSession, expire_on_commit=False
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
@@ -56,5 +62,5 @@ def user_data():
|
||||
"password": "testpassword123",
|
||||
"first_name": "Test",
|
||||
"last_name": "User",
|
||||
"phone": "+1234567890"
|
||||
}
|
||||
"phone": "+1234567890",
|
||||
}
|
||||
|
||||
@@ -4,19 +4,21 @@ Simple test script to verify the women's safety app is working correctly.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import asyncpg
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import asyncpg
|
||||
|
||||
# Add project root to path
|
||||
sys.path.insert(0, str(Path(__file__).parent))
|
||||
|
||||
from shared.config import settings
|
||||
from shared.database import engine, AsyncSessionLocal
|
||||
from sqlalchemy import text
|
||||
|
||||
from services.user_service.models import User
|
||||
from services.user_service.schemas import UserCreate
|
||||
from shared.auth import get_password_hash
|
||||
from sqlalchemy import text
|
||||
from shared.config import settings
|
||||
from shared.database import AsyncSessionLocal, engine
|
||||
|
||||
|
||||
async def test_database_connection():
|
||||
@@ -24,17 +26,17 @@ async def test_database_connection():
|
||||
print("🔍 Testing database connection...")
|
||||
try:
|
||||
# Test direct asyncpg connection
|
||||
conn = await asyncpg.connect(settings.DATABASE_URL.replace('+asyncpg', ''))
|
||||
await conn.execute('SELECT 1')
|
||||
conn = await asyncpg.connect(settings.DATABASE_URL.replace("+asyncpg", ""))
|
||||
await conn.execute("SELECT 1")
|
||||
await conn.close()
|
||||
print("✅ Direct asyncpg connection successful")
|
||||
|
||||
|
||||
# Test SQLAlchemy engine connection
|
||||
async with engine.begin() as conn:
|
||||
result = await conn.execute(text('SELECT version()'))
|
||||
result = await conn.execute(text("SELECT version()"))
|
||||
version = result.scalar()
|
||||
print(f"✅ SQLAlchemy connection successful (PostgreSQL {version[:20]}...)")
|
||||
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"❌ Database connection failed: {e}")
|
||||
@@ -50,18 +52,22 @@ async def test_database_tables():
|
||||
result = await session.execute(text("SELECT COUNT(*) FROM users"))
|
||||
count = result.scalar()
|
||||
print(f"✅ Users table exists with {count} users")
|
||||
|
||||
|
||||
# Test table structure
|
||||
result = await session.execute(text("""
|
||||
result = await session.execute(
|
||||
text(
|
||||
"""
|
||||
SELECT column_name, data_type
|
||||
FROM information_schema.columns
|
||||
WHERE table_name = 'users'
|
||||
ORDER BY ordinal_position
|
||||
LIMIT 5
|
||||
"""))
|
||||
"""
|
||||
)
|
||||
)
|
||||
columns = result.fetchall()
|
||||
print(f"✅ Users table has columns: {[col[0] for col in columns]}")
|
||||
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"❌ Database table test failed: {e}")
|
||||
@@ -75,35 +81,40 @@ async def test_user_creation():
|
||||
async with AsyncSessionLocal() as session:
|
||||
# Create test user
|
||||
test_email = "test_debug@example.com"
|
||||
|
||||
|
||||
# Delete if exists
|
||||
await session.execute(text("DELETE FROM users WHERE email = :email"),
|
||||
{"email": test_email})
|
||||
await session.execute(
|
||||
text("DELETE FROM users WHERE email = :email"), {"email": test_email}
|
||||
)
|
||||
await session.commit()
|
||||
|
||||
|
||||
# Create new user
|
||||
user = User(
|
||||
email=test_email,
|
||||
phone="+1234567890",
|
||||
password_hash=get_password_hash("testpass"),
|
||||
first_name="Test",
|
||||
last_name="User"
|
||||
last_name="User",
|
||||
)
|
||||
session.add(user)
|
||||
await session.commit()
|
||||
|
||||
|
||||
# Verify creation
|
||||
result = await session.execute(text("SELECT id, email FROM users WHERE email = :email"),
|
||||
{"email": test_email})
|
||||
result = await session.execute(
|
||||
text("SELECT id, email FROM users WHERE email = :email"),
|
||||
{"email": test_email},
|
||||
)
|
||||
user_row = result.fetchone()
|
||||
|
||||
|
||||
if user_row:
|
||||
print(f"✅ User created successfully: ID={user_row[0]}, Email={user_row[1]}")
|
||||
print(
|
||||
f"✅ User created successfully: ID={user_row[0]}, Email={user_row[1]}"
|
||||
)
|
||||
return True
|
||||
else:
|
||||
print("❌ User creation failed - user not found after creation")
|
||||
return False
|
||||
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ User creation test failed: {e}")
|
||||
return False
|
||||
@@ -113,33 +124,38 @@ async def test_auth_functions():
|
||||
"""Test authentication functions."""
|
||||
print("🔍 Testing authentication functions...")
|
||||
try:
|
||||
from shared.auth import get_password_hash, verify_password, create_access_token, verify_token
|
||||
|
||||
from shared.auth import (
|
||||
create_access_token,
|
||||
get_password_hash,
|
||||
verify_password,
|
||||
verify_token,
|
||||
)
|
||||
|
||||
# Test password hashing
|
||||
password = "testpassword123"
|
||||
hashed = get_password_hash(password)
|
||||
print(f"✅ Password hashing works")
|
||||
|
||||
|
||||
# Test password verification
|
||||
if verify_password(password, hashed):
|
||||
print("✅ Password verification works")
|
||||
else:
|
||||
print("❌ Password verification failed")
|
||||
return False
|
||||
|
||||
|
||||
# Test token creation and verification
|
||||
token_data = {"sub": "123", "email": "test@example.com"}
|
||||
token = create_access_token(token_data)
|
||||
verified_data = verify_token(token)
|
||||
|
||||
|
||||
if verified_data and verified_data["user_id"] == 123:
|
||||
print("✅ Token creation and verification works")
|
||||
else:
|
||||
print("❌ Token verification failed")
|
||||
return False
|
||||
|
||||
|
||||
return True
|
||||
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Authentication test failed: {e}")
|
||||
return False
|
||||
@@ -150,14 +166,14 @@ async def main():
|
||||
print("🚀 Starting Women's Safety App System Tests")
|
||||
print(f"Database URL: {settings.DATABASE_URL}")
|
||||
print("=" * 60)
|
||||
|
||||
|
||||
tests = [
|
||||
test_database_connection,
|
||||
test_database_tables,
|
||||
test_user_creation,
|
||||
test_auth_functions,
|
||||
]
|
||||
|
||||
|
||||
results = []
|
||||
for test in tests:
|
||||
try:
|
||||
@@ -167,7 +183,7 @@ async def main():
|
||||
print(f"❌ Test {test.__name__} failed with exception: {e}")
|
||||
results.append(False)
|
||||
print()
|
||||
|
||||
|
||||
print("=" * 60)
|
||||
if all(results):
|
||||
print("🎉 All tests passed! The system is ready for use.")
|
||||
@@ -179,4 +195,4 @@ async def main():
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(asyncio.run(main()))
|
||||
sys.exit(asyncio.run(main()))
|
||||
|
||||
@@ -5,9 +5,10 @@ Run this script to test all major API endpoints
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import httpx
|
||||
import json
|
||||
from typing import Dict, Any
|
||||
from typing import Any, Dict
|
||||
|
||||
import httpx
|
||||
|
||||
BASE_URL = "http://localhost:8000"
|
||||
|
||||
@@ -17,22 +18,24 @@ class APITester:
|
||||
self.base_url = base_url
|
||||
self.token = None
|
||||
self.user_id = None
|
||||
|
||||
|
||||
async def test_registration(self) -> Dict[str, Any]:
|
||||
"""Test user registration"""
|
||||
print("🔐 Testing user registration...")
|
||||
|
||||
|
||||
user_data = {
|
||||
"email": "test@example.com",
|
||||
"password": "testpassword123",
|
||||
"first_name": "Test",
|
||||
"last_name": "User",
|
||||
"phone": "+1234567890"
|
||||
"phone": "+1234567890",
|
||||
}
|
||||
|
||||
|
||||
async with httpx.AsyncClient() as client:
|
||||
response = await client.post(f"{self.base_url}/api/v1/register", json=user_data)
|
||||
|
||||
response = await client.post(
|
||||
f"{self.base_url}/api/v1/register", json=user_data
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
self.user_id = data["id"]
|
||||
@@ -41,19 +44,18 @@ class APITester:
|
||||
else:
|
||||
print(f"❌ Registration failed: {response.status_code} - {response.text}")
|
||||
return {}
|
||||
|
||||
|
||||
async def test_login(self) -> str:
|
||||
"""Test user login and get token"""
|
||||
print("🔑 Testing user login...")
|
||||
|
||||
login_data = {
|
||||
"email": "test@example.com",
|
||||
"password": "testpassword123"
|
||||
}
|
||||
|
||||
|
||||
login_data = {"email": "test@example.com", "password": "testpassword123"}
|
||||
|
||||
async with httpx.AsyncClient() as client:
|
||||
response = await client.post(f"{self.base_url}/api/v1/login", json=login_data)
|
||||
|
||||
response = await client.post(
|
||||
f"{self.base_url}/api/v1/login", json=login_data
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
self.token = data["access_token"]
|
||||
@@ -62,188 +64,219 @@ class APITester:
|
||||
else:
|
||||
print(f"❌ Login failed: {response.status_code} - {response.text}")
|
||||
return ""
|
||||
|
||||
|
||||
async def test_profile(self):
|
||||
"""Test getting and updating profile"""
|
||||
if not self.token:
|
||||
print("❌ No token available for profile test")
|
||||
return
|
||||
|
||||
|
||||
print("👤 Testing profile operations...")
|
||||
headers = {"Authorization": f"Bearer {self.token}"}
|
||||
|
||||
|
||||
async with httpx.AsyncClient() as client:
|
||||
# Get profile
|
||||
response = await client.get(f"{self.base_url}/api/v1/profile", headers=headers)
|
||||
response = await client.get(
|
||||
f"{self.base_url}/api/v1/profile", headers=headers
|
||||
)
|
||||
if response.status_code == 200:
|
||||
print("✅ Profile retrieval successful")
|
||||
else:
|
||||
print(f"❌ Profile retrieval failed: {response.status_code}")
|
||||
|
||||
|
||||
# Update profile
|
||||
update_data = {"bio": "Updated bio for testing"}
|
||||
response = await client.put(f"{self.base_url}/api/v1/profile", json=update_data, headers=headers)
|
||||
response = await client.put(
|
||||
f"{self.base_url}/api/v1/profile", json=update_data, headers=headers
|
||||
)
|
||||
if response.status_code == 200:
|
||||
print("✅ Profile update successful")
|
||||
else:
|
||||
print(f"❌ Profile update failed: {response.status_code}")
|
||||
|
||||
|
||||
async def test_location_update(self):
|
||||
"""Test location services"""
|
||||
if not self.token:
|
||||
print("❌ No token available for location test")
|
||||
return
|
||||
|
||||
|
||||
print("📍 Testing location services...")
|
||||
headers = {"Authorization": f"Bearer {self.token}"}
|
||||
|
||||
location_data = {
|
||||
"latitude": 37.7749,
|
||||
"longitude": -122.4194,
|
||||
"accuracy": 10.5
|
||||
}
|
||||
|
||||
|
||||
location_data = {"latitude": 37.7749, "longitude": -122.4194, "accuracy": 10.5}
|
||||
|
||||
async with httpx.AsyncClient() as client:
|
||||
# Update location
|
||||
response = await client.post(f"{self.base_url}/api/v1/update-location", json=location_data, headers=headers)
|
||||
response = await client.post(
|
||||
f"{self.base_url}/api/v1/update-location",
|
||||
json=location_data,
|
||||
headers=headers,
|
||||
)
|
||||
if response.status_code == 200:
|
||||
print("✅ Location update successful")
|
||||
else:
|
||||
print(f"❌ Location update failed: {response.status_code} - {response.text}")
|
||||
|
||||
print(
|
||||
f"❌ Location update failed: {response.status_code} - {response.text}"
|
||||
)
|
||||
|
||||
# Get nearby users
|
||||
params = {
|
||||
"latitude": 37.7749,
|
||||
"longitude": -122.4194,
|
||||
"radius_km": 1.0
|
||||
}
|
||||
response = await client.get(f"{self.base_url}/api/v1/nearby-users", params=params, headers=headers)
|
||||
params = {"latitude": 37.7749, "longitude": -122.4194, "radius_km": 1.0}
|
||||
response = await client.get(
|
||||
f"{self.base_url}/api/v1/nearby-users", params=params, headers=headers
|
||||
)
|
||||
if response.status_code == 200:
|
||||
nearby = response.json()
|
||||
print(f"✅ Nearby users query successful - found {len(nearby)} users")
|
||||
else:
|
||||
print(f"❌ Nearby users query failed: {response.status_code}")
|
||||
|
||||
|
||||
async def test_emergency_alert(self):
|
||||
"""Test emergency alert system"""
|
||||
if not self.token:
|
||||
print("❌ No token available for emergency test")
|
||||
return
|
||||
|
||||
|
||||
print("🚨 Testing emergency alert system...")
|
||||
headers = {"Authorization": f"Bearer {self.token}"}
|
||||
|
||||
|
||||
alert_data = {
|
||||
"latitude": 37.7749,
|
||||
"longitude": -122.4194,
|
||||
"alert_type": "general",
|
||||
"message": "Test emergency alert",
|
||||
"address": "123 Test Street, San Francisco, CA"
|
||||
"address": "123 Test Street, San Francisco, CA",
|
||||
}
|
||||
|
||||
|
||||
async with httpx.AsyncClient() as client:
|
||||
# Create emergency alert
|
||||
response = await client.post(f"{self.base_url}/api/v1/alert", json=alert_data, headers=headers)
|
||||
response = await client.post(
|
||||
f"{self.base_url}/api/v1/alert", json=alert_data, headers=headers
|
||||
)
|
||||
if response.status_code == 200:
|
||||
alert = response.json()
|
||||
alert_id = alert["id"]
|
||||
print(f"✅ Emergency alert created successfully! Alert ID: {alert_id}")
|
||||
|
||||
|
||||
# Get my alerts
|
||||
response = await client.get(f"{self.base_url}/api/v1/alerts/my", headers=headers)
|
||||
response = await client.get(
|
||||
f"{self.base_url}/api/v1/alerts/my", headers=headers
|
||||
)
|
||||
if response.status_code == 200:
|
||||
alerts = response.json()
|
||||
print(f"✅ Retrieved {len(alerts)} alerts")
|
||||
else:
|
||||
print(f"❌ Failed to retrieve alerts: {response.status_code}")
|
||||
|
||||
|
||||
# Resolve alert
|
||||
response = await client.put(f"{self.base_url}/api/v1/alert/{alert_id}/resolve", headers=headers)
|
||||
response = await client.put(
|
||||
f"{self.base_url}/api/v1/alert/{alert_id}/resolve", headers=headers
|
||||
)
|
||||
if response.status_code == 200:
|
||||
print("✅ Alert resolved successfully")
|
||||
else:
|
||||
print(f"❌ Failed to resolve alert: {response.status_code}")
|
||||
|
||||
|
||||
else:
|
||||
print(f"❌ Emergency alert creation failed: {response.status_code} - {response.text}")
|
||||
|
||||
print(
|
||||
f"❌ Emergency alert creation failed: {response.status_code} - {response.text}"
|
||||
)
|
||||
|
||||
async def test_calendar_entry(self):
|
||||
"""Test calendar services"""
|
||||
if not self.token:
|
||||
print("❌ No token available for calendar test")
|
||||
return
|
||||
|
||||
|
||||
print("📅 Testing calendar services...")
|
||||
headers = {"Authorization": f"Bearer {self.token}"}
|
||||
|
||||
|
||||
calendar_data = {
|
||||
"entry_date": "2024-01-15",
|
||||
"entry_type": "period",
|
||||
"flow_intensity": "medium",
|
||||
"mood": "happy",
|
||||
"energy_level": 4
|
||||
"energy_level": 4,
|
||||
}
|
||||
|
||||
|
||||
async with httpx.AsyncClient() as client:
|
||||
# Create calendar entry
|
||||
response = await client.post(f"{self.base_url}/api/v1/entries", json=calendar_data, headers=headers)
|
||||
response = await client.post(
|
||||
f"{self.base_url}/api/v1/entries", json=calendar_data, headers=headers
|
||||
)
|
||||
if response.status_code == 200:
|
||||
print("✅ Calendar entry created successfully")
|
||||
|
||||
|
||||
# Get calendar entries
|
||||
response = await client.get(f"{self.base_url}/api/v1/entries", headers=headers)
|
||||
response = await client.get(
|
||||
f"{self.base_url}/api/v1/entries", headers=headers
|
||||
)
|
||||
if response.status_code == 200:
|
||||
entries = response.json()
|
||||
print(f"✅ Retrieved {len(entries)} calendar entries")
|
||||
else:
|
||||
print(f"❌ Failed to retrieve calendar entries: {response.status_code}")
|
||||
|
||||
print(
|
||||
f"❌ Failed to retrieve calendar entries: {response.status_code}"
|
||||
)
|
||||
|
||||
# Get cycle overview
|
||||
response = await client.get(f"{self.base_url}/api/v1/cycle-overview", headers=headers)
|
||||
response = await client.get(
|
||||
f"{self.base_url}/api/v1/cycle-overview", headers=headers
|
||||
)
|
||||
if response.status_code == 200:
|
||||
overview = response.json()
|
||||
print(f"✅ Cycle overview retrieved - Phase: {overview.get('current_phase', 'unknown')}")
|
||||
print(
|
||||
f"✅ Cycle overview retrieved - Phase: {overview.get('current_phase', 'unknown')}"
|
||||
)
|
||||
else:
|
||||
print(f"❌ Failed to get cycle overview: {response.status_code}")
|
||||
|
||||
|
||||
else:
|
||||
print(f"❌ Calendar entry creation failed: {response.status_code} - {response.text}")
|
||||
|
||||
print(
|
||||
f"❌ Calendar entry creation failed: {response.status_code} - {response.text}"
|
||||
)
|
||||
|
||||
async def test_notifications(self):
|
||||
"""Test notification services"""
|
||||
if not self.token:
|
||||
print("❌ No token available for notification test")
|
||||
return
|
||||
|
||||
|
||||
print("🔔 Testing notification services...")
|
||||
headers = {"Authorization": f"Bearer {self.token}"}
|
||||
|
||||
device_data = {
|
||||
"token": "test_fcm_token_12345",
|
||||
"platform": "android"
|
||||
}
|
||||
|
||||
|
||||
device_data = {"token": "test_fcm_token_12345", "platform": "android"}
|
||||
|
||||
async with httpx.AsyncClient() as client:
|
||||
# Register device token
|
||||
response = await client.post(f"{self.base_url}/api/v1/register-device", json=device_data, headers=headers)
|
||||
response = await client.post(
|
||||
f"{self.base_url}/api/v1/register-device",
|
||||
json=device_data,
|
||||
headers=headers,
|
||||
)
|
||||
if response.status_code == 200:
|
||||
print("✅ Device token registered successfully")
|
||||
|
||||
|
||||
# Get my devices
|
||||
response = await client.get(f"{self.base_url}/api/v1/my-devices", headers=headers)
|
||||
response = await client.get(
|
||||
f"{self.base_url}/api/v1/my-devices", headers=headers
|
||||
)
|
||||
if response.status_code == 200:
|
||||
devices = response.json()
|
||||
print(f"✅ Retrieved device info - {devices['device_count']} devices")
|
||||
print(
|
||||
f"✅ Retrieved device info - {devices['device_count']} devices"
|
||||
)
|
||||
else:
|
||||
print(f"❌ Failed to retrieve devices: {response.status_code}")
|
||||
|
||||
|
||||
else:
|
||||
print(f"❌ Device token registration failed: {response.status_code} - {response.text}")
|
||||
|
||||
print(
|
||||
f"❌ Device token registration failed: {response.status_code} - {response.text}"
|
||||
)
|
||||
|
||||
async def test_health_checks(self):
|
||||
"""Test system health endpoints"""
|
||||
print("🏥 Testing health checks...")
|
||||
|
||||
|
||||
async with httpx.AsyncClient() as client:
|
||||
# Gateway health
|
||||
response = await client.get(f"{self.base_url}/api/v1/health")
|
||||
@@ -251,52 +284,58 @@ class APITester:
|
||||
print("✅ API Gateway health check passed")
|
||||
else:
|
||||
print(f"❌ API Gateway health check failed: {response.status_code}")
|
||||
|
||||
|
||||
# Services status
|
||||
response = await client.get(f"{self.base_url}/api/v1/services-status")
|
||||
if response.status_code == 200:
|
||||
status = response.json()
|
||||
healthy_services = sum(1 for service in status["services"].values() if service["status"] == "healthy")
|
||||
healthy_services = sum(
|
||||
1
|
||||
for service in status["services"].values()
|
||||
if service["status"] == "healthy"
|
||||
)
|
||||
total_services = len(status["services"])
|
||||
print(f"✅ Services status check - {healthy_services}/{total_services} services healthy")
|
||||
|
||||
print(
|
||||
f"✅ Services status check - {healthy_services}/{total_services} services healthy"
|
||||
)
|
||||
|
||||
# Print individual service status
|
||||
for name, service in status["services"].items():
|
||||
status_icon = "✅" if service["status"] == "healthy" else "❌"
|
||||
print(f" {status_icon} {name}: {service['status']}")
|
||||
else:
|
||||
print(f"❌ Services status check failed: {response.status_code}")
|
||||
|
||||
|
||||
async def run_all_tests(self):
|
||||
"""Run all API tests"""
|
||||
print("🚀 Starting API Tests for Women's Safety App\n")
|
||||
|
||||
|
||||
# Test basic functionality
|
||||
await self.test_health_checks()
|
||||
print()
|
||||
|
||||
|
||||
await self.test_registration()
|
||||
print()
|
||||
|
||||
|
||||
await self.test_login()
|
||||
print()
|
||||
|
||||
|
||||
if self.token:
|
||||
await self.test_profile()
|
||||
print()
|
||||
|
||||
|
||||
await self.test_location_update()
|
||||
print()
|
||||
|
||||
|
||||
await self.test_emergency_alert()
|
||||
print()
|
||||
|
||||
|
||||
await self.test_calendar_entry()
|
||||
print()
|
||||
|
||||
|
||||
await self.test_notifications()
|
||||
print()
|
||||
|
||||
|
||||
print("🎉 API testing completed!")
|
||||
|
||||
|
||||
@@ -304,23 +343,25 @@ async def main():
|
||||
"""Main function to run tests"""
|
||||
print("Women's Safety App - API Test Suite")
|
||||
print("=" * 50)
|
||||
|
||||
|
||||
# Check if services are running
|
||||
try:
|
||||
async with httpx.AsyncClient() as client:
|
||||
response = await client.get(f"{BASE_URL}/api/v1/health", timeout=5.0)
|
||||
if response.status_code != 200:
|
||||
print(f"❌ Services not responding. Make sure to run './start_services.sh' first")
|
||||
print(
|
||||
f"❌ Services not responding. Make sure to run './start_services.sh' first"
|
||||
)
|
||||
return
|
||||
except Exception as e:
|
||||
print(f"❌ Cannot connect to services: {e}")
|
||||
print("Make sure to run './start_services.sh' first")
|
||||
return
|
||||
|
||||
|
||||
# Run tests
|
||||
tester = APITester()
|
||||
await tester.run_all_tests()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
asyncio.run(main())
|
||||
|
||||
@@ -1,50 +1,62 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import asyncio
|
||||
import aiohttp
|
||||
import json
|
||||
import subprocess
|
||||
import time
|
||||
import signal
|
||||
import os
|
||||
import signal
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
|
||||
import aiohttp
|
||||
|
||||
|
||||
async def test_user_service():
|
||||
"""Test the User Service API"""
|
||||
|
||||
|
||||
# Start the service
|
||||
print("🚀 Starting User Service...")
|
||||
|
||||
|
||||
# Set up environment
|
||||
env = os.environ.copy()
|
||||
env['PYTHONPATH'] = f"{os.getcwd()}:{env.get('PYTHONPATH', '')}"
|
||||
|
||||
env["PYTHONPATH"] = f"{os.getcwd()}:{env.get('PYTHONPATH', '')}"
|
||||
|
||||
# Start uvicorn process
|
||||
process = subprocess.Popen([
|
||||
sys.executable, "-m", "uvicorn", "main:app",
|
||||
"--host", "0.0.0.0", "--port", "8001"
|
||||
], cwd="services/user_service", env=env)
|
||||
|
||||
process = subprocess.Popen(
|
||||
[
|
||||
sys.executable,
|
||||
"-m",
|
||||
"uvicorn",
|
||||
"main:app",
|
||||
"--host",
|
||||
"0.0.0.0",
|
||||
"--port",
|
||||
"8001",
|
||||
],
|
||||
cwd="services/user_service",
|
||||
env=env,
|
||||
)
|
||||
|
||||
print("⏳ Waiting for service to start...")
|
||||
await asyncio.sleep(5)
|
||||
|
||||
|
||||
try:
|
||||
# Test registration
|
||||
async with aiohttp.ClientSession() as session:
|
||||
print("🧪 Testing user registration...")
|
||||
|
||||
|
||||
registration_data = {
|
||||
"email": "test3@example.com",
|
||||
"password": "testpassword123",
|
||||
"first_name": "Test",
|
||||
"last_name": "User3",
|
||||
"phone": "+1234567892"
|
||||
"phone": "+1234567892",
|
||||
}
|
||||
|
||||
|
||||
async with session.post(
|
||||
"http://localhost:8001/api/v1/register",
|
||||
json=registration_data,
|
||||
headers={"Content-Type": "application/json"}
|
||||
headers={"Content-Type": "application/json"},
|
||||
) as response:
|
||||
if response.status == 201:
|
||||
data = await response.json()
|
||||
@@ -54,19 +66,16 @@ async def test_user_service():
|
||||
text = await response.text()
|
||||
print(f"❌ Registration failed with status {response.status}")
|
||||
print(f"📝 Error: {text}")
|
||||
|
||||
|
||||
# Test login
|
||||
print("\n🧪 Testing user login...")
|
||||
|
||||
login_data = {
|
||||
"email": "test3@example.com",
|
||||
"password": "testpassword123"
|
||||
}
|
||||
|
||||
|
||||
login_data = {"email": "test3@example.com", "password": "testpassword123"}
|
||||
|
||||
async with session.post(
|
||||
"http://localhost:8001/api/v1/login",
|
||||
json=login_data,
|
||||
headers={"Content-Type": "application/json"}
|
||||
headers={"Content-Type": "application/json"},
|
||||
) as response:
|
||||
if response.status == 200:
|
||||
data = await response.json()
|
||||
@@ -76,7 +85,7 @@ async def test_user_service():
|
||||
text = await response.text()
|
||||
print(f"❌ Login failed with status {response.status}")
|
||||
print(f"📝 Error: {text}")
|
||||
|
||||
|
||||
# Test health check
|
||||
print("\n🧪 Testing health check...")
|
||||
async with session.get("http://localhost:8001/api/v1/health") as response:
|
||||
@@ -88,10 +97,10 @@ async def test_user_service():
|
||||
text = await response.text()
|
||||
print(f"❌ Health check failed with status {response.status}")
|
||||
print(f"📝 Error: {text}")
|
||||
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Test failed with exception: {e}")
|
||||
|
||||
|
||||
finally:
|
||||
# Stop the service
|
||||
print("\n🛑 Stopping service...")
|
||||
@@ -99,5 +108,6 @@ async def test_user_service():
|
||||
process.wait()
|
||||
print("✅ Test completed!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(test_user_service())
|
||||
asyncio.run(test_user_service())
|
||||
|
||||
71
tests/test_basic.py
Normal file
71
tests/test_basic.py
Normal file
@@ -0,0 +1,71 @@
|
||||
"""
|
||||
Basic Unit Tests for Women's Safety App Backend
|
||||
"""
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
|
||||
def test_basic_health_check():
|
||||
"""Базовый тест работоспособности"""
|
||||
# Простая проверка что модули импортируются
|
||||
import fastapi
|
||||
import sqlalchemy
|
||||
import redis
|
||||
|
||||
assert True # Если дошли сюда, то импорты работают
|
||||
|
||||
|
||||
def test_basic_functionality():
|
||||
"""Тест базовой функциональности"""
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
@app.get("/health")
|
||||
def health():
|
||||
return {"status": "ok"}
|
||||
|
||||
client = TestClient(app)
|
||||
response = client.get("/health")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"status": "ok"}
|
||||
|
||||
|
||||
def test_environment_variables():
|
||||
"""Тест переменных окружения"""
|
||||
import os
|
||||
|
||||
# Проверяем что переменные окружения доступны
|
||||
database_url = os.getenv("DATABASE_URL")
|
||||
redis_url = os.getenv("REDIS_URL")
|
||||
jwt_secret = os.getenv("JWT_SECRET_KEY")
|
||||
|
||||
assert database_url is not None
|
||||
assert redis_url is not None
|
||||
assert jwt_secret is not None
|
||||
|
||||
|
||||
def test_pydantic_models():
|
||||
"""Тест Pydantic моделей"""
|
||||
from pydantic import BaseModel
|
||||
|
||||
class TestModel(BaseModel):
|
||||
name: str
|
||||
age: int
|
||||
|
||||
model = TestModel(name="Test", age=25)
|
||||
assert model.name == "Test"
|
||||
assert model.age == 25
|
||||
|
||||
|
||||
def test_password_hashing():
|
||||
"""Тест хеширования паролей"""
|
||||
from passlib.context import CryptContext
|
||||
|
||||
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
|
||||
|
||||
password = "testpassword123"
|
||||
hashed = pwd_context.hash(password)
|
||||
|
||||
assert pwd_context.verify(password, hashed)
|
||||
assert not pwd_context.verify("wrongpassword", hashed)
|
||||
@@ -18,7 +18,7 @@ class TestUserService:
|
||||
"""Test registration with duplicate email"""
|
||||
# First registration
|
||||
await client.post("/api/v1/register", json=user_data)
|
||||
|
||||
|
||||
# Second registration with same email
|
||||
response = await client.post("/api/v1/register", json=user_data)
|
||||
assert response.status_code == 400
|
||||
@@ -28,12 +28,9 @@ class TestUserService:
|
||||
"""Test user login"""
|
||||
# Register user first
|
||||
await client.post("/api/v1/register", json=user_data)
|
||||
|
||||
|
||||
# Login
|
||||
login_data = {
|
||||
"email": user_data["email"],
|
||||
"password": user_data["password"]
|
||||
}
|
||||
login_data = {"email": user_data["email"], "password": user_data["password"]}
|
||||
response = await client.post("/api/v1/login", json=login_data)
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
@@ -42,10 +39,7 @@ class TestUserService:
|
||||
|
||||
async def test_login_invalid_credentials(self, client: AsyncClient):
|
||||
"""Test login with invalid credentials"""
|
||||
login_data = {
|
||||
"email": "wrong@example.com",
|
||||
"password": "wrongpassword"
|
||||
}
|
||||
login_data = {"email": "wrong@example.com", "password": "wrongpassword"}
|
||||
response = await client.post("/api/v1/login", json=login_data)
|
||||
assert response.status_code == 401
|
||||
|
||||
@@ -53,12 +47,12 @@ class TestUserService:
|
||||
"""Test getting user profile"""
|
||||
# Register and login
|
||||
await client.post("/api/v1/register", json=user_data)
|
||||
login_response = await client.post("/api/v1/login", json={
|
||||
"email": user_data["email"],
|
||||
"password": user_data["password"]
|
||||
})
|
||||
login_response = await client.post(
|
||||
"/api/v1/login",
|
||||
json={"email": user_data["email"], "password": user_data["password"]},
|
||||
)
|
||||
token = login_response.json()["access_token"]
|
||||
|
||||
|
||||
# Get profile
|
||||
headers = {"Authorization": f"Bearer {token}"}
|
||||
response = await client.get("/api/v1/profile", headers=headers)
|
||||
@@ -70,16 +64,18 @@ class TestUserService:
|
||||
"""Test updating user profile"""
|
||||
# Register and login
|
||||
await client.post("/api/v1/register", json=user_data)
|
||||
login_response = await client.post("/api/v1/login", json={
|
||||
"email": user_data["email"],
|
||||
"password": user_data["password"]
|
||||
})
|
||||
login_response = await client.post(
|
||||
"/api/v1/login",
|
||||
json={"email": user_data["email"], "password": user_data["password"]},
|
||||
)
|
||||
token = login_response.json()["access_token"]
|
||||
|
||||
|
||||
# Update profile
|
||||
update_data = {"bio": "Updated bio text"}
|
||||
headers = {"Authorization": f"Bearer {token}"}
|
||||
response = await client.put("/api/v1/profile", json=update_data, headers=headers)
|
||||
response = await client.put(
|
||||
"/api/v1/profile", json=update_data, headers=headers
|
||||
)
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["bio"] == "Updated bio text"
|
||||
assert data["bio"] == "Updated bio text"
|
||||
|
||||
Reference in New Issue
Block a user