feat: wire feishu notifications into workflows
This commit is contained in:
@@ -13,7 +13,7 @@ from review_agent.models import (
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
|
||||
def test_notify_completion_records_success(django_user_model):
|
||||
def test_notify_completion_records_success(django_user_model, monkeypatch):
|
||||
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-NOTIFY")
|
||||
@@ -33,6 +33,16 @@ def test_notify_completion_records_success(django_user_model):
|
||||
file_name="filled.docx",
|
||||
storage_path="filled.docx",
|
||||
)
|
||||
calls = []
|
||||
fake_record = type(
|
||||
"Record",
|
||||
(),
|
||||
{"send_status": "success", "SendStatus": type("SendStatus", (), {"FAILED": "failed"}), "error_message": ""},
|
||||
)()
|
||||
monkeypatch.setattr(
|
||||
"review_agent.application_form_fill.services.notifier.dispatch_workflow_notification",
|
||||
lambda context: calls.append(context) or fake_record,
|
||||
)
|
||||
|
||||
record = notify_completion(batch, [exported])
|
||||
|
||||
@@ -40,6 +50,7 @@ def test_notify_completion_records_success(django_user_model):
|
||||
assert record.export_ids == [exported.pk]
|
||||
assert record.template_codes == ["registration_certificate"]
|
||||
assert record.sent_at is not None
|
||||
assert calls[0].workflow_type == "application_form_fill"
|
||||
|
||||
|
||||
def test_notify_completion_records_failure_without_raising(django_user_model):
|
||||
|
||||
96
tests/test_feishu_workflow_adapters.py
Normal file
96
tests/test_feishu_workflow_adapters.py
Normal file
@@ -0,0 +1,96 @@
|
||||
import pytest
|
||||
|
||||
from review_agent.models import (
|
||||
ApplicationFormFillBatch,
|
||||
Conversation,
|
||||
ExportedSummaryFile,
|
||||
FileSummaryBatch,
|
||||
RegulatoryIssue,
|
||||
RegulatoryReviewBatch,
|
||||
)
|
||||
from review_agent.notifications.message_builder import absolute_result_url
|
||||
from review_agent.notifications.workflow_adapters import (
|
||||
build_application_form_fill_context,
|
||||
build_file_summary_context,
|
||||
build_regulatory_review_context,
|
||||
)
|
||||
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
|
||||
def test_file_summary_adapter_builds_summary(settings, django_user_model):
|
||||
settings.PUBLIC_BASE_URL = "http://example.test"
|
||||
user = django_user_model.objects.create_user(username="owner", password="pass")
|
||||
conversation = Conversation.objects.create(user=user, title="会话")
|
||||
batch = FileSummaryBatch.objects.create(
|
||||
conversation=conversation,
|
||||
user=user,
|
||||
batch_no="FS-001",
|
||||
status=FileSummaryBatch.Status.SUCCESS,
|
||||
total_files=3,
|
||||
success_files=2,
|
||||
failed_files=1,
|
||||
total_pages=15,
|
||||
)
|
||||
|
||||
context = build_file_summary_context(batch)
|
||||
|
||||
assert context.workflow_type == "file_summary"
|
||||
assert context.workflow_batch_no == "FS-001"
|
||||
assert "异常" in "\n".join(context.summary_lines)
|
||||
assert absolute_result_url(context.result_path).endswith(f"/api/review-agent/file-summary/{batch.pk}/status/")
|
||||
|
||||
|
||||
def test_regulatory_review_adapter_builds_risk_summary(django_user_model):
|
||||
user = django_user_model.objects.create_user(username="owner", password="pass")
|
||||
conversation = Conversation.objects.create(user=user, title="会话")
|
||||
summary_batch = FileSummaryBatch.objects.create(conversation=conversation, user=user, batch_no="FS-RR")
|
||||
batch = RegulatoryReviewBatch.objects.create(
|
||||
conversation=conversation,
|
||||
user=user,
|
||||
source_summary_batch=summary_batch,
|
||||
batch_no="RR-001",
|
||||
status=RegulatoryReviewBatch.Status.SUCCESS,
|
||||
)
|
||||
RegulatoryIssue.objects.create(
|
||||
batch=batch,
|
||||
category=RegulatoryIssue.Category.COMPLETENESS,
|
||||
severity=RegulatoryIssue.Severity.BLOCKING,
|
||||
title="缺少资料",
|
||||
)
|
||||
|
||||
context = build_regulatory_review_context(batch)
|
||||
|
||||
assert context.workflow_type == "regulatory_review"
|
||||
assert "阻断项 1" in "\n".join(context.summary_lines)
|
||||
|
||||
|
||||
def test_application_form_fill_adapter_builds_export_and_conflict_summary(django_user_model):
|
||||
user = django_user_model.objects.create_user(username="owner", password="pass")
|
||||
conversation = Conversation.objects.create(user=user, title="会话")
|
||||
summary_batch = FileSummaryBatch.objects.create(conversation=conversation, user=user, batch_no="FS-AFF")
|
||||
batch = ApplicationFormFillBatch.objects.create(
|
||||
conversation=conversation,
|
||||
user=user,
|
||||
source_summary_batch=summary_batch,
|
||||
batch_no="AFF-001",
|
||||
status=ApplicationFormFillBatch.Status.PARTIAL_SUCCESS,
|
||||
selected_templates=["registration_certificate"],
|
||||
conflict_summary=[{"field": "product_name"}],
|
||||
)
|
||||
ExportedSummaryFile.objects.create(
|
||||
batch=summary_batch,
|
||||
workflow_type="application_form_fill",
|
||||
workflow_batch_id=batch.pk,
|
||||
export_category="filled_template",
|
||||
export_type=ExportedSummaryFile.ExportType.WORD,
|
||||
file_name="filled.docx",
|
||||
storage_path="filled.docx",
|
||||
)
|
||||
|
||||
context = build_application_form_fill_context(batch)
|
||||
|
||||
assert context.workflow_type == "application_form_fill"
|
||||
assert "导出文件 1" in "\n".join(context.summary_lines)
|
||||
assert "冲突字段 1" in "\n".join(context.summary_lines)
|
||||
@@ -71,6 +71,31 @@ def test_start_file_summary_workflow_runs_synchronously_for_tests(django_user_mo
|
||||
assert WorkflowEvent.objects.filter(batch=batch, event_type="workflow_completed").exists()
|
||||
|
||||
|
||||
def test_file_summary_workflow_dispatches_completion_notification(monkeypatch, django_user_model):
|
||||
user = django_user_model.objects.create_user(username="owner", password="pass")
|
||||
conversation = Conversation.objects.create(user=user, title="会话")
|
||||
FileAttachment.objects.create(
|
||||
conversation=conversation,
|
||||
user=user,
|
||||
original_name="a.docx",
|
||||
storage_path="x/a.docx",
|
||||
file_size=1,
|
||||
)
|
||||
batch = create_file_summary_batch(conversation=conversation, user=user)
|
||||
calls = []
|
||||
|
||||
def fake_dispatch(context):
|
||||
calls.append(context)
|
||||
|
||||
monkeypatch.setattr("review_agent.file_summary.workflow.dispatch_workflow_notification", fake_dispatch)
|
||||
|
||||
start_file_summary_workflow(batch, async_run=False)
|
||||
|
||||
assert calls
|
||||
assert calls[-1].workflow_type == "file_summary"
|
||||
assert calls[-1].workflow_batch_id == batch.pk
|
||||
|
||||
|
||||
def test_workflow_extracts_archive_and_scans_extracted_files(settings, tmp_path, django_user_model):
|
||||
settings.MEDIA_ROOT = tmp_path
|
||||
user = django_user_model.objects.create_user(username="owner", password="pass")
|
||||
|
||||
@@ -9,6 +9,7 @@ from review_agent.models import (
|
||||
)
|
||||
from review_agent.regulatory_review.services.export import build_markdown_report, build_result_payload
|
||||
from review_agent.regulatory_review.services.feishu_notifier import create_mock_notifications
|
||||
from review_agent.regulatory_review.workflow import RegulatoryWorkflowExecutor
|
||||
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
@@ -77,3 +78,32 @@ def test_notification_records_enter_reports(django_user_model):
|
||||
|
||||
assert "通知记录" in build_markdown_report(batch)
|
||||
assert build_result_payload(batch)["notifications"][0]["channel"] == "mock"
|
||||
|
||||
|
||||
def test_regulatory_completion_notification_uses_dispatcher(monkeypatch, 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-NOTIFY-DISPATCH",
|
||||
status=FileSummaryBatch.Status.SUCCESS,
|
||||
)
|
||||
batch = RegulatoryReviewBatch.objects.create(
|
||||
conversation=conversation,
|
||||
user=user,
|
||||
source_summary_batch=summary,
|
||||
batch_no="RR-NOTIFY-DISPATCH",
|
||||
status=RegulatoryReviewBatch.Status.SUCCESS,
|
||||
)
|
||||
calls = []
|
||||
|
||||
monkeypatch.setattr(
|
||||
"review_agent.regulatory_review.workflow.dispatch_workflow_notification",
|
||||
lambda context: calls.append(context),
|
||||
)
|
||||
|
||||
RegulatoryWorkflowExecutor(batch)._dispatch_completion_notification()
|
||||
|
||||
assert calls
|
||||
assert calls[0].workflow_type == "regulatory_review"
|
||||
|
||||
Reference in New Issue
Block a user