pipeline issues fix
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
2025-09-25 11:59:54 +09:00
parent dc50a9858e
commit 4e3768a6ee
39 changed files with 1297 additions and 739 deletions

View File

@@ -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",
}

View File

@@ -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()))

View File

@@ -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())

View File

@@ -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
View 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)

View File

@@ -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"