Files
medical_training/test/test_cms_user.py
T

171 lines
7.9 KiB
Python
Raw Normal View History

"""CMS 超级管理员 - 用户管理接口测试(CMS-USER-1~8)。"""
import io
from openpyxl import Workbook
from django.core.files.uploadedfile import SimpleUploadedFile
from rest_framework.test import APIClient
from apps.user.models import User
from .conftest import CacheTestCase, create_test_user, get_auth_client, ensure_institution
CMS_USER_URL = '/api/cms/users/'
XLSX_CT = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
def u_detail(pk):
return f'/api/cms/users/{pk}/'
def make_xlsx(headers, rows):
wb = Workbook(); ws = wb.active
ws.append(headers)
for r in rows:
ws.append(r)
buf = io.BytesIO(); wb.save(buf); buf.seek(0)
return SimpleUploadedFile('import.xlsx', buf.read(), content_type=XLSX_CT)
def super_client(phone='13922200001'):
admin = create_test_user(phone=phone, password='Admin123', role_type='super_admin')
return get_auth_client(admin), admin
class CmsUserPermissionTest(CacheTestCase):
def test_requires_auth(self):
self.assertEqual(APIClient().get(CMS_USER_URL).status_code, 401)
def test_non_manager_forbidden(self):
# 用户管理仅超管 + 医院管理员;其它角色 403
for role in ('student', 'doctor', 'content_admin'):
u = create_test_user(phone={'student': '13922200010', 'doctor': '13922200011',
'content_admin': '13922200012'}[role], role_type=role)
resp = get_auth_client(u).get(CMS_USER_URL)
self.assertEqual(resp.status_code, 403, f'{role}: {resp.content}')
self.assertEqual(resp.json()['code'], 'CMS_PERMISSION_DENIED')
class CmsUserCrudTest(CacheTestCase):
def setUp(self):
super().setUp()
self.client, self.admin = super_client()
self.inst = ensure_institution(name='测试医院', code='CU-H001')
def test_list(self):
create_test_user(phone='13922200021', real_name='学生甲', role_type='student')
resp = self.client.get(CMS_USER_URL)
self.assertEqual(resp.status_code, 200, resp.content)
self.assertIn('results', resp.json())
def test_create_success(self):
resp = self.client.post(CMS_USER_URL, {
'phone': '13922200030', 'real_name': '李医生',
'role_type': 'doctor', 'institution': self.inst.id,
})
self.assertEqual(resp.status_code, 201, resp.content)
self.assertEqual(resp.json()['phone'], '13922200030')
self.assertEqual(resp.json()['role_type'], 'doctor')
user = User.objects.get(phone='13922200030')
self.assertTrue(user.check_password('Pass13922200030')) # 默认密码
def test_create_role_required(self):
resp = self.client.post(CMS_USER_URL, {
'phone': '13922200031', 'real_name': '无角色', 'institution': self.inst.id})
self.assertEqual(resp.status_code, 400, resp.content)
def test_create_institution_required(self):
resp = self.client.post(CMS_USER_URL, {
'phone': '13922200032', 'real_name': '无机构', 'role_type': 'student'})
self.assertEqual(resp.status_code, 400, resp.content)
self.assertEqual(resp.json()['code'], 'CMS_VALIDATION_ERROR')
def test_create_bad_phone(self):
resp = self.client.post(CMS_USER_URL, {
'phone': '123', 'real_name': 'x', 'role_type': 'student', 'institution': self.inst.id})
self.assertEqual(resp.status_code, 400, resp.content)
def test_create_duplicate_phone(self):
create_test_user(phone='13922200040', role_type='student')
resp = self.client.post(CMS_USER_URL, {
'phone': '13922200040', 'real_name': 'dup', 'role_type': 'student',
'institution': self.inst.id})
self.assertEqual(resp.status_code, 400, resp.content)
self.assertEqual(resp.json()['code'], 'CMS_USER_PHONE_EXISTS')
def test_edit_partial_keeps_role_institution(self):
"""方案B:只改姓名、不带角色/机构 → 200,角色与机构保持原值。"""
u = create_test_user(phone='13922200050', real_name='原名', role_type='student',
institution=self.inst)
resp = self.client.patch(u_detail(u.id), {'real_name': '新名'})
self.assertEqual(resp.status_code, 200, resp.content)
u.refresh_from_db()
self.assertEqual(u.real_name, '新名')
self.assertEqual(u.role_type, 'student') # 未传 → 保持
self.assertEqual(u.institution_id, self.inst.id) # 未传 → 保持
def test_edit_cannot_blank_role(self):
"""方案B:传了 role_type 但为空 → 400(不可清空角色)。"""
u = create_test_user(phone='13922200051', real_name='x', role_type='student',
institution=self.inst)
resp = self.client.patch(u_detail(u.id), {'role_type': ''})
self.assertEqual(resp.status_code, 400, resp.content)
self.assertEqual(resp.json()['code'], 'CMS_VALIDATION_ERROR')
def test_edit_cannot_blank_institution(self):
"""方案B:传了 institution=null → 400(不可清空机构)。"""
u = create_test_user(phone='13922200052', real_name='x', role_type='student',
institution=self.inst)
resp = self.client.patch(u_detail(u.id), {'institution': None}, format='json')
self.assertEqual(resp.status_code, 400, resp.content)
self.assertEqual(resp.json()['code'], 'CMS_VALIDATION_ERROR')
def test_soft_delete(self):
u = create_test_user(phone='13922200060', role_type='student')
resp = self.client.delete(u_detail(u.id))
self.assertEqual(resp.status_code, 204, resp.content)
self.assertFalse(User.objects.filter(id=u.id).exists()) # 默认管理器过滤
obj = User.all_objects.get(id=u.id)
self.assertTrue(obj.is_deleted) # 实际未物删
def test_reset_password(self):
u = create_test_user(phone='13922200070', password='OldPass1', role_type='student')
resp = self.client.post(f'/api/cms/users/{u.id}/reset-password/', {})
self.assertEqual(resp.status_code, 200, resp.content)
self.assertEqual(resp.json()['password'], 'Pass13922200070')
u.refresh_from_db()
self.assertTrue(u.check_password('Pass13922200070'))
def test_import_template(self):
resp = self.client.get('/api/cms/users/import-template/')
self.assertEqual(resp.status_code, 200, resp.content)
self.assertEqual(resp['Content-Type'], XLSX_CT)
def test_export(self):
create_test_user(phone='13922200080', role_type='student')
resp = self.client.get('/api/cms/users/export/')
self.assertEqual(resp.status_code, 200)
self.assertEqual(resp['Content-Type'], XLSX_CT)
def test_import_users(self):
f = make_xlsx(
['手机号', '姓名', '角色', '机构编码'],
[
['13922200091', '导入甲', '医生', 'CU-H001'], # ✅
['13922200092', '导入乙', 'student', ''], # 机构编码空 → 失败
['bad', '格式错', '学生', 'CU-H001'], # 手机号格式错
['13922200093', '角色错', '不存在角色', 'CU-H001'], # 角色非法
],
)
resp = self.client.post('/api/cms/users/import/', {'file': f}, format='multipart')
self.assertEqual(resp.status_code, 200, resp.content)
body = resp.json()
self.assertEqual(body['total'], 4)
self.assertEqual(body['success'], 1)
self.assertEqual(body['failed'], 3)
self.assertTrue(User.objects.filter(phone='13922200091', role_type='doctor').exists())
def test_import_no_file(self):
resp = self.client.post('/api/cms/users/import/', {}, format='multipart')
self.assertEqual(resp.status_code, 400, resp.content)
self.assertEqual(resp.json()['code'], 'CMS_IMPORT_FILE_REQUIRED')