#!/usr/bin/env python3 import requests import json import time import random from datetime import datetime, timedelta from typing import Dict, List, Any, Optional import threading import statistics from concurrent.futures import ThreadPoolExecutor, as_completed import argparse from db_config import load_db_config class APITester: def __init__(self, base_url: str = "http://192.168.1.83:5001"): self.base_url = base_url self.db_config = load_db_config() self.results = [] def generate_health_data(self, device_sn: str) -> Dict[str, Any]: now = datetime.now() timestamp = now.strftime("%Y-%m-%d %H:%M:%S") return { "data": { "deviceSn": device_sn, "heart_rate": random.randint(60, 120), "blood_oxygen": random.randint(95, 100) if random.random() > 0.3 else 0, "body_temperature": f"{random.uniform(36.0, 37.5):.1f}", "step": random.randint(0, 15000), "distance": f"{random.uniform(0, 10):.1f}", "calorie": f"{random.uniform(0, 500):.1f}", "latitude": f"{random.uniform(22.5, 22.6):.6f}", "longitude": f"{random.uniform(114.0, 114.1):.6f}", "altitude": f"{random.uniform(0, 100):.1f}", "stress": random.randint(0, 100), "upload_method": random.choice(["wifi", "4g", "bluetooth"]), "blood_pressure_systolic": random.randint(110, 140), "blood_pressure_diastolic": random.randint(70, 90), "sleepData": "null", "exerciseDailyData": "null", "exerciseWeekData": "null", "scientificSleepData": "null", "workoutData": "null", "timestamp": timestamp } } def generate_device_info(self, device_sn: str) -> Dict[str, Any]: now = datetime.now() timestamp = now.strftime("%Y-%m-%d %H:%M:%S") return { "System Software Version": f"GLL-AL30BCN {random.randint(3,5)}.0.0.{random.randint(800,999)}", "Wifi Address": ":".join([f"{random.randint(0,255):02x}" for _ in range(6)]), "Bluetooth Address": ":".join([f"{random.randint(0,255):02X}" for _ in range(6)]), "IP Address": f"192.168.1.{random.randint(100, 254)}", "Network Access Mode": random.choice([1, 2, 3]), "SerialNumber": device_sn, "Device Name": f"HUAWEI WATCH B7-{random.randint(500,600)}-BF{random.randint(0,9)}", "IMEI": f"86615206{random.randint(10000000, 99999999)}", "batteryLevel": random.randint(10, 100), "voltage": random.randint(3500, 4500), "chargingStatus": random.choice(["NONE", "CHARGING", "FULL"]), "status": random.choice(["ACTIVE", "INACTIVE", "SLEEP"]), "timestamp": timestamp, "wearState": random.choice([0, 1]) } def generate_common_event(self, device_sn: str) -> Dict[str, Any]: now = datetime.now() timestamp = now.strftime("%Y-%m-%d %H:%M:%S") health_data = self.generate_health_data(device_sn) event_types = [ 'com.tdtech.ohos.action.WEAR_STATUS_CHANGED' ] return { 'eventType': random.choice(event_types), 'eventValue': str(random.choice([0, 1])), 'deviceSn': device_sn, 'latitude': round(random.uniform(22.5, 22.6), 6), 'longitude': round(random.uniform(114.0, 114.1), 6), 'altitude': random.randint(0, 100), 'timestamp': timestamp, 'healthData': health_data } def make_request(self, endpoint: str, data: Dict[str, Any], timeout: int = 30) -> Dict[str, Any]: url = f"{self.base_url}/{endpoint}" headers = { 'Content-Type': 'application/json', 'User-Agent': 'API-Tester/1.0' } start_time = time.time() try: response = requests.post(url, json=data, headers=headers, timeout=timeout) end_time = time.time() result = { 'endpoint': endpoint, 'status_code': response.status_code, 'response_time': round((end_time - start_time) * 1000, 2), 'success': response.status_code == 200, 'timestamp': datetime.now().isoformat(), 'request_size': len(json.dumps(data).encode('utf-8')), 'response_size': len(response.content) } try: result['response_data'] = response.json() except: result['response_data'] = response.text if response.status_code != 200: result['error'] = f"HTTP {response.status_code}: {response.text[:200]}" return result except requests.exceptions.Timeout: return { 'endpoint': endpoint, 'status_code': 0, 'response_time': timeout * 1000, 'success': False, 'error': 'Request timeout', 'timestamp': datetime.now().isoformat() } except Exception as e: return { 'endpoint': endpoint, 'status_code': 0, 'response_time': 0, 'success': False, 'error': str(e), 'timestamp': datetime.now().isoformat() } def test_single_api(self, endpoint: str, device_sn: str) -> Dict[str, Any]: if endpoint == 'upload_health_data': data = self.generate_health_data(device_sn) elif endpoint == 'upload_device_info': data = self.generate_device_info(device_sn) elif endpoint == 'upload_common_event': data = self.generate_common_event(device_sn) else: raise ValueError(f"Unknown endpoint: {endpoint}") return self.make_request(endpoint, data) def performance_test(self, endpoint: str, device_sns: List[str], concurrent_users: int = 5, requests_per_user: int = 10) -> List[Dict[str, Any]]: results = [] def worker(device_sn: str, request_count: int) -> List[Dict[str, Any]]: worker_results = [] for i in range(request_count): result = self.test_single_api(endpoint, device_sn) result['user_id'] = threading.current_thread().ident result['request_number'] = i + 1 worker_results.append(result) time.sleep(random.uniform(0.1, 0.5)) return worker_results with ThreadPoolExecutor(max_workers=concurrent_users) as executor: futures = [] for i in range(concurrent_users): device_sn = random.choice(device_sns) future = executor.submit(worker, device_sn, requests_per_user) futures.append(future) for future in as_completed(futures): results.extend(future.result()) return results def functional_test(self, device_sns: List[str]) -> Dict[str, List[Dict[str, Any]]]: endpoints = ['upload_health_data', 'upload_device_info', 'upload_common_event'] results = {} for endpoint in endpoints: print(f"Testing {endpoint}...") endpoint_results = [] for device_sn in device_sns[:5]: result = self.test_single_api(endpoint, device_sn) endpoint_results.append(result) time.sleep(0.1) results[endpoint] = endpoint_results return results def generate_report(self, results: Dict[str, Any], output_file: str = None): report = { 'test_summary': { 'timestamp': datetime.now().isoformat(), 'base_url': self.base_url, 'total_endpoints_tested': 0, 'total_requests': 0, 'success_rate': 0, 'average_response_time': 0 }, 'endpoint_details': {}, 'performance_metrics': {}, 'errors': [] } all_results = [] for endpoint, endpoint_results in results.items(): if isinstance(endpoint_results, list): all_results.extend(endpoint_results) successful_requests = [r for r in endpoint_results if r.get('success', False)] response_times = [r['response_time'] for r in endpoint_results if 'response_time' in r] endpoint_stats = { 'total_requests': len(endpoint_results), 'successful_requests': len(successful_requests), 'success_rate': (len(successful_requests) / len(endpoint_results)) * 100 if endpoint_results else 0, 'average_response_time': statistics.mean(response_times) if response_times else 0, 'min_response_time': min(response_times) if response_times else 0, 'max_response_time': max(response_times) if response_times else 0, 'median_response_time': statistics.median(response_times) if response_times else 0, 'errors': [r for r in endpoint_results if not r.get('success', False)] } report['endpoint_details'][endpoint] = endpoint_stats if all_results: successful_results = [r for r in all_results if r.get('success', False)] all_response_times = [r['response_time'] for r in all_results if 'response_time' in r] report['test_summary'].update({ 'total_endpoints_tested': len(results), 'total_requests': len(all_results), 'success_rate': (len(successful_results) / len(all_results)) * 100, 'average_response_time': statistics.mean(all_response_times) if all_response_times else 0 }) report_text = self.format_report(report) if output_file: with open(output_file, 'w', encoding='utf-8') as f: f.write(report_text) print(f"Report saved to: {output_file}") print(report_text) return report def format_report(self, report: Dict[str, Any]) -> str: lines = [] lines.append("=" * 80) lines.append("API 测试报告") lines.append("=" * 80) lines.append(f"测试时间: {report['test_summary']['timestamp']}") lines.append(f"测试地址: {report['test_summary']['base_url']}") lines.append(f"测试接口数: {report['test_summary']['total_endpoints_tested']}") lines.append(f"总请求数: {report['test_summary']['total_requests']}") lines.append(f"成功率: {report['test_summary']['success_rate']:.2f}%") lines.append(f"平均响应时间: {report['test_summary']['average_response_time']:.2f}ms") lines.append("") lines.append("接口详情:") lines.append("-" * 50) for endpoint, stats in report['endpoint_details'].items(): lines.append(f"\n{endpoint}:") lines.append(f" 总请求数: {stats['total_requests']}") lines.append(f" 成功请求数: {stats['successful_requests']}") lines.append(f" 成功率: {stats['success_rate']:.2f}%") lines.append(f" 平均响应时间: {stats['average_response_time']:.2f}ms") lines.append(f" 最小响应时间: {stats['min_response_time']:.2f}ms") lines.append(f" 最大响应时间: {stats['max_response_time']:.2f}ms") lines.append(f" 中位数响应时间: {stats['median_response_time']:.2f}ms") if stats['errors']: lines.append(f" 错误数: {len(stats['errors'])}") for i, error in enumerate(stats['errors'][:3]): lines.append(f" 错误 {i+1}: {error.get('error', 'Unknown error')}") return "\n".join(lines) def main(): parser = argparse.ArgumentParser(description='API Testing Tool') parser.add_argument('--url', default='http://192.168.1.83:5001', help='Base URL for API') parser.add_argument('--mode', choices=['functional', 'performance', 'both'], default='both', help='Test mode') parser.add_argument('--concurrent', type=int, default=5, help='Concurrent users for performance test') parser.add_argument('--requests', type=int, default=10, help='Requests per user for performance test') parser.add_argument('--output', help='Output file for report') args = parser.parse_args() tester = APITester(args.url) if not tester.db_config.connect(): print("数据库连接失败,请检查配置") return devices = tester.db_config.get_devices(50) if not devices: print("未找到设备数据,请检查数据库") return device_sns = [device['device_sn'] for device in devices] print(f"从数据库获取到 {len(device_sns)} 个设备序列号") results = {} if args.mode in ['functional', 'both']: print("\n开始功能测试...") functional_results = tester.functional_test(device_sns) results.update(functional_results) if args.mode in ['performance', 'both']: print(f"\n开始性能测试 (并发用户: {args.concurrent}, 每用户请求: {args.requests})...") endpoints = ['upload_health_data', 'upload_device_info', 'upload_common_event'] for endpoint in endpoints: print(f"Testing {endpoint} performance...") perf_results = tester.performance_test(endpoint, device_sns, args.concurrent, args.requests) results[f"{endpoint}_performance"] = perf_results if args.output: output_file = args.output else: timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") output_file = f"api_test_report_{timestamp}.txt" tester.generate_report(results, output_file) tester.db_config.disconnect() if __name__ == "__main__": main()