Files
medical_training/test/test_cms_user.py
T

191 lines
8.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""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 u_update(pk):
return f'/api/cms/users/{pk}/update/' # 编辑:POST(原 PATCH /{id}/
def u_disable(pk):
return f'/api/cms/users/{pk}/disable/' # 停用:POST(原 DELETE /{id}/
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.post(u_update(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.post(u_update(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.post(u_update(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.post(u_disable(u.id))
self.assertEqual(resp.status_code, 200, 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_recreate_soft_deleted_phone_returns_400(self):
"""软删后用相同手机号重建:返回 400 CMS_USER_PHONE_EXISTS(不产生重复行)。"""
u = create_test_user(phone='13922200061', role_type='student', institution=self.inst)
self.client.post(u_disable(u.id))
self.assertFalse(User.objects.filter(phone='13922200061').exists())
resp = self.client.post(CMS_USER_URL, {
'phone': '13922200061', 'real_name': '重建', 'role_type': 'student',
'institution': self.inst.id})
self.assertEqual(resp.status_code, 400, resp.content)
self.assertEqual(resp.json()['code'], 'CMS_USER_PHONE_EXISTS')
self.assertEqual(User.all_objects.filter(phone='13922200061').count(), 1)
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')