import unittest import urllib.request import urllib.parse import json import sys import os sys.path.insert(0, os.path.dirname(os.path.dirname(__file__))) class TestAPIBase(unittest.TestCase): BASE_URL = 'http://localhost:3000/api' def post(self, path, data): url = f'{self.BASE_URL}{path}' req = urllib.request.Request( url, data=json.dumps(data).encode('utf-8'), headers={'Content-Type': 'application/json'}, method='POST' ) return urllib.request.urlopen(req) def get(self, path, params=None): url = f'{self.BASE_URL}{path}' if params: url += '?' + urllib.parse.urlencode(params) req = urllib.request.Request(url) return urllib.request.urlopen(req) def put(self, path, data, token=None): url = f'{self.BASE_URL}{path}' headers = {'Content-Type': 'application/json'} if token: headers['Authorization'] = f'Bearer {token}' req = urllib.request.Request( url, data=json.dumps(data).encode('utf-8'), headers=headers, method='PUT' ) return urllib.request.urlopen(req) class TestAuthAPI(TestAPIBase): def test_01_login_success(self): """测试正常登录""" data = {'phone': '13800138000', 'code': '1234'} response = self.post('/auth/login', data) self.assertEqual(response.status, 200) result = json.loads(response.read()) self.assertIn('token', result) self.assertIn('user', result) self.assertTrue(result['token']) print('✓ 登录成功测试通过') def test_02_login_invalid_phone(self): """测试无效手机号""" data = {'phone': '123', 'code': '1234'} try: self.post('/auth/login', data) self.fail('应该抛出异常') except urllib.error.HTTPError as e: self.assertEqual(e.code, 400) result = json.loads(e.read()) self.assertIn('error', result) print('✓ 无效手机号测试通过') def test_03_login_invalid_code(self): """测试无效验证码""" data = {'phone': '13800138000', 'code': '123'} try: self.post('/auth/login', data) self.fail('应该抛出异常') except urllib.error.HTTPError as e: self.assertEqual(e.code, 400) print('✓ 无效验证码测试通过') def test_04_get_current_user(self): """测试获取当前用户""" # 先登录获取token data = {'phone': '13800138001', 'code': '1234'} login_response = self.post('/auth/login', data) result = json.loads(login_response.read()) token = result['token'] url = f'{self.BASE_URL}/auth/me' req = urllib.request.Request(url, headers={'Authorization': f'Bearer {token}'}) response = urllib.request.urlopen(req) self.assertEqual(response.status, 200) user = json.loads(response.read()) self.assertEqual(user['phone'], '13800138001') print('✓ 获取当前用户测试通过') def test_05_update_profile_valid(self): """测试更新用户资料-正常""" data = {'phone': '13800138002', 'code': '1234'} login_response = self.post('/auth/login', data) result = json.loads(login_response.read()) token = result['token'] update_data = {'nickname': '测试用户'} response = self.put('/auth/profile', update_data, token) self.assertEqual(response.status, 200) user = json.loads(response.read()) self.assertEqual(user['nickname'], '测试用户') print('✓ 更新用户资料测试通过') def test_06_update_profile_invalid_nickname(self): """测试更新用户资料-昵称太短""" data = {'phone': '13800138003', 'code': '1234'} login_response = self.post('/auth/login', data) result = json.loads(login_response.read()) token = result['token'] update_data = {'nickname': 'a'} try: self.put('/auth/profile', update_data, token) self.fail('应该抛出异常') except urllib.error.HTTPError as e: self.assertEqual(e.code, 400) print('✓ 昵称太短测试通过') class TestTaskAPI(TestAPIBase): def test_01_get_tasks_list(self): """测试获取任务列表""" response = self.get('/tasks') self.assertEqual(response.status, 200) result = json.loads(response.read()) self.assertIn('items', result) self.assertIn('total', result) print('✓ 获取任务列表测试通过') def test_02_get_tasks_with_pagination(self): """测试任务列表分页""" response = self.get('/tasks', {'page': 1, 'limit': 5}) self.assertEqual(response.status, 200) result = json.loads(response.read()) self.assertTrue(len(result['items']) <= 5) print('✓ 任务列表分页测试通过') def test_03_get_tasks_with_search(self): """测试任务搜索""" response = self.get('/tasks', {'search': '测试'}) self.assertEqual(response.status, 200) result = json.loads(response.read()) self.assertIn('items', result) print('✓ 任务搜索测试通过') def test_04_create_task(self): """测试创建任务""" data = {'phone': '13800138010', 'code': '1234'} login_response = self.post('/auth/login', data) result = json.loads(login_response.read()) token = result['token'] task_data = { 'title': '测试任务标题', 'description': '测试任务描述', 'province': '北京市', 'city': '北京市', 'district': '朝阳区', 'street': '望京街道', 'detail_location': '测试地址', 'time': '2026-06-01 10:00', 'reward': '100元', 'publisher_name': '测试发布者', 'contact': '13800138010' } url = f'{self.BASE_URL}/tasks' headers = {'Content-Type': 'application/json', 'Authorization': f'Bearer {token}'} req = urllib.request.Request(url, data=json.dumps(task_data).encode(), headers=headers, method='POST') response = urllib.request.urlopen(req) self.assertEqual(response.status, 200) result = json.loads(response.read()) self.assertIn('id', result) print('✓ 创建任务测试通过') class TestSkillAPI(TestAPIBase): def test_01_get_skills_list(self): """测试获取技能列表""" response = self.get('/skills') self.assertEqual(response.status, 200) result = json.loads(response.read()) self.assertIn('items', result) print('✓ 获取技能列表测试通过') def test_02_create_skill(self): """测试创建技能""" phone = '13800138012' data = {'phone': phone, 'code': '1234'} login_response = self.post('/auth/login', data) result = json.loads(login_response.read()) token = result['token'] skill_data = { 'title': '测试技能', 'description': '测试技能描述', 'province': '北京市', 'city': '北京市', 'district': '朝阳区', 'publisher_name': '测试用户', 'contact': phone } url = f'{self.BASE_URL}/skills' headers = {'Content-Type': 'application/json', 'Authorization': f'Bearer {token}'} req = urllib.request.Request(url, data=json.dumps(skill_data).encode(), headers=headers, method='POST') response = urllib.request.urlopen(req) self.assertEqual(response.status, 200) result = json.loads(response.read()) self.assertIn('id', result) print('✓ 创建技能测试通过') class TestNewsAPI(TestAPIBase): def test_01_get_news_list(self): """测试获取资讯列表""" response = self.get('/news') self.assertEqual(response.status, 200) result = json.loads(response.read()) self.assertIn('items', result) print('✓ 获取资讯列表测试通过') class TestCarouselAPI(TestAPIBase): def test_01_get_carousels(self): """测试获取轮播图""" response = self.get('/carousels') self.assertEqual(response.status, 200) result = json.loads(response.read()) self.assertIsInstance(result, list) print('✓ 获取轮播图测试通过') class TestHealthAPI(TestAPIBase): def test_01_health_check(self): """测试健康检查""" response = self.get('/health') self.assertEqual(response.status, 200) result = json.loads(response.read()) self.assertEqual(result['status'], 'ok') print('✓ 健康检查测试通过') class TestAdminAPI(TestAPIBase): def test_01_admin_login(self): """测试管理员登录""" # 先获取验证码 captcha_response = self.get('/admin/captcha') captcha_result = json.loads(captcha_response.read()) # 解析验证码问题计算答案 question = captcha_result['question'] # 解析 "X + Y = ?" 格式 parts = question.split('+') num1 = int(parts[0].strip()) num2 = int(parts[1].split('=')[0].strip()) answer = num1 + num2 data = { 'username': 'admin', 'password': 'admin', 'captcha': str(answer), 'captcha_id': captcha_result['captcha_id'] } response = self.post('/admin/login', data) self.assertEqual(response.status, 200) result = json.loads(response.read()) self.assertIn('success', result) self.assertTrue(result['success']) print('✓ 管理员登录测试通过') if __name__ == '__main__': print('=' * 60) print(' 服务端API测试') print('=' * 60) print() suite = unittest.TestSuite() # 添加测试用例 suite.addTest(TestAuthAPI('test_01_login_success')) suite.addTest(TestAuthAPI('test_02_login_invalid_phone')) suite.addTest(TestAuthAPI('test_03_login_invalid_code')) suite.addTest(TestAuthAPI('test_04_get_current_user')) suite.addTest(TestAuthAPI('test_05_update_profile_valid')) suite.addTest(TestAuthAPI('test_06_update_profile_invalid_nickname')) suite.addTest(TestTaskAPI('test_01_get_tasks_list')) suite.addTest(TestTaskAPI('test_02_get_tasks_with_pagination')) suite.addTest(TestTaskAPI('test_03_get_tasks_with_search')) suite.addTest(TestTaskAPI('test_04_create_task')) suite.addTest(TestSkillAPI('test_01_get_skills_list')) suite.addTest(TestSkillAPI('test_02_create_skill')) suite.addTest(TestNewsAPI('test_01_get_news_list')) suite.addTest(TestCarouselAPI('test_01_get_carousels')) suite.addTest(TestHealthAPI('test_01_health_check')) suite.addTest(TestAdminAPI('test_01_admin_login')) runner = unittest.TextTestRunner(verbosity=1) result = runner.run(suite) print() print('=' * 60) print(' 测试结果汇总') print('=' * 60) print(f"通过: {result.testsRun - len(result.failures) - len(result.errors)}") print(f"失败: {len(result.failures)}") print(f"错误: {len(result.errors)}") print(f"通过率: {((result.testsRun - len(result.failures) - len(result.errors)) / result.testsRun * 100):.1f}%")