import os from typing import Dict, Optional, Any from dataclasses import dataclass import pytest import requests from uuid import UUID from pydantic import BaseModel from _pytest.terminal import TerminalReporter # Configuration @dataclass class TestConfig: """Test configuration settings""" base_url: str timeout: int = 30 verify_ssl: bool = True # Load config from environment or use defaults config = TestConfig( base_url=os.getenv("API_BASE_URL", "http://localhost:8000/v1") ) # API Response Types class UserResponse(BaseModel): user_id: str api_key: str class AgentResponse(BaseModel): agent_id: UUID class MetricsResponse(BaseModel): total_completions: int average_response_time: float error_rate: float last_24h_completions: int total_tokens_used: int uptime_percentage: float success_rate: float peak_tokens_per_minute: int class APIClient: """API Client with typed methods""" def __init__(self, config: TestConfig): self.config = config self.session = requests.Session() def _url(self, path: str) -> str: """Construct full URL""" return f"{self.config.base_url}/{path.lstrip('/')}" def _request( self, method: str, path: str, headers: Optional[Dict] = None, **kwargs: Any, ) -> requests.Response: """Make HTTP request with config defaults""" url = self._url(path) return self.session.request( method=method, url=url, headers=headers, timeout=self.config.timeout, verify=self.config.verify_ssl, **kwargs, ) def create_user(self, username: str) -> UserResponse: """Create a new user""" response = self._request( "POST", "/users", json={"username": username} ) response.raise_for_status() return UserResponse(**response.json()) def create_agent( self, agent_config: Dict[str, Any], api_key: str ) -> AgentResponse: """Create a new agent""" headers = {"api-key": api_key} response = self._request( "POST", "/agent", headers=headers, json=agent_config ) response.raise_for_status() return AgentResponse(**response.json()) def get_metrics( self, agent_id: UUID, api_key: str ) -> MetricsResponse: """Get agent metrics""" headers = {"api-key": api_key} response = self._request( "GET", f"/agent/{agent_id}/metrics", headers=headers ) response.raise_for_status() return MetricsResponse(**response.json()) # Test Fixtures @pytest.fixture def api_client() -> APIClient: """Fixture for API client""" return APIClient(config) @pytest.fixture def test_user(api_client: APIClient) -> UserResponse: """Fixture for test user""" return api_client.create_user("test_user") @pytest.fixture def test_agent( api_client: APIClient, test_user: UserResponse ) -> AgentResponse: """Fixture for test agent""" agent_config = { "agent_name": "test_agent", "model_name": "gpt-4", "system_prompt": "You are a test agent", "description": "Test agent description", } return api_client.create_agent(agent_config, test_user.api_key) # Tests def test_user_creation(api_client: APIClient): """Test user creation flow""" response = api_client.create_user("new_test_user") assert response.user_id assert response.api_key def test_agent_creation( api_client: APIClient, test_user: UserResponse ): """Test agent creation flow""" agent_config = { "agent_name": "test_agent", "model_name": "gpt-4", "system_prompt": "You are a test agent", "description": "Test agent description", } response = api_client.create_agent( agent_config, test_user.api_key ) assert response.agent_id def test_agent_metrics( api_client: APIClient, test_user: UserResponse, test_agent: AgentResponse, ): """Test metrics retrieval""" metrics = api_client.get_metrics( test_agent.agent_id, test_user.api_key ) assert metrics.total_completions >= 0 assert metrics.error_rate >= 0 assert metrics.uptime_percentage >= 0 def test_invalid_auth(api_client: APIClient): """Test invalid authentication""" with pytest.raises(requests.exceptions.HTTPError) as exc_info: api_client.create_agent({}, "invalid_key") assert exc_info.value.response.status_code == 401 # Custom pytest plugin to capture test results class ResultCapture: def __init__(self): self.total = 0 self.passed = 0 self.failed = 0 self.errors = 0 @pytest.hookimpl(hookwrapper=True) def pytest_terminal_summary( terminalreporter: TerminalReporter, exitstatus: int ): yield capture = getattr( terminalreporter.config, "_result_capture", None ) if capture: capture.total = ( len(terminalreporter.stats.get("passed", [])) + len(terminalreporter.stats.get("failed", [])) + len(terminalreporter.stats.get("error", [])) ) capture.passed = len(terminalreporter.stats.get("passed", [])) capture.failed = len(terminalreporter.stats.get("failed", [])) capture.errors = len(terminalreporter.stats.get("error", [])) @dataclass class TestReport: total_tests: int passed: int failed: int errors: int @property def success_rate(self) -> float: return ( (self.passed / self.total_tests) * 100 if self.total_tests > 0 else 0 ) def run_tests() -> TestReport: """Run tests and generate typed report""" # Create result capture capture = ResultCapture() # Create pytest configuration args = [__file__, "-v"] # Run pytest with our plugin pytest.main(args, plugins=[capture]) # Generate report return TestReport( total_tests=capture.total, passed=capture.passed, failed=capture.failed, errors=capture.errors, ) if __name__ == "__main__": # Example usage with environment variable # export API_BASE_URL=http://api.example.com/v1 report = run_tests() print("\nTest Results:") print(f"Total Tests: {report.total_tests}") print(f"Passed: {report.passed}") print(f"Failed: {report.failed}") print(f"Errors: {report.errors}") print(f"Success Rate: {report.success_rate:.2f}%")