Files
2025-09-07 20:03:14 +08:00

337 lines
14 KiB
Python
Executable File

#!/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()