165 lines
7.0 KiB
Python
165 lines
7.0 KiB
Python
"""病例域 2 条 happy-path 流程测试。"""
|
||
|
||
from unittest.mock import patch, MagicMock
|
||
|
||
from django.core.cache import cache
|
||
|
||
from apps.case.models import CaseBase, TraditionalCase, ScoringRule
|
||
from apps.user.throttling import PdfParseUserThrottle, ScoringRuleGenerateUserThrottle
|
||
from .conftest import (
|
||
CacheTestCase,
|
||
CASE_PARSE_URL, CASE_GENERATE_RULES_URL, CASE_FULL_CREATE_URL,
|
||
case_full_url, create_test_user, get_auth_client, ensure_department,
|
||
build_traditional_payload, make_fake_pdf,
|
||
MOCK_C1_PARSE_RESULT, MOCK_C2_SCORING_RULES,
|
||
)
|
||
|
||
|
||
class CaseFormHappyPathTest(CacheTestCase):
|
||
"""HP-5: 表单录入 → full-create → GET → PATCH → GET 验证"""
|
||
|
||
def setUp(self):
|
||
super().setUp()
|
||
self.user = create_test_user(phone='13900100001', password='CaseTest1')
|
||
self.client = get_auth_client(self.user)
|
||
ensure_department('儿科')
|
||
|
||
def test_flow_form_create_read_update(self):
|
||
"""HP-5: C3 full-create → C4 GET full → C5 PATCH → C4 GET verify"""
|
||
# C3: full-create(2 条评分规则)
|
||
payload = build_traditional_payload(department_name='儿科', scoring_rules_count=2)
|
||
resp = self.client.post(CASE_FULL_CREATE_URL, payload, format='json')
|
||
self.assertEqual(resp.status_code, 201, resp.content)
|
||
|
||
created = resp.json()
|
||
case_id = created['case']['id']
|
||
self.assertEqual(created['case']['case_type'], 'traditional')
|
||
self.assertEqual(created['case']['publish_status'], 0) # 草稿
|
||
self.assertIn('traditional', created)
|
||
self.assertIsNotNone(created['traditional'])
|
||
self.assertEqual(len(created['scoring_rules']), 2)
|
||
|
||
# C4: GET full
|
||
resp = self.client.get(case_full_url(case_id))
|
||
self.assertEqual(resp.status_code, 200, resp.content)
|
||
full = resp.json()
|
||
self.assertEqual(full['case']['title'], payload['title'])
|
||
self.assertEqual(len(full['scoring_rules']), 2)
|
||
|
||
# C5: PATCH(改标题 + 改子表 + 替换为 1 条评分规则)
|
||
patch_data = {
|
||
'title': '更新后的标题',
|
||
'traditional': {
|
||
'standard_diagnosis': '更新后的诊断',
|
||
},
|
||
'scoring_rules': [
|
||
{
|
||
'dimension': '更新后的维度',
|
||
'score_weight': 1.0,
|
||
'ai_auto_score': False,
|
||
'scoring_standard': '更新后的标准',
|
||
},
|
||
],
|
||
}
|
||
resp = self.client.patch(case_full_url(case_id), patch_data, format='json')
|
||
self.assertEqual(resp.status_code, 200, resp.content)
|
||
|
||
# C4: GET 验证更新
|
||
resp = self.client.get(case_full_url(case_id))
|
||
self.assertEqual(resp.status_code, 200, resp.content)
|
||
full = resp.json()
|
||
self.assertEqual(full['case']['title'], '更新后的标题')
|
||
self.assertEqual(full['traditional']['standard_diagnosis'], '更新后的诊断')
|
||
self.assertEqual(len(full['scoring_rules']), 1)
|
||
self.assertEqual(full['scoring_rules'][0]['dimension'], '更新后的维度')
|
||
|
||
# 验证 DB
|
||
case = CaseBase.objects.get(id=case_id)
|
||
self.assertEqual(case.title, '更新后的标题')
|
||
self.assertEqual(ScoringRule.objects.filter(case_id=case_id).count(), 1)
|
||
tc = TraditionalCase.objects.get(case_id=case_id)
|
||
self.assertEqual(tc.standard_diagnosis, '更新后的诊断')
|
||
|
||
|
||
class CasePdfMockHappyPathTest(CacheTestCase):
|
||
"""HP-6: PDF 解析(mock AI)→ 生成评分规则 → full-create → GET → PATCH"""
|
||
|
||
def setUp(self):
|
||
super().setUp()
|
||
self.user = create_test_user(phone='13900100002', password='CaseTest2')
|
||
self.client = get_auth_client(self.user)
|
||
ensure_department('儿科')
|
||
|
||
@patch('apps.case.services.case_importer.extract_text_from_pdfs',
|
||
return_value='患儿,男,4岁,因发热3天就诊。体温38.5°C...')
|
||
def test_flow_pdf_mock_full_pipeline(self, mock_pdf):
|
||
"""HP-6: C1 parse-pdf → C2 generate-scoring-rules → C3 full-create → C4 GET → C5 PATCH"""
|
||
|
||
# mock call_deepseek: 第 1 次返回 C1 解析结果,第 2 次返回 C2 评分规则
|
||
call_count = {'n': 0}
|
||
def mock_deepseek(system_prompt, user_content):
|
||
call_count['n'] += 1
|
||
if call_count['n'] == 1:
|
||
return MOCK_C1_PARSE_RESULT
|
||
return MOCK_C2_SCORING_RULES
|
||
|
||
with (
|
||
patch('apps.case.services.deepseek_client.call_deepseek', side_effect=mock_deepseek),
|
||
patch.object(PdfParseUserThrottle, 'allow_request', return_value=True),
|
||
patch.object(ScoringRuleGenerateUserThrottle, 'allow_request', return_value=True),
|
||
):
|
||
# C1: parse-pdf
|
||
fake_pdf = make_fake_pdf()
|
||
resp = self.client.post(
|
||
CASE_PARSE_URL,
|
||
{'files': fake_pdf, 'case_type': 'traditional'},
|
||
format='multipart',
|
||
)
|
||
self.assertEqual(resp.status_code, 200, resp.content)
|
||
parse_result = resp.json()
|
||
self.assertIn('parse_id', parse_result)
|
||
self.assertEqual(parse_result['case_type'], 'traditional')
|
||
data = parse_result['data']
|
||
self.assertEqual(data['case_type'], 'traditional')
|
||
self.assertIn('traditional', data)
|
||
self.assertNotIn('scoring_rules', data)
|
||
|
||
# C2: generate-scoring-rules
|
||
resp = self.client.post(
|
||
CASE_GENERATE_RULES_URL,
|
||
data,
|
||
format='json',
|
||
)
|
||
self.assertEqual(resp.status_code, 200, resp.content)
|
||
gen_result = resp.json()
|
||
self.assertGreaterEqual(gen_result['generated'], 1)
|
||
scoring_rules = gen_result['scoring_rules']
|
||
|
||
# C3: full-create(组装 C1 data + C2 scoring_rules)
|
||
create_payload = {**data}
|
||
create_payload['scoring_rules'] = scoring_rules
|
||
create_payload['parse_id'] = parse_result['parse_id']
|
||
resp = self.client.post(CASE_FULL_CREATE_URL, create_payload, format='json')
|
||
self.assertEqual(resp.status_code, 201, resp.content)
|
||
created = resp.json()
|
||
case_id = created['case']['id']
|
||
self.assertEqual(len(created['scoring_rules']), len(scoring_rules))
|
||
|
||
# C4: GET full
|
||
resp = self.client.get(case_full_url(case_id))
|
||
self.assertEqual(resp.status_code, 200, resp.content)
|
||
full = resp.json()
|
||
self.assertEqual(full['case']['id'], case_id)
|
||
self.assertEqual(full['case']['case_type'], 'traditional')
|
||
|
||
# C5: PATCH
|
||
resp = self.client.patch(case_full_url(case_id), {
|
||
'title': 'AI-更新标题',
|
||
}, format='json')
|
||
self.assertEqual(resp.status_code, 200, resp.content)
|
||
|
||
# 验证 PATCH 生效
|
||
resp = self.client.get(case_full_url(case_id))
|
||
self.assertEqual(resp.status_code, 200, resp.content)
|
||
self.assertEqual(resp.json()['case']['title'], 'AI-更新标题')
|