266 lines
10 KiB
Python
266 lines
10 KiB
Python
import pytest
|
||
from django.urls import reverse
|
||
|
||
from review_agent.models import (
|
||
Conversation,
|
||
FileSummaryBatch,
|
||
FileSummaryItem,
|
||
RegulatoryArtifact,
|
||
RegulatoryNotificationRecord,
|
||
RegulatoryReviewBatch,
|
||
WorkflowNotificationRecord,
|
||
WorkflowNodeRun,
|
||
)
|
||
|
||
|
||
pytestmark = pytest.mark.django_db
|
||
|
||
|
||
def test_workspace_renders_regulatory_workflow_card(client, django_user_model):
|
||
user = django_user_model.objects.create_user(username="owner", password="pass")
|
||
conversation = Conversation.objects.create(user=user, title="会话")
|
||
summary = FileSummaryBatch.objects.create(
|
||
conversation=conversation,
|
||
user=user,
|
||
batch_no="FS-OK",
|
||
status=FileSummaryBatch.Status.SUCCESS,
|
||
)
|
||
regulatory = RegulatoryReviewBatch.objects.create(
|
||
conversation=conversation,
|
||
user=user,
|
||
source_summary_batch=summary,
|
||
batch_no="RR-CARD",
|
||
status=RegulatoryReviewBatch.Status.SUCCESS,
|
||
risk_summary={"blocking": 1, "high": 1},
|
||
)
|
||
WorkflowNodeRun.objects.create(
|
||
workflow_type="regulatory_review",
|
||
workflow_batch_id=regulatory.pk,
|
||
node_group="regulatory_review",
|
||
node_code="risk_assess",
|
||
node_name="风险评估",
|
||
status=WorkflowNodeRun.Status.SUCCESS,
|
||
progress=100,
|
||
)
|
||
client.force_login(user)
|
||
|
||
response = client.get(f"{reverse('chat')}?conversation={conversation.pk}")
|
||
|
||
content = response.content.decode("utf-8")
|
||
assert "RR-CARD" in content
|
||
assert 'data-workflow-type="regulatory_review"' in content
|
||
assert "阻断项 1" in content
|
||
assert "风险评估" in content
|
||
assert "data-regulatory-status-url-template" in content
|
||
|
||
|
||
def test_workspace_renders_condition_confirmation_form(client, django_user_model):
|
||
user = django_user_model.objects.create_user(username="owner", password="pass")
|
||
conversation = Conversation.objects.create(user=user, title="会话")
|
||
summary = FileSummaryBatch.objects.create(
|
||
conversation=conversation,
|
||
user=user,
|
||
batch_no="FS-OK",
|
||
status=FileSummaryBatch.Status.SUCCESS,
|
||
)
|
||
regulatory = RegulatoryReviewBatch.objects.create(
|
||
conversation=conversation,
|
||
user=user,
|
||
source_summary_batch=summary,
|
||
batch_no="RR-WAIT",
|
||
status=RegulatoryReviewBatch.Status.WAITING_USER,
|
||
condition_json={
|
||
"confirmed": False,
|
||
"candidates": {
|
||
"product_category": {
|
||
"label": "产品类别",
|
||
"input_type": "select",
|
||
"options": ["体外诊断试剂", "医疗器械", "其他"],
|
||
"suggested": "体外诊断试剂",
|
||
},
|
||
"product_name": {
|
||
"label": "产品名称",
|
||
"input_type": "text",
|
||
"suggested": "甲胎蛋白检测试剂盒",
|
||
},
|
||
},
|
||
},
|
||
)
|
||
WorkflowNodeRun.objects.create(
|
||
workflow_type="regulatory_review",
|
||
workflow_batch_id=regulatory.pk,
|
||
node_group="condition_confirm",
|
||
node_code="condition_confirm",
|
||
node_name="适用条件确认",
|
||
status=WorkflowNodeRun.Status.WAITING_USER,
|
||
progress=50,
|
||
)
|
||
client.force_login(user)
|
||
|
||
response = client.get(f"{reverse('chat')}?conversation={conversation.pk}")
|
||
|
||
content = response.content.decode("utf-8")
|
||
assert "适用条件确认" in content
|
||
assert "data-condition-confirm-form" in content
|
||
assert "体外诊断试剂" in content
|
||
assert "甲胎蛋白检测试剂盒" in content
|
||
form_index = content.index("data-condition-confirm-form")
|
||
summary_index = content.index('id="summaryPanel"')
|
||
assert form_index < summary_index
|
||
assert "data-condition-confirm-form" not in content[summary_index:]
|
||
|
||
|
||
def test_workspace_refreshes_incomplete_condition_confirmation_candidates(client, settings, tmp_path, django_user_model):
|
||
settings.MEDIA_ROOT = tmp_path
|
||
user = django_user_model.objects.create_user(username="owner", password="pass")
|
||
conversation = Conversation.objects.create(user=user, title="会话")
|
||
summary = FileSummaryBatch.objects.create(
|
||
conversation=conversation,
|
||
user=user,
|
||
batch_no="FS-OK",
|
||
status=FileSummaryBatch.Status.SUCCESS,
|
||
product_name="第1章 监管信息",
|
||
)
|
||
application = tmp_path / "application.txt"
|
||
application.write_text(
|
||
"卡尤迪生物科技宜兴有限公司申请境内第三类体外诊断试剂"
|
||
"呼吸道合胞病毒、肺炎支原体核酸检测试剂盒(荧光PCR法)产品注册。",
|
||
encoding="utf-8",
|
||
)
|
||
FileSummaryItem.objects.create(
|
||
batch=summary,
|
||
file_index=1,
|
||
directory_level="第1章 监管信息",
|
||
file_name="符合标准的清单.txt",
|
||
file_type="txt",
|
||
relative_path="第1章 监管信息/符合标准的清单.txt",
|
||
storage_path=str(application),
|
||
)
|
||
RegulatoryReviewBatch.objects.create(
|
||
conversation=conversation,
|
||
user=user,
|
||
source_summary_batch=summary,
|
||
batch_no="RR-WAIT-EMPTY",
|
||
status=RegulatoryReviewBatch.Status.WAITING_USER,
|
||
condition_json={
|
||
"confirmed": False,
|
||
"candidates": {
|
||
"product_category": {"label": "产品类别", "input_type": "select", "options": ["其他"], "suggested": "其他"},
|
||
"product_name": {"label": "产品名称", "input_type": "text", "suggested": ""},
|
||
},
|
||
},
|
||
)
|
||
client.force_login(user)
|
||
|
||
response = client.get(f"{reverse('chat')}?conversation={conversation.pk}")
|
||
|
||
content = response.content.decode("utf-8")
|
||
assert "体外诊断试剂" in content
|
||
assert "呼吸道合胞病毒、肺炎支原体核酸检测试剂盒(荧光PCR法)" in content
|
||
|
||
|
||
def test_workspace_renders_rectification_actions_and_summaries(client, tmp_path, django_user_model):
|
||
user = django_user_model.objects.create_user(username="owner", password="pass")
|
||
conversation = Conversation.objects.create(user=user, title="会话")
|
||
summary = FileSummaryBatch.objects.create(
|
||
conversation=conversation,
|
||
user=user,
|
||
batch_no="FS-OK",
|
||
status=FileSummaryBatch.Status.SUCCESS,
|
||
)
|
||
regulatory = RegulatoryReviewBatch.objects.create(
|
||
conversation=conversation,
|
||
user=user,
|
||
source_summary_batch=summary,
|
||
batch_no="RR-RECTIFY",
|
||
status=RegulatoryReviewBatch.Status.SUCCESS,
|
||
)
|
||
record_path = tmp_path / "review_record.json"
|
||
record_path.write_text('{"items":[{"status":"review_passed"}]}', encoding="utf-8")
|
||
RegulatoryArtifact.objects.create(
|
||
batch=regulatory,
|
||
artifact_type=RegulatoryArtifact.ArtifactType.JSON,
|
||
name="review_record.json",
|
||
storage_path=str(record_path),
|
||
metadata={"artifact": "review_record"},
|
||
)
|
||
RegulatoryNotificationRecord.objects.create(
|
||
batch=regulatory,
|
||
channel=RegulatoryNotificationRecord.Channel.MOCK,
|
||
target="法规整改负责人",
|
||
status=RegulatoryNotificationRecord.Status.SENT,
|
||
payload={"title": "缺少申请表"},
|
||
)
|
||
client.force_login(user)
|
||
|
||
response = client.get(f"{reverse('chat')}?conversation={conversation.pk}")
|
||
|
||
content = response.content.decode("utf-8")
|
||
assert "data-rectification-action=\"full-review\"" in content
|
||
assert "data-rectification-action=\"issue-review\"" in content
|
||
assert "通知 1" in content
|
||
assert "复核记录 1" in content
|
||
|
||
|
||
def test_frontend_selects_status_url_by_workflow_type():
|
||
script = open("static/js/app.js", encoding="utf-8").read()
|
||
|
||
assert "workflow_type" in script
|
||
assert "data-regulatory-status-url-template" in script
|
||
assert "statusUrlForWorkflow" in script
|
||
assert "bindConditionConfirmForms" in script
|
||
assert "data-condition-confirm-form" in script
|
||
assert "ensureConditionConfirmationCard" in script
|
||
assert "condition_confirmation" in script
|
||
assert "bindRectificationActionButtons" in script
|
||
assert "data-rectification-action" in script
|
||
|
||
|
||
def test_frontend_polls_regulatory_workflow_with_explicit_workflow_type():
|
||
script = open("static/js/app.js", encoding="utf-8").read()
|
||
|
||
assert "function startWorkflowPolling(batchId, workflow_type)" in script
|
||
assert "startWorkflowPolling(payload.batch_id, payload.workflow_type)" in script
|
||
assert 'startWorkflowPolling(batchId, "regulatory_review")' in script
|
||
assert 'workflow_type || (card ? card.getAttribute("data-workflow-type") || "file_summary" : "file_summary")' in script
|
||
|
||
|
||
def test_frontend_keeps_single_condition_confirmation_prompt():
|
||
script = open("static/js/app.js", encoding="utf-8").read()
|
||
|
||
assert "data-condition-confirmation-card" in script
|
||
assert "removeStaleConditionConfirmationCards" in script
|
||
assert '[data-condition-confirmation-card]' in script
|
||
|
||
|
||
def test_regulatory_status_includes_failed_feishu_notification(client, django_user_model):
|
||
user = django_user_model.objects.create_user(username="owner", password="pass")
|
||
conversation = Conversation.objects.create(user=user, title="会话")
|
||
summary = FileSummaryBatch.objects.create(conversation=conversation, user=user, batch_no="FS-RR")
|
||
batch = RegulatoryReviewBatch.objects.create(
|
||
conversation=conversation,
|
||
user=user,
|
||
source_summary_batch=summary,
|
||
batch_no="RR-FEISHU",
|
||
)
|
||
WorkflowNotificationRecord.objects.create(
|
||
workflow_type="regulatory_review",
|
||
workflow_batch_id=batch.pk,
|
||
workflow_batch_no=batch.batch_no,
|
||
workflow_status=batch.status,
|
||
dedupe_key=f"regulatory_review:{batch.pk}:{batch.status}",
|
||
trigger_user=user,
|
||
channel=WorkflowNotificationRecord.Channel.FEISHU_API,
|
||
target="负责人",
|
||
send_status=WorkflowNotificationRecord.SendStatus.FAILED,
|
||
message_title="法规核查完成",
|
||
error_message="bad receive_id",
|
||
)
|
||
client.force_login(user)
|
||
|
||
response = client.get(f"/api/review-agent/regulatory-review/{batch.pk}/status/")
|
||
|
||
payload = response.json()
|
||
assert payload["latest_notification"]["status_label"] == "飞书通知失败"
|
||
assert payload["latest_notification"]["error_message"] == "bad receive_id"
|