788 lines
30 KiB
Python
788 lines
30 KiB
Python
"""
|
||
RTC_DEMO API 完整测试用例
|
||
覆盖所有API接口
|
||
运行方式: python manage.py test tests --verbosity=2 --keepdb
|
||
"""
|
||
import json
|
||
from datetime import date
|
||
from django.test import TestCase
|
||
from django.urls import reverse
|
||
from rest_framework.test import APITestCase, APIClient
|
||
from rest_framework import status
|
||
from apps.users.models import User
|
||
from apps.admins.models import AdminUser
|
||
from apps.spirits.models import Spirit
|
||
from apps.devices.models import DeviceType, DeviceBatch, Device, UserDevice
|
||
|
||
|
||
# ==================== App端测试 ====================
|
||
|
||
class UserAuthTests(APITestCase):
|
||
"""App端用户认证测试"""
|
||
|
||
def test_phone_login_new_user(self):
|
||
"""测试手机号一键登录 - 新用户"""
|
||
url = '/api/v1/auth/phone-login/'
|
||
data = {'phone': '13800138001'}
|
||
response = self.client.post(url, data, format='json')
|
||
|
||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||
self.assertEqual(response.data['code'], 0)
|
||
self.assertIn('token', response.data['data'])
|
||
self.assertTrue(response.data['data']['is_new_user'])
|
||
self.assertEqual(response.data['data']['user']['phone'], '13800138001')
|
||
|
||
def test_phone_login_existing_user(self):
|
||
"""测试手机号一键登录 - 已有用户"""
|
||
User.objects.create_user(phone='13800138002', nickname='测试用户')
|
||
|
||
url = '/api/v1/auth/phone-login/'
|
||
data = {'phone': '13800138002'}
|
||
response = self.client.post(url, data, format='json')
|
||
|
||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||
self.assertEqual(response.data['code'], 0)
|
||
self.assertFalse(response.data['data']['is_new_user'])
|
||
|
||
def test_phone_login_invalid_phone(self):
|
||
"""测试手机号格式验证"""
|
||
url = '/api/v1/auth/phone-login/'
|
||
data = {'phone': '1234567'}
|
||
response = self.client.post(url, data, format='json')
|
||
|
||
self.assertNotEqual(response.data['code'], 0)
|
||
|
||
def test_phone_login_disabled_user(self):
|
||
"""测试禁用用户登录"""
|
||
User.objects.create_user(phone='13800138003', is_active=False)
|
||
|
||
url = '/api/v1/auth/phone-login/'
|
||
data = {'phone': '13800138003'}
|
||
response = self.client.post(url, data, format='json')
|
||
|
||
self.assertNotEqual(response.data['code'], 0)
|
||
|
||
def test_token_refresh(self):
|
||
"""测试Token刷新"""
|
||
login_url = '/api/v1/auth/phone-login/'
|
||
login_response = self.client.post(login_url, {'phone': '13800138004'}, format='json')
|
||
refresh_token = login_response.data['data']['token']['refresh']
|
||
|
||
refresh_url = '/api/v1/auth/refresh/'
|
||
response = self.client.post(refresh_url, {'refresh': refresh_token}, format='json')
|
||
|
||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||
self.assertEqual(response.data['code'], 0)
|
||
self.assertIn('access', response.data['data'])
|
||
|
||
def test_token_refresh_invalid(self):
|
||
"""测试无效Token刷新"""
|
||
url = '/api/v1/auth/refresh/'
|
||
response = self.client.post(url, {'refresh': 'invalid_token'}, format='json')
|
||
|
||
self.assertNotEqual(response.data['code'], 0)
|
||
|
||
def test_token_refresh_empty(self):
|
||
"""测试空Token刷新"""
|
||
url = '/api/v1/auth/refresh/'
|
||
response = self.client.post(url, {}, format='json')
|
||
|
||
self.assertNotEqual(response.data['code'], 0)
|
||
|
||
|
||
class UserProfileTests(APITestCase):
|
||
"""App端用户信息测试"""
|
||
|
||
def setUp(self):
|
||
self.user = User.objects.create_user(phone='13800138010', nickname='测试用户')
|
||
login_url = '/api/v1/auth/phone-login/'
|
||
response = self.client.post(login_url, {'phone': '13800138010'}, format='json')
|
||
self.access_token = response.data['data']['token']['access']
|
||
self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {self.access_token}')
|
||
|
||
def test_get_user_info(self):
|
||
"""测试获取用户信息"""
|
||
url = '/api/v1/users/me/'
|
||
response = self.client.get(url)
|
||
|
||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||
self.assertEqual(response.data['code'], 0)
|
||
self.assertEqual(response.data['data']['phone'], '13800138010')
|
||
|
||
def test_get_user_info_unauthorized(self):
|
||
"""测试未登录获取用户信息"""
|
||
self.client.credentials()
|
||
url = '/api/v1/users/me/'
|
||
response = self.client.get(url)
|
||
|
||
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
|
||
|
||
def test_update_user_info(self):
|
||
"""测试更新用户信息"""
|
||
url = '/api/v1/users/update_me/'
|
||
data = {'nickname': '新昵称'}
|
||
response = self.client.put(url, data, format='json')
|
||
|
||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||
self.assertEqual(response.data['code'], 0)
|
||
self.assertEqual(response.data['data']['nickname'], '新昵称')
|
||
|
||
def test_update_user_avatar(self):
|
||
"""测试更新用户头像"""
|
||
url = '/api/v1/users/update_me/'
|
||
data = {'avatar': 'https://example.com/avatar.jpg'}
|
||
response = self.client.put(url, data, format='json')
|
||
|
||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||
self.assertEqual(response.data['code'], 0)
|
||
|
||
|
||
class SpiritTests(APITestCase):
|
||
"""App端智能体测试"""
|
||
|
||
def setUp(self):
|
||
self.user = User.objects.create_user(phone='13800138020', nickname='测试用户')
|
||
login_url = '/api/v1/auth/phone-login/'
|
||
response = self.client.post(login_url, {'phone': '13800138020'}, format='json')
|
||
self.access_token = response.data['data']['token']['access']
|
||
self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {self.access_token}')
|
||
|
||
def test_create_spirit(self):
|
||
"""测试创建智能体"""
|
||
url = '/api/v1/spirits/'
|
||
data = {
|
||
'name': '测试心灵',
|
||
'prompt': '你是一个友好的助手',
|
||
'voice_id': 'voice_001'
|
||
}
|
||
response = self.client.post(url, data, format='json')
|
||
|
||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||
self.assertEqual(response.data['code'], 0)
|
||
self.assertEqual(response.data['data']['name'], '测试心灵')
|
||
|
||
def test_create_spirit_minimal(self):
|
||
"""测试创建智能体 - 最少字段"""
|
||
url = '/api/v1/spirits/'
|
||
data = {'name': '简单心灵'}
|
||
response = self.client.post(url, data, format='json')
|
||
|
||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||
self.assertEqual(response.data['code'], 0)
|
||
|
||
def test_list_spirits(self):
|
||
"""测试获取智能体列表"""
|
||
Spirit.objects.create(user=self.user, name='心灵1')
|
||
Spirit.objects.create(user=self.user, name='心灵2')
|
||
|
||
url = '/api/v1/spirits/'
|
||
response = self.client.get(url)
|
||
|
||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||
self.assertEqual(response.data['code'], 0)
|
||
self.assertEqual(len(response.data['data']), 2)
|
||
|
||
def test_list_spirits_empty(self):
|
||
"""测试获取空智能体列表"""
|
||
url = '/api/v1/spirits/'
|
||
response = self.client.get(url)
|
||
|
||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||
self.assertEqual(len(response.data['data']), 0)
|
||
|
||
def test_get_spirit_detail(self):
|
||
"""测试获取智能体详情"""
|
||
spirit = Spirit.objects.create(user=self.user, name='详情测试')
|
||
|
||
url = f'/api/v1/spirits/{spirit.id}/'
|
||
response = self.client.get(url)
|
||
|
||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||
self.assertEqual(response.data['data']['name'], '详情测试')
|
||
|
||
def test_update_spirit(self):
|
||
"""测试更新智能体"""
|
||
spirit = Spirit.objects.create(user=self.user, name='原始名称')
|
||
|
||
url = f'/api/v1/spirits/{spirit.id}/'
|
||
data = {'name': '新名称', 'prompt': '新的提示词'}
|
||
response = self.client.put(url, data, format='json')
|
||
|
||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||
self.assertEqual(response.data['data']['name'], '新名称')
|
||
|
||
def test_delete_spirit(self):
|
||
"""测试删除智能体"""
|
||
spirit = Spirit.objects.create(user=self.user, name='待删除')
|
||
|
||
url = f'/api/v1/spirits/{spirit.id}/'
|
||
response = self.client.delete(url)
|
||
|
||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||
self.assertFalse(Spirit.objects.filter(id=spirit.id).exists())
|
||
|
||
def test_spirit_isolation(self):
|
||
"""测试智能体用户隔离 - 不能访问其他用户的智能体"""
|
||
other_user = User.objects.create_user(phone='13800138021')
|
||
other_spirit = Spirit.objects.create(user=other_user, name='其他用户的心灵')
|
||
|
||
url = f'/api/v1/spirits/{other_spirit.id}/'
|
||
response = self.client.get(url)
|
||
|
||
# 应该返回404或权限错误
|
||
self.assertNotEqual(response.status_code, status.HTTP_200_OK)
|
||
|
||
|
||
class DeviceTests(APITestCase):
|
||
"""App端设备测试"""
|
||
|
||
def setUp(self):
|
||
self.user = User.objects.create_user(phone='13800138050', nickname='测试用户')
|
||
login_url = '/api/v1/auth/phone-login/'
|
||
response = self.client.post(login_url, {'phone': '13800138050'}, format='json')
|
||
self.access_token = response.data['data']['token']['access']
|
||
self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {self.access_token}')
|
||
|
||
self.device_type = DeviceType.objects.create(
|
||
brand='AL',
|
||
product_code='DZBJ-ON',
|
||
name='电子吧唧-联网版'
|
||
)
|
||
self.device = Device.objects.create(
|
||
sn='AL-DZBJ-ON-25W45-A01-00001',
|
||
device_type=self.device_type,
|
||
mac_address='AA:BB:CC:DD:EE:FF',
|
||
status='in_stock'
|
||
)
|
||
|
||
def test_query_by_mac(self):
|
||
"""测试通过MAC地址查询SN码(无需登录)"""
|
||
self.client.credentials() # 清除认证
|
||
url = '/api/v1/devices/query-by-mac/?mac=AA:BB:CC:DD:EE:FF'
|
||
response = self.client.get(url)
|
||
|
||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||
self.assertEqual(response.data['code'], 0)
|
||
self.assertEqual(response.data['data']['sn'], 'AL-DZBJ-ON-25W45-A01-00001')
|
||
|
||
def test_query_by_mac_lowercase(self):
|
||
"""测试MAC地址查询 - 小写格式"""
|
||
self.client.credentials()
|
||
url = '/api/v1/devices/query-by-mac/?mac=aa:bb:cc:dd:ee:ff'
|
||
response = self.client.get(url)
|
||
|
||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||
self.assertEqual(response.data['code'], 0)
|
||
|
||
def test_query_by_mac_dash_format(self):
|
||
"""测试MAC地址查询 - 横杠格式"""
|
||
self.client.credentials()
|
||
url = '/api/v1/devices/query-by-mac/?mac=AA-BB-CC-DD-EE-FF'
|
||
response = self.client.get(url)
|
||
|
||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||
|
||
def test_query_by_mac_not_found(self):
|
||
"""测试MAC地址查询 - 设备不存在"""
|
||
self.client.credentials()
|
||
url = '/api/v1/devices/query-by-mac/?mac=11:22:33:44:55:66'
|
||
response = self.client.get(url)
|
||
|
||
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
|
||
|
||
def test_query_by_mac_empty(self):
|
||
"""测试MAC地址查询 - 空参数"""
|
||
self.client.credentials()
|
||
url = '/api/v1/devices/query-by-mac/'
|
||
response = self.client.get(url)
|
||
|
||
self.assertNotEqual(response.data['code'], 0)
|
||
|
||
def test_verify_device(self):
|
||
"""测试验证设备SN"""
|
||
url = '/api/v1/devices/verify/'
|
||
data = {'sn': 'AL-DZBJ-ON-25W45-A01-00001'}
|
||
response = self.client.post(url, data, format='json')
|
||
|
||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||
self.assertEqual(response.data['code'], 0)
|
||
self.assertTrue(response.data['data']['is_bindable'])
|
||
|
||
def test_verify_device_not_found(self):
|
||
"""测试验证设备SN - 不存在"""
|
||
url = '/api/v1/devices/verify/'
|
||
data = {'sn': 'NOT-EXIST-SN'}
|
||
response = self.client.post(url, data, format='json')
|
||
|
||
self.assertNotEqual(response.data['code'], 0)
|
||
|
||
def test_bind_device(self):
|
||
"""测试绑定设备"""
|
||
spirit = Spirit.objects.create(user=self.user, name='测试心灵')
|
||
|
||
url = '/api/v1/devices/bind/'
|
||
data = {
|
||
'sn': 'AL-DZBJ-ON-25W45-A01-00001',
|
||
'spirit_id': spirit.id
|
||
}
|
||
response = self.client.post(url, data, format='json')
|
||
|
||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||
self.assertEqual(response.data['code'], 0)
|
||
|
||
# 验证设备状态
|
||
self.device.refresh_from_db()
|
||
self.assertEqual(self.device.status, 'bound')
|
||
|
||
def test_bind_device_without_spirit(self):
|
||
"""测试绑定设备 - 不绑定智能体"""
|
||
device2 = Device.objects.create(
|
||
sn='AL-DZBJ-ON-25W45-A01-00002',
|
||
device_type=self.device_type,
|
||
status='in_stock'
|
||
)
|
||
|
||
url = '/api/v1/devices/bind/'
|
||
data = {'sn': 'AL-DZBJ-ON-25W45-A01-00002'}
|
||
response = self.client.post(url, data, format='json')
|
||
|
||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||
|
||
def test_my_devices(self):
|
||
"""测试我的设备列表"""
|
||
UserDevice.objects.create(user=self.user, device=self.device, is_active=True)
|
||
|
||
url = '/api/v1/devices/my_devices/'
|
||
response = self.client.get(url)
|
||
|
||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||
self.assertEqual(response.data['code'], 0)
|
||
self.assertEqual(len(response.data['data']), 1)
|
||
|
||
def test_my_devices_empty(self):
|
||
"""测试我的设备列表 - 空列表"""
|
||
url = '/api/v1/devices/my_devices/'
|
||
response = self.client.get(url)
|
||
|
||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||
self.assertEqual(len(response.data['data']), 0)
|
||
|
||
def test_unbind_device(self):
|
||
"""测试解绑设备"""
|
||
user_device = UserDevice.objects.create(user=self.user, device=self.device, is_active=True)
|
||
self.device.status = 'bound'
|
||
self.device.save()
|
||
|
||
url = f'/api/v1/devices/{user_device.id}/unbind/'
|
||
response = self.client.delete(url)
|
||
|
||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||
|
||
user_device.refresh_from_db()
|
||
self.assertFalse(user_device.is_active)
|
||
|
||
def test_update_spirit_on_device(self):
|
||
"""测试更新设备绑定的智能体"""
|
||
spirit1 = Spirit.objects.create(user=self.user, name='心灵1')
|
||
spirit2 = Spirit.objects.create(user=self.user, name='心灵2')
|
||
user_device = UserDevice.objects.create(
|
||
user=self.user, device=self.device, spirit=spirit1, is_active=True
|
||
)
|
||
|
||
url = f'/api/v1/devices/{user_device.id}/update-spirit/'
|
||
data = {'spirit_id': spirit2.id}
|
||
response = self.client.put(url, data, format='json')
|
||
|
||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||
user_device.refresh_from_db()
|
||
self.assertEqual(user_device.spirit_id, spirit2.id)
|
||
|
||
|
||
# ==================== 管理端测试 ====================
|
||
|
||
class AdminAuthTests(APITestCase):
|
||
"""管理端认证测试"""
|
||
|
||
def setUp(self):
|
||
self.admin = AdminUser.objects.create_user(
|
||
username='admin',
|
||
password='admin123',
|
||
role='super_admin'
|
||
)
|
||
|
||
def test_admin_login(self):
|
||
"""测试管理员登录"""
|
||
url = '/api/admin/auth/login/'
|
||
data = {'username': 'admin', 'password': 'admin123'}
|
||
response = self.client.post(url, data, format='json')
|
||
|
||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||
self.assertEqual(response.data['code'], 0)
|
||
self.assertIn('token', response.data['data'])
|
||
self.assertEqual(response.data['data']['admin']['username'], 'admin')
|
||
|
||
def test_admin_login_wrong_password(self):
|
||
"""测试管理员登录 - 密码错误"""
|
||
url = '/api/admin/auth/login/'
|
||
data = {'username': 'admin', 'password': 'wrong'}
|
||
response = self.client.post(url, data, format='json')
|
||
|
||
self.assertNotEqual(response.data['code'], 0)
|
||
|
||
def test_admin_login_user_not_exist(self):
|
||
"""测试管理员登录 - 用户不存在"""
|
||
url = '/api/admin/auth/login/'
|
||
data = {'username': 'notexist', 'password': 'password'}
|
||
response = self.client.post(url, data, format='json')
|
||
|
||
self.assertNotEqual(response.data['code'], 0)
|
||
|
||
def test_admin_login_disabled(self):
|
||
"""测试管理员登录 - 账户禁用"""
|
||
disabled_admin = AdminUser.objects.create_user(
|
||
username='disabled', password='pass123', is_active=False
|
||
)
|
||
|
||
url = '/api/admin/auth/login/'
|
||
data = {'username': 'disabled', 'password': 'pass123'}
|
||
response = self.client.post(url, data, format='json')
|
||
|
||
self.assertNotEqual(response.data['code'], 0)
|
||
|
||
def test_admin_token_refresh(self):
|
||
"""测试管理员Token刷新"""
|
||
login_url = '/api/admin/auth/login/'
|
||
login_response = self.client.post(
|
||
login_url, {'username': 'admin', 'password': 'admin123'}, format='json'
|
||
)
|
||
refresh_token = login_response.data['data']['token']['refresh']
|
||
|
||
refresh_url = '/api/admin/auth/refresh/'
|
||
response = self.client.post(refresh_url, {'refresh': refresh_token}, format='json')
|
||
|
||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||
self.assertEqual(response.data['code'], 0)
|
||
|
||
|
||
class AdminProfileTests(APITestCase):
|
||
"""管理端个人信息测试"""
|
||
|
||
def setUp(self):
|
||
self.admin = AdminUser.objects.create_user(
|
||
username='admin', password='admin123', role='admin'
|
||
)
|
||
login_url = '/api/admin/auth/login/'
|
||
response = self.client.post(
|
||
login_url, {'username': 'admin', 'password': 'admin123'}, format='json'
|
||
)
|
||
self.access_token = response.data['data']['token']['access']
|
||
self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {self.access_token}')
|
||
|
||
def test_get_admin_profile(self):
|
||
"""测试获取管理员信息"""
|
||
url = '/api/admin/profile/me/'
|
||
response = self.client.get(url)
|
||
|
||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||
self.assertEqual(response.data['data']['username'], 'admin')
|
||
|
||
def test_change_password(self):
|
||
"""测试修改密码"""
|
||
url = '/api/admin/profile/change-password/'
|
||
data = {'old_password': 'admin123', 'new_password': 'newpass123'}
|
||
response = self.client.put(url, data, format='json')
|
||
|
||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||
|
||
# 验证新密码可以登录
|
||
self.client.credentials()
|
||
login_url = '/api/admin/auth/login/'
|
||
login_response = self.client.post(
|
||
login_url, {'username': 'admin', 'password': 'newpass123'}, format='json'
|
||
)
|
||
self.assertEqual(login_response.data['code'], 0)
|
||
|
||
def test_change_password_wrong_old(self):
|
||
"""测试修改密码 - 原密码错误"""
|
||
url = '/api/admin/profile/change-password/'
|
||
data = {'old_password': 'wrongpass', 'new_password': 'newpass123'}
|
||
response = self.client.put(url, data, format='json')
|
||
|
||
self.assertNotEqual(response.data['code'], 0)
|
||
|
||
|
||
class AdminUserManageTests(APITestCase):
|
||
"""管理员用户管理测试"""
|
||
|
||
def setUp(self):
|
||
self.super_admin = AdminUser.objects.create_user(
|
||
username='superadmin', password='super123', role='super_admin'
|
||
)
|
||
login_url = '/api/admin/auth/login/'
|
||
response = self.client.post(
|
||
login_url, {'username': 'superadmin', 'password': 'super123'}, format='json'
|
||
)
|
||
self.access_token = response.data['data']['token']['access']
|
||
self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {self.access_token}')
|
||
|
||
def test_list_admins(self):
|
||
"""测试管理员列表"""
|
||
url = '/api/admin/admins/'
|
||
response = self.client.get(url)
|
||
|
||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||
self.assertEqual(response.data['code'], 0)
|
||
|
||
def test_create_admin(self):
|
||
"""测试创建管理员"""
|
||
url = '/api/admin/admins/'
|
||
data = {
|
||
'username': 'newadmin',
|
||
'password': 'newpass123',
|
||
'name': '新管理员',
|
||
'role': 'operator'
|
||
}
|
||
response = self.client.post(url, data, format='json')
|
||
|
||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||
self.assertEqual(response.data['data']['username'], 'newadmin')
|
||
|
||
def test_get_admin_detail(self):
|
||
"""测试获取管理员详情"""
|
||
admin = AdminUser.objects.create_user(username='testadmin', password='test123')
|
||
|
||
url = f'/api/admin/admins/{admin.id}/'
|
||
response = self.client.get(url)
|
||
|
||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||
self.assertEqual(response.data['data']['username'], 'testadmin')
|
||
|
||
def test_toggle_admin_status(self):
|
||
"""测试启用/禁用管理员"""
|
||
admin = AdminUser.objects.create_user(username='testadmin2', password='test123')
|
||
|
||
url = f'/api/admin/admins/{admin.id}/toggle-status/'
|
||
response = self.client.post(url, format='json')
|
||
|
||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||
admin.refresh_from_db()
|
||
self.assertFalse(admin.is_active)
|
||
|
||
def test_reset_admin_password(self):
|
||
"""测试重置管理员密码"""
|
||
admin = AdminUser.objects.create_user(username='testadmin3', password='oldpass')
|
||
|
||
url = f'/api/admin/admins/{admin.id}/reset-password/'
|
||
data = {'new_password': 'resetpass123'}
|
||
response = self.client.post(url, data, format='json')
|
||
|
||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||
|
||
|
||
class AdminDeviceTypeTests(APITestCase):
|
||
"""管理端设备类型测试"""
|
||
|
||
def setUp(self):
|
||
self.admin = AdminUser.objects.create_user(
|
||
username='admin', password='admin123', role='super_admin'
|
||
)
|
||
login_url = '/api/admin/auth/login/'
|
||
response = self.client.post(
|
||
login_url, {'username': 'admin', 'password': 'admin123'}, format='json'
|
||
)
|
||
self.access_token = response.data['data']['token']['access']
|
||
self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {self.access_token}')
|
||
|
||
def test_create_device_type_network(self):
|
||
"""测试创建联网设备类型"""
|
||
url = '/api/admin/device-types/'
|
||
data = {
|
||
'brand': 'AL',
|
||
'product_code': 'DZBJ-ON',
|
||
'name': '电子吧唧-联网版'
|
||
}
|
||
response = self.client.post(url, data, format='json')
|
||
|
||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||
self.assertEqual(response.data['code'], 0)
|
||
self.assertTrue(response.data['data']['is_network_required'])
|
||
|
||
def test_create_device_type_offline(self):
|
||
"""测试创建非联网设备类型"""
|
||
url = '/api/admin/device-types/'
|
||
data = {
|
||
'brand': 'AL',
|
||
'product_code': 'DZBJ-OFF',
|
||
'name': '电子吧唧-离线版'
|
||
}
|
||
response = self.client.post(url, data, format='json')
|
||
|
||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||
self.assertFalse(response.data['data']['is_network_required'])
|
||
|
||
def test_list_device_types(self):
|
||
"""测试设备类型列表"""
|
||
DeviceType.objects.create(brand='AL', product_code='TEST-ON', name='测试设备')
|
||
|
||
url = '/api/admin/device-types/'
|
||
response = self.client.get(url)
|
||
|
||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||
self.assertEqual(response.data['code'], 0)
|
||
|
||
def test_get_device_type_detail(self):
|
||
"""测试设备类型详情"""
|
||
dt = DeviceType.objects.create(brand='AL', product_code='TEST-DT', name='测试类型')
|
||
|
||
url = f'/api/admin/device-types/{dt.id}/'
|
||
response = self.client.get(url)
|
||
|
||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||
self.assertEqual(response.data['data']['name'], '测试类型')
|
||
|
||
def test_update_device_type(self):
|
||
"""测试更新设备类型"""
|
||
dt = DeviceType.objects.create(brand='AL', product_code='TEST-UP', name='原名称')
|
||
|
||
url = f'/api/admin/device-types/{dt.id}/'
|
||
data = {'name': '新名称'}
|
||
response = self.client.put(url, data, format='json')
|
||
|
||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||
self.assertEqual(response.data['data']['name'], '新名称')
|
||
|
||
|
||
class AdminBatchTests(APITestCase):
|
||
"""管理端批次测试"""
|
||
|
||
def setUp(self):
|
||
self.admin = AdminUser.objects.create_user(
|
||
username='admin', password='admin123', role='super_admin'
|
||
)
|
||
login_url = '/api/admin/auth/login/'
|
||
response = self.client.post(
|
||
login_url, {'username': 'admin', 'password': 'admin123'}, format='json'
|
||
)
|
||
self.access_token = response.data['data']['token']['access']
|
||
self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {self.access_token}')
|
||
|
||
self.device_type = DeviceType.objects.create(
|
||
brand='AL', product_code='DZBJ-ON', name='电子吧唧-联网版'
|
||
)
|
||
|
||
def test_create_batch_and_generate_sn(self):
|
||
"""测试创建批次并生成SN码"""
|
||
url = '/api/admin/device-batches/'
|
||
data = {
|
||
'device_type': self.device_type.id,
|
||
'batch_no': 'A01',
|
||
'production_date': '2026-01-28',
|
||
'quantity': 10,
|
||
'remark': '测试批次'
|
||
}
|
||
response = self.client.post(url, data, format='json')
|
||
|
||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||
self.assertEqual(response.data['code'], 0)
|
||
self.assertEqual(response.data['data']['quantity'], 10)
|
||
self.assertIn('sn_range', response.data['data'])
|
||
|
||
# 验证SN码格式
|
||
sn_start = response.data['data']['sn_range']['start']
|
||
self.assertTrue(sn_start.startswith('AL-DZBJ-ON-'))
|
||
|
||
# 验证设备数量
|
||
device_count = Device.objects.filter(batch__batch_no='A01').count()
|
||
self.assertEqual(device_count, 10)
|
||
|
||
def test_list_batches(self):
|
||
"""测试批次列表"""
|
||
url = '/api/admin/device-batches/'
|
||
response = self.client.get(url)
|
||
|
||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||
self.assertEqual(response.data['code'], 0)
|
||
|
||
def test_get_batch_detail(self):
|
||
"""测试批次详情"""
|
||
batch = DeviceBatch.objects.create(
|
||
device_type=self.device_type,
|
||
batch_no='B01',
|
||
production_date=date(2026, 1, 28),
|
||
quantity=5
|
||
)
|
||
|
||
url = f'/api/admin/device-batches/{batch.id}/'
|
||
response = self.client.get(url)
|
||
|
||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||
self.assertIn('statistics', response.data['data'])
|
||
|
||
def test_get_batch_devices(self):
|
||
"""测试批次设备列表"""
|
||
batch = DeviceBatch.objects.create(
|
||
device_type=self.device_type,
|
||
batch_no='C01',
|
||
production_date=date(2026, 1, 28),
|
||
quantity=3
|
||
)
|
||
Device.objects.create(sn='TEST-001', batch=batch, device_type=self.device_type)
|
||
Device.objects.create(sn='TEST-002', batch=batch, device_type=self.device_type)
|
||
|
||
url = f'/api/admin/device-batches/{batch.id}/devices/'
|
||
response = self.client.get(url)
|
||
|
||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||
self.assertEqual(len(response.data['data']['items']), 2)
|
||
|
||
|
||
class AuthSeparationTests(APITestCase):
|
||
"""验证App和Admin认证分离"""
|
||
|
||
def setUp(self):
|
||
# 创建App用户
|
||
self.app_user = User.objects.create_user(phone='13800138099')
|
||
login_url = '/api/v1/auth/phone-login/'
|
||
response = self.client.post(login_url, {'phone': '13800138099'}, format='json')
|
||
self.app_token = response.data['data']['token']['access']
|
||
|
||
# 创建Admin用户
|
||
self.admin = AdminUser.objects.create_user(
|
||
username='admin', password='admin123', role='admin'
|
||
)
|
||
login_url = '/api/admin/auth/login/'
|
||
response = self.client.post(
|
||
login_url, {'username': 'admin', 'password': 'admin123'}, format='json'
|
||
)
|
||
self.admin_token = response.data['data']['token']['access']
|
||
|
||
def test_app_token_cannot_access_admin_api(self):
|
||
"""测试App Token无法访问管理端API"""
|
||
self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {self.app_token}')
|
||
|
||
url = '/api/admin/device-types/'
|
||
response = self.client.get(url)
|
||
|
||
self.assertNotEqual(response.data.get('code', 0), 0)
|
||
|
||
def test_admin_token_cannot_access_app_api(self):
|
||
"""测试Admin Token无法访问App端API"""
|
||
self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {self.admin_token}')
|
||
|
||
url = '/api/v1/users/me/'
|
||
response = self.client.get(url)
|
||
|
||
self.assertNotEqual(response.data.get('code', 0), 0)
|
||
|
||
def test_no_token_cannot_access_protected_api(self):
|
||
"""测试无Token无法访问受保护API"""
|
||
self.client.credentials()
|
||
|
||
url = '/api/v1/users/me/'
|
||
response = self.client.get(url)
|
||
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
|
||
|
||
url = '/api/admin/device-types/'
|
||
response = self.client.get(url)
|
||
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
|