#!/usr/bin/env python3 """ 城市手册 - API 自动化测试脚本 测试所有已实现的功能 """ import requests import sys import json BASE_URL = 'http://127.0.0.1:81/api' FRONTEND_URL = 'http://127.0.0.1:81' # 颜色输出 GREEN = '\033[92m' RED = '\033[91m' YELLOW = '\033[93m' RESET = '\033[0m' def log_success(msg): print(f"{GREEN}✓{RESET} {msg}") def log_error(msg): print(f"{RED}✗{RESET} {msg}") def log_info(msg): print(f"{YELLOW}→{RESET} {msg}") def test_frontend_pages(): """测试前端页面可访问性""" log_info("\n📄 测试前端页面...") pages = [ ('/', '首页'), ('/login/', '登录页'), ('/register/', '注册页'), ('/cities/', '城市列表页'), ] passed = 0 for path, name in pages: try: resp = requests.get(f"{FRONTEND_URL}{path}", timeout=5) if resp.status_code == 200: log_success(f"{name} ({path}) - {resp.status_code}") passed += 1 else: log_error(f"{name} ({path}) - {resp.status_code}") except Exception as e: log_error(f"{name} ({path}) - {str(e)}") return passed, len(pages) def test_user_auth(): """测试用户认证系统""" log_info("\n🔐 测试用户系统...") passed = 0 total = 4 # 1. 测试登录 try: resp = requests.post(f"{BASE_URL}/token/", json={ 'username': 'demo', 'password': 'demo123' }, timeout=5) if resp.status_code == 200 and 'access' in resp.json(): token = resp.json()['access'] log_success(f"用户登录 - {resp.status_code}") passed += 1 else: log_error(f"用户登录 - {resp.status_code}") except Exception as e: log_error(f"用户登录 - {str(e)}") token = None # 2. 测试获取当前用户信息 if token: try: resp = requests.get(f"{BASE_URL}/users/me/", headers={'Authorization': f'Bearer {token}'}, timeout=5) if resp.status_code == 200: user = resp.json() log_success(f"获取用户信息 - {resp.status_code} (用户名:{user.get('username', 'N/A')})") passed += 1 else: log_error(f"获取用户信息 - {resp.status_code}") except Exception as e: log_error(f"获取用户信息 - {str(e)}") # 3. 测试错误密码登录 try: resp = requests.post(f"{BASE_URL}/token/", json={ 'username': 'demo', 'password': 'wrongpassword' }, timeout=5) if resp.status_code == 401: log_success(f"错误密码拒绝 - {resp.status_code}") passed += 1 else: log_error(f"错误密码拒绝 - {resp.status_code} (应该返回 401)") except Exception as e: log_error(f"错误密码拒绝 - {str(e)}") # 4. 测试 token 刷新端点存在 try: # 只需要测试端点存在,不实际刷新(因为需要有效的 refresh token) resp = requests.options(f"{BASE_URL}/token/refresh/", timeout=5) # OPTIONS 请求应该返回 200 或 401(需要认证) if resp.status_code in [200, 401, 403]: log_success(f"Token 刷新端点 - 可用") passed += 1 else: log_error(f"Token 刷新端点 - {resp.status_code}") except Exception as e: log_error(f"Token 刷新端点 - {str(e)}") return passed, total def test_regions(): """测试区域系统""" log_info("\n🗺️ 测试区域系统...") passed = 0 total = 4 # 1. 获取区域列表 try: resp = requests.get(f"{BASE_URL}/regions/", timeout=5) if resp.status_code == 200: data = resp.json() count = data.get('count', 0) log_success(f"区域列表 - {resp.status_code} (共{count}个区域)") passed += 1 else: log_error(f"区域列表 - {resp.status_code}") except Exception as e: log_error(f"区域列表 - {str(e)}") # 2. 获取省级区域 try: resp = requests.get(f"{BASE_URL}/regions/provinces/", timeout=5) if resp.status_code == 200: data = resp.json() # API 可能返回 list 或 paginated response if isinstance(data, list): count = len(data) else: count = data.get('count', len(data)) log_success(f"省级区域 - {resp.status_code} (共{count}个省)") passed += 1 else: log_error(f"省级区域 - {resp.status_code}") except Exception as e: log_error(f"省级区域 - {str(e)}") # 3. 获取区域详情(北京) try: resp = requests.get(f"{BASE_URL}/regions/1/", timeout=5) if resp.status_code == 200: data = resp.json() log_success(f"区域详情 - {resp.status_code} ({data.get('name', 'N/A')})") passed += 1 else: log_error(f"区域详情 - {resp.status_code}") except Exception as e: log_error(f"区域详情 - {str(e)}") # 4. 获取子区域 try: resp = requests.get(f"{BASE_URL}/regions/3/children/", timeout=5) if resp.status_code == 200: data = resp.json() # API 可能返回 list 或 paginated response if isinstance(data, list): count = len(data) else: count = data.get('count', len(data)) log_success(f"子区域 - {resp.status_code} (广东省有{count}个子区域)") passed += 1 else: log_error(f"子区域 - {resp.status_code}") except Exception as e: log_error(f"子区域 - {str(e)}") return passed, total def test_articles(): """测试文章系统""" log_info("\n📝 测试文章系统...") passed = 0 total = 2 # 1. 获取文章列表 try: resp = requests.get(f"{BASE_URL}/articles/", timeout=5) if resp.status_code == 200: data = resp.json() count = data.get('count', 0) log_success(f"文章列表 - {resp.status_code} (共{count}篇文章)") passed += 1 else: log_error(f"文章列表 - {resp.status_code}") except Exception as e: log_error(f"文章列表 - {str(e)}") # 2. 按区域筛选文章 try: resp = requests.get(f"{BASE_URL}/articles/?region=1", timeout=5) if resp.status_code == 200: data = resp.json() count = data.get('count', 0) log_success(f"按区域筛选文章 - {resp.status_code} (北京有{count}篇文章)") passed += 1 else: log_error(f"按区域筛选文章 - {resp.status_code}") except Exception as e: log_error(f"按区域筛选文章 - {str(e)}") return passed, total def test_services(): """测试特色服务系统""" log_info("\n🏪 测试特色服务...") passed = 0 total = 2 # 1. 获取服务列表 try: resp = requests.get(f"{BASE_URL}/services/", timeout=5) if resp.status_code == 200: data = resp.json() count = data.get('count', 0) log_success(f"服务列表 - {resp.status_code} (共{count}个服务)") passed += 1 else: log_error(f"服务列表 - {resp.status_code}") except Exception as e: log_error(f"服务列表 - {str(e)}") # 2. 按区域筛选服务 try: resp = requests.get(f"{BASE_URL}/services/?region=1", timeout=5) if resp.status_code == 200: data = resp.json() count = data.get('count', 0) log_success(f"按区域筛选服务 - {resp.status_code} (北京有{count}个服务)") passed += 1 else: log_error(f"按区域筛选服务 - {resp.status_code}") except Exception as e: log_error(f"按区域筛选服务 - {str(e)}") return passed, total def test_comments_ratings(): """测试评论和评分系统""" log_info("\n💬 测试评论和评分...") passed = 0 total = 2 # 1. 获取评论列表 try: resp = requests.get(f"{BASE_URL}/comments/", timeout=5) if resp.status_code == 200: log_success(f"评论列表 - {resp.status_code}") passed += 1 else: log_error(f"评论列表 - {resp.status_code}") except Exception as e: log_error(f"评论列表 - {str(e)}") # 2. 获取评分列表 try: resp = requests.get(f"{BASE_URL}/ratings/", timeout=5) if resp.status_code == 200: log_success(f"评分列表 - {resp.status_code}") passed += 1 else: log_error(f"评分列表 - {resp.status_code}") except Exception as e: log_error(f"评分列表 - {str(e)}") return passed, total def test_admin(): """测试 Admin 后台""" log_info("\n⚙️ 测试 Admin 后台...") try: resp = requests.get(f"{FRONTEND_URL}/admin/", timeout=5, allow_redirects=False) if resp.status_code in [200, 302]: log_success(f"Admin 后台 - {resp.status_code}") return 1, 1 else: log_error(f"Admin 后台 - {resp.status_code}") return 0, 1 except Exception as e: log_error(f"Admin 后台 - {str(e)}") return 0, 1 def main(): print("\n" + "="*60) print("📖 城市手册 - API 自动化测试") print("="*60) total_passed = 0 total_tests = 0 # 运行所有测试 tests = [ test_frontend_pages, test_user_auth, test_regions, test_articles, test_services, test_comments_ratings, test_admin, ] for test_func in tests: try: passed, total = test_func() total_passed += passed total_tests += total except Exception as e: log_error(f"{test_func.__name__} 异常:{str(e)}") # 打印总结 print("\n" + "="*60) print(f"📊 测试结果:{total_passed}/{total_tests} 通过") if total_passed == total_tests: print(f"{GREEN}✅ 所有测试通过!{RESET}") else: failed = total_tests - total_passed print(f"{RED}⚠️ {failed} 个测试失败{RESET}") print("="*60 + "\n") return 0 if total_passed == total_tests else 1 if __name__ == '__main__': sys.exit(main())