Files
medical_training/test/swagger_profile_config.py
T

148 lines
7.5 KiB
Python
Raw Normal View History

2026-06-08 17:36:03 +08:00
"""
Swagger Try-it-out 等效脚本:仅测配置页三接口。
1) GET /api/user/institution_info/ 机构信息获取
2) GET /api/user/my_departments/ 所属机构科室列表(不分页)
3) POST /api/user/profile/config/ 医学生信息配置
运行方式:.venv\\Scripts\\python.exe test/swagger_profile_config.py
前提:Django dev server 已在 http://127.0.0.1:8000 运行,Redis 已启动。
"""
import sys
import json
import subprocess
import requests
sys.stdout.reconfigure(encoding='utf-8')
sys.stderr.reconfigure(encoding='utf-8')
BASE = 'http://127.0.0.1:8000'
PYTHON = r'D:\01Agent\medical_training\.venv\Scripts\python.exe'
CWD = r'D:\01Agent\medical_training'
PASS, FAIL = 'PASS', 'FAIL'
results = []
TRIAL_INST_CODE = 'PKU_LAB_TRIAL'
STUDENT_PHONE = '13700000055'
def log(api_id, method, url, expected, actual, detail='', resp_body=None):
exp = expected if isinstance(expected, (list, tuple)) else [expected]
status = PASS if actual in exp else FAIL
results.append((api_id, status))
print(f' {status} {api_id:<14} {method:<5} {url:<38} expect={str(expected):<10} got={actual} {detail}')
if resp_body is not None:
body = json.dumps(resp_body, ensure_ascii=False, indent=2)
if len(body) > 1200:
body = body[:1200] + f'... (truncated, {len(body)} chars)'
print(f' <<< {body}')
def django_eval(code):
preamble = (
'import django, os; '
'os.environ.setdefault("DJANGO_SETTINGS_MODULE","config.settings"); '
'django.setup(); '
)
proc = subprocess.run([PYTHON, '-c', preamble + code], capture_output=True, text=True, cwd=CWD)
return proc.stdout.strip()
# ─── 准备:试用机构 + banner + 两个科室 + 学生用户 + token ─────────────────────
print('\n[准备] 配置试用机构 banner、科室、学生用户...')
setup = django_eval(
f'from apps.user.models import User, Institution, Department; '
f'from rest_framework_simplejwt.tokens import RefreshToken; '
f'inst, _ = Institution.objects.get_or_create(code="{TRIAL_INST_CODE}", '
f' defaults={{"name":"北大医学部(实验室)试用","type":"hospital"}}); '
f'inst.banner_url = "institutions/default_hospital.png"; inst.save(update_fields=["banner_url"]); '
f'd1, _ = Department.objects.get_or_create(name="内科", defaults={{"category":"临床"}}); '
f'd2, _ = Department.objects.get_or_create(name="外科", defaults={{"category":"临床"}}); '
2026-06-08 17:36:03 +08:00
f'User.objects.filter(phone="{STUDENT_PHONE}").delete(); '
f'u = User.objects.create_user(username="{STUDENT_PHONE}", password=None, phone="{STUDENT_PHONE}", '
f' real_name="配置页测试学生", role_type="student", institution=inst, status=1); '
f'print(str(RefreshToken.for_user(u).access_token) + "|" + str(d1.id) + "|" + str(d2.id) + "|" + str(inst.id))'
)
access, dept1_id, dept2_id, inst_id = setup.split('|')
auth = {'Authorization': f'Bearer {access}'}
print(f'[准备] 完成 inst_id={inst_id} dept1={dept1_id} dept2={dept2_id}\n')
print('=' * 100)
print(' 配置页三接口 Swagger Try-it-out')
print('=' * 100)
# ── 1. 机构信息获取 ───────────────────────────────────────────────────────────
r = s = requests.get(f'{BASE}/api/user/institution_info/', headers=auth)
body = r.json() if r.headers.get('content-type', '').startswith('application/json') else None
banner = (body or {}).get('banner_url', '')
detail = f'name={body.get("name","")}, banner_ok={banner.endswith("/static/institutions/default_hospital.png")}' if body else ''
log('institution_info', 'GET', '/api/user/institution_info/', 200, r.status_code, detail, resp_body=body)
# 1b. 未登录 → 401
r = requests.get(f'{BASE}/api/user/institution_info/')
log('inst_info-401', 'GET', '/api/user/institution_info/ (anon)', 401, r.status_code)
# ── 2. 所属机构科室列表(不分页)─────────────────────────────────────────────
r = requests.get(f'{BASE}/api/user/my_departments/', headers=auth)
body = r.json() if r.headers.get('content-type', '').startswith('application/json') else None
is_list = isinstance(body, list)
names = [d.get('name') for d in body] if is_list else []
detail = f'not_paginated={is_list}, count={len(names)}, names={names}'
log('my_departments', 'GET', '/api/user/my_departments/', 200, r.status_code, detail, resp_body=body)
# ── 3. 医学生信息配置 ─────────────────────────────────────────────────────────
cfg_body = {'department': int(dept1_id), 'title_name': '住院医师', 'practice_years': '1-3年'}
r = requests.post(f'{BASE}/api/user/profile/config/', json=cfg_body, headers=auth)
body = r.json() if r.headers.get('content-type', '').startswith('application/json') else None
u = (body or {}).get('user', {})
detail = (f'dept={u.get("department")}, title={u.get("title_name")}, '
f'years={u.get("practice_years")}') if body else ''
log('profile_config', 'POST', '/api/user/profile/config/', 200, r.status_code, detail, resp_body=body)
# 3b. 跨机构科室 → 400(用一个属于别的机构的科室)
other_dept = django_eval(
'from apps.user.models import Institution, Department; '
'o, _ = Institution.objects.get_or_create(code="SWAG_OTHER_CFG", defaults={"name":"配置页其它医院","type":"hospital"}); '
'd, _ = Department.objects.get_or_create(institution=o, name="放射科", defaults={"category":"医技"}); '
'print(d.id)'
)
r = requests.post(f'{BASE}/api/user/profile/config/',
json={'department': int(other_dept), 'title_name': '住院医师', 'practice_years': '1-3年'},
headers=auth)
body = r.json() if r.headers.get('content-type', '').startswith('application/json') else None
log('cfg-cross-inst', 'POST', '/api/user/profile/config/ (other inst dept)', 400, r.status_code,
f'code={(body or {}).get("code","")}', resp_body=body)
# 3c. 缺字段 → 400
r = requests.post(f'{BASE}/api/user/profile/config/', json={'title_name': '住院医师'}, headers=auth)
log('cfg-missing', 'POST', '/api/user/profile/config/ (missing fields)', 400, r.status_code)
# 验证落库
db = django_eval(
f'from apps.user.models import User; u=User.objects.get(phone="{STUDENT_PHONE}"); '
f'print(f"{{u.department_id}}|{{u.title_name}}|{{u.practice_years}}")'
)
log('cfg-db-check', 'CHECK', 'user.department/title/years persisted',
f'{dept1_id}|住院医师|1-3年', db)
# ─── 清理 ─────────────────────────────────────────────────────────────────────
django_eval(f'from apps.user.models import User; User.objects.filter(phone="{STUDENT_PHONE}").delete(); print("cleaned")')
# ─── 汇总 ─────────────────────────────────────────────────────────────────────
print('=' * 100)
total = len(results)
passed = sum(1 for _, st in results if st == PASS)
failed = total - passed
print(f' 总计: {total} | 通过: {passed} | 失败: {failed}')
if failed:
print(' 失败:', [aid for aid, st in results if st == FAIL])
sys.exit(1)
print(' ALL PASSED — 配置页三接口验证通过!')
sys.exit(0)