Spaces:
Runtime error
Runtime error
import asyncio | |
import json | |
from datetime import datetime | |
from typing import Any, Dict, List, Optional | |
from uuid import UUID | |
import httpx | |
from loguru import logger | |
# Configure logger | |
logger.add( | |
"tests/api_test_{time}.log", | |
rotation="1 day", | |
retention="7 days", | |
level="DEBUG", | |
format="{time:YYYY-MM-DD HH:mm:ss} | {level} | {message}", | |
) | |
class TestConfig: | |
"""Test configuration and utilities""" | |
BASE_URL: str = "http://localhost:8000/v1" | |
TEST_USERNAME: str = "test_user" | |
api_key: Optional[str] = None | |
user_id: Optional[UUID] = None | |
test_agent_id: Optional[UUID] = None | |
class TestResult: | |
"""Model for test results""" | |
def __init__( | |
self, | |
test_name: str, | |
status: str, | |
duration: float, | |
error: Optional[str] = None, | |
details: Optional[Dict[str, Any]] = None, | |
): | |
self.test_name = test_name | |
self.status = status | |
self.duration = duration | |
self.error = error | |
self.details = details or {} | |
def dict(self): | |
return { | |
"test_name": self.test_name, | |
"status": self.status, | |
"duration": self.duration, | |
"error": self.error, | |
"details": self.details, | |
} | |
async def log_response( | |
response: httpx.Response, test_name: str | |
) -> None: | |
"""Log API response details""" | |
logger.debug(f"\n{test_name} Response:") | |
logger.debug(f"Status Code: {response.status_code}") | |
logger.debug(f"Headers: {dict(response.headers)}") | |
try: | |
logger.debug(f"Body: {response.json()}") | |
except json.JSONDecodeError: | |
logger.debug(f"Body: {response.text}") | |
async def create_test_user() -> TestResult: | |
"""Create a test user and get API key""" | |
start_time = datetime.now() | |
try: | |
async with httpx.AsyncClient() as client: | |
response = await client.post( | |
f"{TestConfig.BASE_URL}/users", | |
json={"username": TestConfig.TEST_USERNAME}, | |
) | |
await log_response(response, "Create User") | |
if response.status_code == 200: | |
data = response.json() | |
TestConfig.api_key = data["api_key"] | |
TestConfig.user_id = UUID(data["user_id"]) | |
return TestResult( | |
test_name="create_test_user", | |
status="passed", | |
duration=( | |
datetime.now() - start_time | |
).total_seconds(), | |
details={"user_id": str(TestConfig.user_id)}, | |
) | |
else: | |
return TestResult( | |
test_name="create_test_user", | |
status="failed", | |
duration=( | |
datetime.now() - start_time | |
).total_seconds(), | |
error=f"Failed to create user: {response.text}", | |
) | |
except Exception as e: | |
logger.error(f"Error in create_test_user: {str(e)}") | |
return TestResult( | |
test_name="create_test_user", | |
status="error", | |
duration=(datetime.now() - start_time).total_seconds(), | |
error=str(e), | |
) | |
async def create_test_agent() -> TestResult: | |
"""Create a test agent""" | |
start_time = datetime.now() | |
try: | |
# Create agent config according to the AgentConfig model | |
agent_config = { | |
"agent_name": "test_agent", | |
"model_name": "gpt-4", | |
"description": "Test agent for API testing", | |
"system_prompt": "You are a test agent.", | |
"temperature": 0.1, | |
"max_loops": 1, | |
"dynamic_temperature_enabled": True, | |
"user_name": TestConfig.TEST_USERNAME, | |
"retry_attempts": 1, | |
"context_length": 4000, | |
"output_type": "string", | |
"streaming_on": False, | |
"tags": ["test", "api"], | |
"stopping_token": "<DONE>", | |
"auto_generate_prompt": False, | |
} | |
async with httpx.AsyncClient() as client: | |
response = await client.post( | |
f"{TestConfig.BASE_URL}/agent", | |
json=agent_config, | |
headers={"api-key": TestConfig.api_key}, | |
) | |
await log_response(response, "Create Agent") | |
if response.status_code == 200: | |
data = response.json() | |
TestConfig.test_agent_id = UUID(data["agent_id"]) | |
return TestResult( | |
test_name="create_test_agent", | |
status="passed", | |
duration=( | |
datetime.now() - start_time | |
).total_seconds(), | |
details={ | |
"agent_id": str(TestConfig.test_agent_id) | |
}, | |
) | |
else: | |
return TestResult( | |
test_name="create_test_agent", | |
status="failed", | |
duration=( | |
datetime.now() - start_time | |
).total_seconds(), | |
error=f"Failed to create agent: {response.text}", | |
) | |
except Exception as e: | |
logger.error(f"Error in create_test_agent: {str(e)}") | |
return TestResult( | |
test_name="create_test_agent", | |
status="error", | |
duration=(datetime.now() - start_time).total_seconds(), | |
error=str(e), | |
) | |
async def test_agent_completion() -> TestResult: | |
"""Test agent completion endpoint""" | |
start_time = datetime.now() | |
try: | |
completion_request = { | |
"prompt": "Hello, this is a test prompt.", | |
"agent_id": str(TestConfig.test_agent_id), | |
"max_tokens": 100, | |
"temperature_override": 0.5, | |
"stream": False, | |
} | |
async with httpx.AsyncClient() as client: | |
response = await client.post( | |
f"{TestConfig.BASE_URL}/agent/completions", | |
json=completion_request, | |
headers={"api-key": TestConfig.api_key}, | |
) | |
await log_response(response, "Agent Completion") | |
if response.status_code == 200: | |
return TestResult( | |
test_name="test_agent_completion", | |
status="passed", | |
duration=( | |
datetime.now() - start_time | |
).total_seconds(), | |
details={"response": response.json()}, | |
) | |
else: | |
return TestResult( | |
test_name="test_agent_completion", | |
status="failed", | |
duration=( | |
datetime.now() - start_time | |
).total_seconds(), | |
error=f"Failed completion test: {response.text}", | |
) | |
except Exception as e: | |
logger.error(f"Error in test_agent_completion: {str(e)}") | |
return TestResult( | |
test_name="test_agent_completion", | |
status="error", | |
duration=(datetime.now() - start_time).total_seconds(), | |
error=str(e), | |
) | |
async def test_agent_metrics() -> TestResult: | |
"""Test agent metrics endpoint""" | |
start_time = datetime.now() | |
try: | |
if not TestConfig.test_agent_id: | |
return TestResult( | |
test_name="test_agent_metrics", | |
status="failed", | |
duration=( | |
datetime.now() - start_time | |
).total_seconds(), | |
error="No test agent ID available", | |
) | |
async with httpx.AsyncClient() as client: | |
response = await client.get( | |
f"{TestConfig.BASE_URL}/agent/{str(TestConfig.test_agent_id)}/metrics", | |
headers={"api-key": TestConfig.api_key}, | |
) | |
await log_response(response, "Agent Metrics") | |
if response.status_code == 200: | |
return TestResult( | |
test_name="test_agent_metrics", | |
status="passed", | |
duration=( | |
datetime.now() - start_time | |
).total_seconds(), | |
details={"metrics": response.json()}, | |
) | |
else: | |
return TestResult( | |
test_name="test_agent_metrics", | |
status="failed", | |
duration=( | |
datetime.now() - start_time | |
).total_seconds(), | |
error=f"Failed metrics test: {response.text}", | |
) | |
except Exception as e: | |
logger.error(f"Error in test_agent_metrics: {str(e)}") | |
return TestResult( | |
test_name="test_agent_metrics", | |
status="error", | |
duration=(datetime.now() - start_time).total_seconds(), | |
error=str(e), | |
) | |
async def test_update_agent() -> TestResult: | |
"""Test agent update endpoint""" | |
start_time = datetime.now() | |
try: | |
if not TestConfig.test_agent_id: | |
return TestResult( | |
test_name="test_update_agent", | |
status="failed", | |
duration=( | |
datetime.now() - start_time | |
).total_seconds(), | |
error="No test agent ID available", | |
) | |
update_data = { | |
"description": "Updated test agent description", | |
"tags": ["test", "updated"], | |
"max_loops": 2, | |
} | |
async with httpx.AsyncClient() as client: | |
response = await client.patch( | |
f"{TestConfig.BASE_URL}/agent/{str(TestConfig.test_agent_id)}", | |
json=update_data, | |
headers={"api-key": TestConfig.api_key}, | |
) | |
await log_response(response, "Update Agent") | |
if response.status_code == 200: | |
return TestResult( | |
test_name="test_update_agent", | |
status="passed", | |
duration=( | |
datetime.now() - start_time | |
).total_seconds(), | |
details={"update_response": response.json()}, | |
) | |
else: | |
return TestResult( | |
test_name="test_update_agent", | |
status="failed", | |
duration=( | |
datetime.now() - start_time | |
).total_seconds(), | |
error=f"Failed update test: {response.text}", | |
) | |
except Exception as e: | |
logger.error(f"Error in test_update_agent: {str(e)}") | |
return TestResult( | |
test_name="test_update_agent", | |
status="error", | |
duration=(datetime.now() - start_time).total_seconds(), | |
error=str(e), | |
) | |
async def test_error_handling() -> TestResult: | |
"""Test API error handling""" | |
start_time = datetime.now() | |
try: | |
async with httpx.AsyncClient() as client: | |
# Test with invalid API key | |
invalid_agent_id = "00000000-0000-0000-0000-000000000000" | |
response = await client.get( | |
f"{TestConfig.BASE_URL}/agent/{invalid_agent_id}/metrics", | |
headers={"api-key": "invalid_key"}, | |
) | |
await log_response(response, "Invalid API Key Test") | |
if response.status_code in [401, 403]: | |
return TestResult( | |
test_name="test_error_handling", | |
status="passed", | |
duration=( | |
datetime.now() - start_time | |
).total_seconds(), | |
details={"error_response": response.json()}, | |
) | |
else: | |
return TestResult( | |
test_name="test_error_handling", | |
status="failed", | |
duration=( | |
datetime.now() - start_time | |
).total_seconds(), | |
error="Error handling test failed", | |
) | |
except Exception as e: | |
logger.error(f"Error in test_error_handling: {str(e)}") | |
return TestResult( | |
test_name="test_error_handling", | |
status="error", | |
duration=(datetime.now() - start_time).total_seconds(), | |
error=str(e), | |
) | |
async def cleanup_test_resources() -> TestResult: | |
"""Clean up test resources""" | |
start_time = datetime.now() | |
try: | |
if TestConfig.test_agent_id: | |
async with httpx.AsyncClient() as client: | |
response = await client.delete( | |
f"{TestConfig.BASE_URL}/agent/{str(TestConfig.test_agent_id)}", | |
headers={"api-key": TestConfig.api_key}, | |
) | |
await log_response(response, "Delete Agent") | |
return TestResult( | |
test_name="cleanup_test_resources", | |
status="passed", | |
duration=(datetime.now() - start_time).total_seconds(), | |
details={"cleanup": "completed"}, | |
) | |
except Exception as e: | |
logger.error(f"Error in cleanup_test_resources: {str(e)}") | |
return TestResult( | |
test_name="cleanup_test_resources", | |
status="error", | |
duration=(datetime.now() - start_time).total_seconds(), | |
error=str(e), | |
) | |
async def run_all_tests() -> List[TestResult]: | |
"""Run all tests in sequence""" | |
logger.info("Starting API test suite") | |
results = [] | |
# Initialize | |
results.append(await create_test_user()) | |
if results[-1].status != "passed": | |
logger.error( | |
"Failed to create test user, aborting remaining tests" | |
) | |
return results | |
# Add delay to ensure user is properly created | |
await asyncio.sleep(1) | |
# Core tests | |
test_functions = [ | |
create_test_agent, | |
test_agent_completion, | |
test_agent_metrics, | |
test_update_agent, | |
test_error_handling, | |
] | |
for test_func in test_functions: | |
result = await test_func() | |
results.append(result) | |
logger.info(f"Test {result.test_name}: {result.status}") | |
if result.error: | |
logger.error( | |
f"Error in {result.test_name}: {result.error}" | |
) | |
# Add small delay between tests | |
await asyncio.sleep(0.5) | |
# Cleanup | |
results.append(await cleanup_test_resources()) | |
# Log summary | |
passed = sum(1 for r in results if r.status == "passed") | |
failed = sum(1 for r in results if r.status == "failed") | |
errors = sum(1 for r in results if r.status == "error") | |
logger.info("\nTest Summary:") | |
logger.info(f"Total Tests: {len(results)}") | |
logger.info(f"Passed: {passed}") | |
logger.info(f"Failed: {failed}") | |
logger.info(f"Errors: {errors}") | |
return results | |
def main(): | |
"""Main entry point for running tests""" | |
logger.info("Starting API testing suite") | |
try: | |
results = asyncio.run(run_all_tests()) | |
# Write results to JSON file | |
with open("test_results.json", "w") as f: | |
json.dump( | |
[result.dict() for result in results], | |
f, | |
indent=2, | |
default=str, | |
) | |
logger.info("Test results written to test_results.json") | |
except Exception: | |
logger.error("Fatal error in test suite: ") | |
main() | |