feat: init commint
This commit is contained in:
204
backend/apps/tickets/tests/test_ticket_automation.py
Normal file
204
backend/apps/tickets/tests/test_ticket_automation.py
Normal file
@@ -0,0 +1,204 @@
|
||||
import pytest
|
||||
from django.contrib.auth import get_user_model
|
||||
|
||||
from apps.channels.models import ChannelConversation, ChannelEndpoint, ChannelMessage
|
||||
from apps.solutions.models import TicketResolution
|
||||
from apps.tickets.automation import (
|
||||
apply_automation_action,
|
||||
approve_and_send,
|
||||
close_due_ticket_automation,
|
||||
handle_customer_feedback,
|
||||
handle_customer_reply_delivery_failed,
|
||||
)
|
||||
from apps.tickets.models import (
|
||||
CustomerReplyTask,
|
||||
Ticket,
|
||||
TicketAutomationEvent,
|
||||
TicketAutomationRun,
|
||||
TicketAutomationRunStatus,
|
||||
)
|
||||
from apps.workitems.models import WorkItem, WorkItemStatus, WorkItemType
|
||||
|
||||
|
||||
@pytest.mark.django_db(transaction=True)
|
||||
def test_one_approval_resolves_and_closes_without_duplicate_business_facts():
|
||||
actor = get_user_model().objects.create_superuser(
|
||||
username="automation-approver",
|
||||
password="test-password",
|
||||
)
|
||||
ticket = Ticket.objects.create(
|
||||
ticket_no="TK-AUTO-0001",
|
||||
title="登录回调后仍停留在登录页",
|
||||
description="生产环境登录回调成功,但页面没有跳转。",
|
||||
status="pending_confirm",
|
||||
)
|
||||
reply_task = CustomerReplyTask.objects.create(
|
||||
ticket=ticket,
|
||||
status="draft",
|
||||
draft_text="您好,请重新登录并确认页面已恢复。",
|
||||
final_text="您好,请重新登录并确认页面已恢复。",
|
||||
revision=1,
|
||||
)
|
||||
run = TicketAutomationRun.objects.create(
|
||||
ticket=ticket,
|
||||
status=TicketAutomationRunStatus.WAITING_APPROVAL,
|
||||
current_step="approve_and_send",
|
||||
reply_task=reply_task,
|
||||
policy_snapshot={
|
||||
"policy_version": "test",
|
||||
"auto_close_hours": 24,
|
||||
"max_retries": 2,
|
||||
},
|
||||
)
|
||||
|
||||
approve_and_send(
|
||||
run,
|
||||
actor=actor,
|
||||
expected_reply_revision=1,
|
||||
final_text=reply_task.final_text,
|
||||
resolution_text="认证回调状态已恢复,会话可以正常写入。",
|
||||
manual_delivery_confirmed=True,
|
||||
)
|
||||
|
||||
ticket.refresh_from_db()
|
||||
run.refresh_from_db()
|
||||
reply_task.refresh_from_db()
|
||||
assert ticket.status == "resolved"
|
||||
assert reply_task.status == "sent"
|
||||
assert run.status == TicketAutomationRunStatus.WAITING_CUSTOMER
|
||||
assert run.next_action_at is not None
|
||||
assert TicketResolution.objects.filter(ticket=ticket, source="system").count() == 1
|
||||
assert not WorkItem.objects.filter(
|
||||
ticket=ticket,
|
||||
task_type=WorkItemType.TICKET_CLOSE,
|
||||
status=WorkItemStatus.ACTIVE,
|
||||
).exists()
|
||||
|
||||
# 浏览器重复提交相同批准不会产生第二条回复、解决记录或关闭计划。
|
||||
approve_and_send(
|
||||
run,
|
||||
actor=actor,
|
||||
expected_reply_revision=1,
|
||||
final_text=reply_task.final_text,
|
||||
resolution_text="认证回调状态已恢复,会话可以正常写入。",
|
||||
manual_delivery_confirmed=True,
|
||||
)
|
||||
assert CustomerReplyTask.objects.filter(ticket=ticket).count() == 1
|
||||
assert TicketResolution.objects.filter(ticket=ticket).count() == 1
|
||||
assert TicketAutomationEvent.objects.filter(
|
||||
run=run,
|
||||
event_type="waiting_customer",
|
||||
).count() == 1
|
||||
|
||||
assert close_due_ticket_automation(run.id, force=True) is True
|
||||
assert close_due_ticket_automation(run.id, force=True) is False
|
||||
ticket.refresh_from_db()
|
||||
run.refresh_from_db()
|
||||
assert ticket.status == "closed"
|
||||
assert run.status == TicketAutomationRunStatus.COMPLETED
|
||||
assert TicketAutomationEvent.objects.filter(
|
||||
run=run,
|
||||
event_type="completed",
|
||||
).count() == 1
|
||||
|
||||
|
||||
@pytest.mark.django_db(transaction=True)
|
||||
def test_negative_feedback_after_close_creates_one_linked_follow_up_ticket():
|
||||
source = Ticket.objects.create(
|
||||
ticket_no="TK-AUTO-0002",
|
||||
title="登录问题已关闭",
|
||||
description="登录回调问题已经恢复。",
|
||||
status="closed",
|
||||
priority="high",
|
||||
environment="生产环境",
|
||||
related_system="统一身份平台",
|
||||
)
|
||||
run = TicketAutomationRun.objects.create(
|
||||
ticket=source,
|
||||
status=TicketAutomationRunStatus.COMPLETED,
|
||||
current_step="closed",
|
||||
policy_snapshot={"policy_version": "test"},
|
||||
)
|
||||
|
||||
assert handle_customer_feedback(source.id, "还是不行,登录后继续报错") == (
|
||||
"follow_up_created"
|
||||
)
|
||||
assert handle_customer_feedback(source.id, "还是不行,登录后继续报错") == (
|
||||
"follow_up_created"
|
||||
)
|
||||
|
||||
follow_up = Ticket.objects.get(follow_up_of=source)
|
||||
assert follow_up.status == "pending"
|
||||
assert follow_up.priority == "high"
|
||||
assert follow_up.environment == "生产环境"
|
||||
assert source.follow_up_tickets.count() == 1
|
||||
assert source.events.filter(event_type="follow_up_created").count() == 1
|
||||
assert run.events.filter(event_type="follow_up_created").count() == 1
|
||||
|
||||
|
||||
@pytest.mark.django_db(transaction=True)
|
||||
def test_failed_channel_delivery_retries_the_same_outbound_message(monkeypatch):
|
||||
actor = get_user_model().objects.create_superuser(
|
||||
username="automation-retry",
|
||||
password="test-password",
|
||||
)
|
||||
ticket = Ticket.objects.create(
|
||||
ticket_no="TK-AUTO-0003",
|
||||
title="渠道重试",
|
||||
description="验证渠道失败后的幂等重试。",
|
||||
status="pending_confirm",
|
||||
)
|
||||
reply_task = CustomerReplyTask.objects.create(
|
||||
ticket=ticket,
|
||||
status="approved",
|
||||
draft_text="请确认恢复结果。",
|
||||
final_text="请确认恢复结果。",
|
||||
revision=1,
|
||||
)
|
||||
run = TicketAutomationRun.objects.create(
|
||||
ticket=ticket,
|
||||
status=TicketAutomationRunStatus.WAITING_DELIVERY,
|
||||
current_step="wait_delivery",
|
||||
reply_task=reply_task,
|
||||
approved_by=actor,
|
||||
policy_snapshot={"policy_version": "test"},
|
||||
)
|
||||
endpoint = ChannelEndpoint.objects.create(name="自动化重试渠道")
|
||||
conversation = ChannelConversation.objects.create(
|
||||
endpoint=endpoint,
|
||||
external_conversation_id="retry-conversation",
|
||||
ticket=ticket,
|
||||
)
|
||||
message = ChannelMessage.objects.create(
|
||||
endpoint=endpoint,
|
||||
conversation=conversation,
|
||||
ticket=ticket,
|
||||
customer_reply_task=reply_task,
|
||||
reply_revision=1,
|
||||
direction="outbound",
|
||||
idempotency_key="automation-retry-message",
|
||||
text=reply_task.final_text,
|
||||
status="failed",
|
||||
error_message="上游超时",
|
||||
created_by=actor,
|
||||
)
|
||||
queued = []
|
||||
monkeypatch.setattr(
|
||||
"apps.channels.tasks.deliver_channel_message_task.delay",
|
||||
lambda message_id: queued.append(message_id),
|
||||
)
|
||||
|
||||
handle_customer_reply_delivery_failed(reply_task.id, "上游超时")
|
||||
run.refresh_from_db()
|
||||
assert run.current_step == "delivery_failed"
|
||||
assert run.last_error_code == "channel_delivery_failed"
|
||||
|
||||
apply_automation_action(run, "retry", actor=actor)
|
||||
|
||||
run.refresh_from_db()
|
||||
message.refresh_from_db()
|
||||
assert message.status == "queued"
|
||||
assert message.idempotency_key == "automation-retry-message"
|
||||
assert run.current_step == "wait_delivery"
|
||||
assert run.last_error_message == ""
|
||||
assert queued == [message.id]
|
||||
Reference in New Issue
Block a user