Files

172 lines
7.3 KiB
Python
Raw Permalink 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.
"""病例域 2 条 happy-path 流程测试。"""
from unittest.mock import patch, MagicMock
from django.core.cache import cache
from apps.case.models import CaseBase, TraditionalCase, ScoringRule, CaseExamItem
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-create2 条评分规则)
payload = build_traditional_payload(
department_name='儿科', scoring_rules_count=2, with_exam_items=True,
)
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)
self.assertEqual(len(created['exam_items']), 1)
self.assertEqual(created['exam_items'][0]['item_code'], 'blood_routine')
# 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)
self.assertEqual(len(full['exam_items']), 1)
# 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)
self.assertEqual(CaseExamItem.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))
self.assertEqual(len(created['exam_items']), 1)
# 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-更新标题')