Spaces:
Running
Running
import asyncio | |
import aiohttp | |
import time | |
import argparse | |
from faker import Faker | |
class AuthClient: | |
""" | |
Asynchronous Python client for interacting with the Authentication API | |
""" | |
def __init__(self, base_url="http://localhost:7860/api"): | |
""" | |
Initialize the client with the API base URL | |
Args: | |
base_url (str): The base URL of the API | |
""" | |
self.base_url = base_url | |
self.token = None | |
self.session = None | |
async def __aenter__(self): | |
"""Create and enter an aiohttp session""" | |
self.session = aiohttp.ClientSession() | |
return self | |
async def __aexit__(self, exc_type, exc_val, exc_tb): | |
"""Close the aiohttp session""" | |
if self.session: | |
await self.session.close() | |
async def _get_session(self): | |
"""Get or create an aiohttp session""" | |
if self.session is None: | |
self.session = aiohttp.ClientSession() | |
return self.session | |
async def register(self, email, password): | |
""" | |
Register a new user | |
Args: | |
email (str): User's email | |
password (str): User's password (should be at least 8 characters) | |
Returns: | |
dict: The user data returned by the API | |
Raises: | |
Exception: If registration fails | |
""" | |
url = f"{self.base_url}/register" | |
data = { | |
"email": email, | |
"password": password | |
} | |
session = await self._get_session() | |
async with session.post(url, json=data) as response: | |
if response.status == 201: | |
return await response.json() | |
else: | |
error_data = await response.json() | |
error_detail = error_data.get("detail", "Unknown error") | |
raise Exception(f"Registration failed: {error_detail} (Status: {response.status})") | |
async def login(self, email, password): | |
""" | |
Login to obtain an authentication token | |
Args: | |
email (str): User's email | |
password (str): User's password | |
Returns: | |
dict: The token data returned by the API | |
Raises: | |
Exception: If login fails | |
""" | |
url = f"{self.base_url}/login" | |
data = { | |
"email": email, | |
"password": password | |
} | |
session = await self._get_session() | |
async with session.post(url, json=data) as response: | |
if response.status == 200: | |
token_data = await response.json() | |
self.token = token_data["access_token"] | |
return token_data | |
else: | |
error_data = await response.json() | |
error_detail = error_data.get("detail", "Unknown error") | |
raise Exception(f"Login failed: {error_detail} (Status: {response.status})") | |
async def get_current_user(self): | |
""" | |
Get information about the current logged-in user | |
Returns: | |
dict: The user data returned by the API | |
Raises: | |
Exception: If not authenticated or request fails | |
""" | |
if not self.token: | |
raise Exception("Not authenticated. Please login first.") | |
url = f"{self.base_url}/users/me" | |
headers = {"Authorization": f"Bearer {self.token}"} | |
session = await self._get_session() | |
async with session.get(url, headers=headers) as response: | |
if response.status == 200: | |
return await response.json() | |
else: | |
error_data = await response.json() | |
error_detail = error_data.get("detail", "Unknown error") | |
raise Exception(f"Failed to get user info: {error_detail} (Status: {response.status})") | |
def logout(self): | |
"""Clear the authentication token""" | |
self.token = None | |
async def load_test(num_users=10, concurrency=5, base_url="http://localhost:7860/api"): | |
""" | |
Run a load test with multiple simulated users | |
Args: | |
num_users (int): Total number of users to simulate | |
concurrency (int): Number of concurrent users | |
base_url (str): The base URL of the API | |
""" | |
fake = Faker() | |
start_time = time.time() | |
completed = 0 | |
success_count = 0 | |
failure_count = 0 | |
# Semaphore to limit concurrency | |
sem = asyncio.Semaphore(concurrency) | |
# For progress tracking | |
progress_lock = asyncio.Lock() | |
async def run_single_user(): | |
nonlocal completed, success_count, failure_count | |
async with sem: # This limits concurrency | |
async with AuthClient(base_url) as client: | |
try: | |
# Generate random user data | |
email = fake.email() | |
password = fake.password(length=12, special_chars=True, digits=True, | |
upper_case=True, lower_case=True) | |
# Complete user flow | |
await client.register(email, password) | |
await client.login(email, password) | |
await client.get_current_user() | |
client.logout() | |
async with progress_lock: | |
completed += 1 | |
success_count += 1 | |
# Print progress | |
print(f"Progress: {completed}/{num_users} users completed", end="\r") | |
except Exception as e: | |
async with progress_lock: | |
completed += 1 | |
failure_count += 1 | |
print(f"Error: {e}") | |
print(f"Progress: {completed}/{num_users} users completed", end="\r") | |
# Create all tasks | |
tasks = [run_single_user() for _ in range(num_users)] | |
# Display start message | |
print(f"Starting load test with {num_users} users (max {concurrency} concurrent)...") | |
# Run all tasks | |
await asyncio.gather(*tasks) | |
# Calculate stats | |
end_time = time.time() | |
duration = end_time - start_time | |
# Display results | |
print("\n\n--- Load Test Results ---") | |
print(f"Total users: {num_users}") | |
print(f"Concurrency level: {concurrency}") | |
print(f"Successful flows: {success_count} ({success_count/num_users*100:.1f}%)") | |
print(f"Failed flows: {failure_count} ({failure_count/num_users*100:.1f}%)") | |
print(f"Total duration: {duration:.2f} seconds") | |
if success_count > 0: | |
print(f"Average time per successful user: {duration/success_count:.2f} seconds") | |
print(f"Requests per second: {success_count/duration:.2f}") | |
async def single_user_test(base_url): | |
"""Run a test with a single user""" | |
fake = Faker() | |
async with AuthClient(base_url) as client: | |
# Generate random user data | |
first_name = fake.first_name() | |
last_name = fake.last_name() | |
email = fake.email() | |
password = fake.password(length=12, special_chars=True, digits=True, upper_case=True, lower_case=True) | |
try: | |
# Register a new user | |
print(f"Registering a new user: {first_name} {last_name}...") | |
user = await client.register(email, password) | |
print(f"Registered user: {user}") | |
# Login | |
print("\nLogging in...") | |
token_data = await client.login(email, password) | |
print(f"Login successful, token: {token_data['access_token'][:10]}...") | |
# Get current user | |
print("\nGetting current user info...") | |
user_info = await client.get_current_user() | |
print(f"Current user: {user_info}") | |
# Logout | |
print("\nLogging out...") | |
client.logout() | |
print("Logged out successfully") | |
except Exception as e: | |
print(f"Error: {e}") | |
async def custom_user_test(email, password, base_url): | |
"""Test with specific user credentials""" | |
async with AuthClient(base_url) as client: | |
try: | |
# Try to login first (for existing users) | |
print(f"\nAttempting to login with email: {email}...") | |
try: | |
token_data = await client.login(email, password) | |
print(f"Login successful, token: {token_data['access_token'][:10]}...") | |
except Exception as e: | |
print(f"Login failed: {e}") | |
# If login fails, try to register | |
print(f"\nAttempting to register with email: {email}...") | |
user = await client.register(email, password) | |
print(f"Registered user: {user}") | |
# Now try login again after registration | |
print("\nLogging in after registration...") | |
token_data = await client.login(email, password) | |
print(f"Login successful, token: {token_data['access_token'][:10]}...") | |
# Get current user | |
print("\nGetting current user info...") | |
user_info = await client.get_current_user() | |
print(f"Current user: {user_info}") | |
# Logout | |
print("\nLogging out...") | |
client.logout() | |
print("Logged out successfully") | |
except Exception as e: | |
print(f"Error: {e}") | |
async def main(): | |
# Define command-line arguments | |
parser = argparse.ArgumentParser(description='Authentication API Client CLI') | |
# Main command options | |
parser.add_argument('--url', type=str, default="http://localhost:7860/api", | |
help='Base URL for the API (default: http://localhost:7860/api)') | |
parser.add_argument('--remote', action='store_true', | |
help='Use the remote API endpoint') | |
# Create subparsers for different commands | |
subparsers = parser.add_subparsers(dest='command', help='Command to execute') | |
# Single user test command | |
single_parser = subparsers.add_parser('single', help='Run a test with a single random user') | |
# Load test command | |
load_parser = subparsers.add_parser('load', help='Run a load test with multiple users') | |
load_parser.add_argument('--users', type=int, default=100, | |
help='Number of users to simulate (default: 10)') | |
load_parser.add_argument('--concurrency', type=int, default=10, | |
help='Maximum number of concurrent users (default: 5)') | |
# Custom user test command | |
custom_parser = subparsers.add_parser('custom', help='Test with specific user credentials') | |
custom_parser.add_argument('--email', type=str, required=True, help='User email') | |
custom_parser.add_argument('--password', type=str, required=True, help='User password') | |
# Parse arguments | |
args = parser.parse_args() | |
# Set remote URL if specified | |
if args.remote: | |
base_url = "https://amaye15-authenticationapp.hf.space/api" | |
print(f"Using remote API at: {base_url}") | |
else: | |
base_url = args.url | |
print(f"Using API at: {base_url}") | |
# Execute the appropriate command | |
if args.command == 'single': | |
await single_user_test(base_url) | |
elif args.command == 'load': | |
await load_test(args.users, args.concurrency, base_url) | |
elif args.command == 'custom': | |
await custom_user_test(args.email, args.password, base_url) | |
else: | |
# If no command specified, show help | |
parser.print_help() | |
if __name__ == "__main__": | |
asyncio.run(main()) |