660 lines
24 KiB
Python
660 lines
24 KiB
Python
from fastapi.testclient import TestClient
|
|
|
|
from services.customer_service.app import app as customer_app
|
|
from services.interaction_service.app import app as interaction_app
|
|
from services.ivr_service.app import app as ivr_app
|
|
from services.routing_service.app import app as routing_app
|
|
from services.shared.db import get_session
|
|
from services.shared.sql_models import (
|
|
AsteriskCallLinkRow,
|
|
CallRecordingRow,
|
|
Customer,
|
|
CustomerExternalIdentity,
|
|
Queue,
|
|
TelegramMessageRow,
|
|
TelegramThreadRow,
|
|
VoiceAISessionRow,
|
|
VoiceTranscriptSegmentRow,
|
|
)
|
|
|
|
|
|
def test_customer_and_interaction_lifecycle():
|
|
customer_client = TestClient(customer_app)
|
|
interaction_client = TestClient(interaction_app)
|
|
|
|
customer = customer_client.post(
|
|
"/customers",
|
|
json={"display_name": "Test Client", "phones": ["+77010000001"], "preferred_phone": "+77010000001"},
|
|
)
|
|
assert customer.status_code == 200
|
|
customer_id = customer.json()["customer_id"]
|
|
|
|
interaction = interaction_client.post(
|
|
"/interactions",
|
|
json={
|
|
"channel": "voice",
|
|
"subject": "Need support",
|
|
"customer_id": customer_id,
|
|
"queue_id": "q_main",
|
|
"priority": 2,
|
|
},
|
|
headers={"X-User": "operator", "X-Role": "operator"},
|
|
)
|
|
assert interaction.status_code == 200
|
|
interaction_id = interaction.json()["interaction_id"]
|
|
|
|
assigned = interaction_client.patch(
|
|
f"/interactions/{interaction_id}/assign",
|
|
json={"assignee": "operator_a"},
|
|
headers={"X-User": "supervisor", "X-Role": "supervisor"},
|
|
)
|
|
assert assigned.status_code == 200
|
|
assert assigned.json()["status"] == "in_progress"
|
|
|
|
escalated = interaction_client.post(
|
|
f"/interactions/{interaction_id}/escalate",
|
|
json={"target_queue_id": "line2"},
|
|
headers={"X-User": "operator", "X-Role": "operator"},
|
|
)
|
|
assert escalated.status_code == 200
|
|
assert escalated.json()["status"] == "escalated"
|
|
|
|
closed = interaction_client.patch(
|
|
f"/interactions/{interaction_id}/status",
|
|
json={"status": "closed"},
|
|
headers={"X-User": "operator", "X-Role": "operator"},
|
|
)
|
|
assert closed.status_code == 200
|
|
assert closed.json()["status"] == "closed"
|
|
|
|
timeline = interaction_client.get(f"/interactions/{interaction_id}/timeline")
|
|
assert timeline.status_code == 200
|
|
assert len(timeline.json()["events"]) >= 4
|
|
|
|
|
|
def test_customer_history_aggregates_telegram_and_voice_context():
|
|
customer_client = TestClient(customer_app)
|
|
interaction_client = TestClient(interaction_app)
|
|
|
|
customer = customer_client.post(
|
|
"/customers",
|
|
json={
|
|
"display_name": "History Client",
|
|
"phones": ["+77010000019"],
|
|
"preferred_phone": "+77010000019",
|
|
"tags": ["priority"],
|
|
},
|
|
)
|
|
assert customer.status_code == 200
|
|
customer_id = customer.json()["customer_id"]
|
|
|
|
voice_interaction = interaction_client.post(
|
|
"/interactions",
|
|
json={
|
|
"channel": "voice",
|
|
"subject": "Need callback after AI handoff",
|
|
"customer_id": customer_id,
|
|
"queue_id": "q_voice",
|
|
"priority": 2,
|
|
},
|
|
headers={"X-User": "operator", "X-Role": "operator"},
|
|
)
|
|
assert voice_interaction.status_code == 200
|
|
voice_interaction_id = voice_interaction.json()["interaction_id"]
|
|
|
|
telegram_interaction = interaction_client.post(
|
|
"/interactions",
|
|
json={
|
|
"channel": "telegram",
|
|
"subject": "Order issue in Telegram",
|
|
"customer_id": customer_id,
|
|
"queue_id": "q_chat",
|
|
"priority": 3,
|
|
},
|
|
headers={"X-User": "operator", "X-Role": "operator"},
|
|
)
|
|
assert telegram_interaction.status_code == 200
|
|
telegram_interaction_id = telegram_interaction.json()["interaction_id"]
|
|
|
|
assigned = interaction_client.patch(
|
|
f"/interactions/{telegram_interaction_id}/assign",
|
|
json={"assignee": "operator_a"},
|
|
headers={"X-User": "supervisor", "X-Role": "supervisor"},
|
|
)
|
|
assert assigned.status_code == 200
|
|
|
|
session = get_session()
|
|
try:
|
|
session.add(
|
|
TelegramThreadRow(
|
|
thread_id="tgt_history_customer",
|
|
chat_id="chat_history_customer",
|
|
interaction_id=telegram_interaction_id,
|
|
telegram_user_id="tg_hist_1",
|
|
username="history_client",
|
|
display_name="History Client",
|
|
queue_id="q_chat",
|
|
status="in_progress",
|
|
claimed_by_user="operator_a",
|
|
claimed_at="2026-04-03T09:05:00+00:00",
|
|
ai_session_id="ais_hist_1",
|
|
ai_state="human_owned",
|
|
ai_handoff_reason="Нужно проверить статус заказа вручную",
|
|
ai_last_model_at="2026-04-03T09:04:00+00:00",
|
|
last_message_at="2026-04-03T09:06:00+00:00",
|
|
last_message_preview="Клиент просит уточнить статус заказа",
|
|
created_at="2026-04-03T09:00:00+00:00",
|
|
updated_at="2026-04-03T09:06:00+00:00",
|
|
)
|
|
)
|
|
session.add(
|
|
TelegramMessageRow(
|
|
message_id="tgm_history_customer_in",
|
|
thread_id="tgt_history_customer",
|
|
interaction_id=telegram_interaction_id,
|
|
chat_id="chat_history_customer",
|
|
text="Клиент просит уточнить статус заказа",
|
|
customer_external_id="telegram:history_client",
|
|
direction="inbound",
|
|
telegram_message_id_external="501",
|
|
operator_user=None,
|
|
author_type="customer",
|
|
author_id="tg_hist_1",
|
|
delivery_status=None,
|
|
payload_json="{}",
|
|
created_at="2026-04-03T09:06:00+00:00",
|
|
)
|
|
)
|
|
session.add(
|
|
AsteriskCallLinkRow(
|
|
call_id="call_history_customer",
|
|
linked_id="linked_history_customer",
|
|
queue_code="voice_support",
|
|
queue_id="q_voice",
|
|
interaction_id=voice_interaction_id,
|
|
caller_number="+77010000019",
|
|
caller_name="History Client",
|
|
status="active",
|
|
telephony_status="connected",
|
|
claimed_by_user="operator_a",
|
|
claimed_at="2026-04-03T09:11:00+00:00",
|
|
operator_extension="2001",
|
|
channel_name="PJSIP/2001-000001",
|
|
voice_session_id="vas_history_1",
|
|
ai_session_id="ais_voice_1",
|
|
ai_state="handoff_required",
|
|
ai_handoff_reason="AI не смог подтвердить оплату по заказу",
|
|
ai_last_model_at="2026-04-03T09:12:00+00:00",
|
|
started_at="2026-04-03T09:10:00+00:00",
|
|
connected_at="2026-04-03T09:11:00+00:00",
|
|
ended_at=None,
|
|
updated_at="2026-04-03T09:12:00+00:00",
|
|
)
|
|
)
|
|
session.add(
|
|
CallRecordingRow(
|
|
recording_id="rec_history_customer",
|
|
channel="voice",
|
|
call_id="call_history_customer",
|
|
interaction_id=voice_interaction_id,
|
|
source_event_id="evt_history_customer",
|
|
file_name="history-call.wav",
|
|
storage_backend="local_fs",
|
|
storage_path="history/history-call.wav",
|
|
mime_type="audio/wav",
|
|
size_bytes=1024,
|
|
duration_seconds=48,
|
|
checksum_sha256="abc123",
|
|
status="ready",
|
|
recorded_at="2026-04-03T09:13:00+00:00",
|
|
created_at="2026-04-03T09:13:00+00:00",
|
|
updated_at="2026-04-03T09:13:00+00:00",
|
|
archived_at=None,
|
|
)
|
|
)
|
|
session.add(
|
|
VoiceTranscriptSegmentRow(
|
|
segment_id="seg_history_customer",
|
|
session_id="vas_history_1",
|
|
call_id="call_history_customer",
|
|
interaction_id=voice_interaction_id,
|
|
speaker="caller",
|
|
source_type="asr",
|
|
sequence_no=1,
|
|
text="Мне нужно, чтобы оператор подтвердил оплату по счёту",
|
|
confidence=0.93,
|
|
is_final=True,
|
|
barge_in_interrupted=False,
|
|
payload_json="{}",
|
|
created_at="2026-04-03T09:11:30+00:00",
|
|
)
|
|
)
|
|
session.commit()
|
|
finally:
|
|
session.close()
|
|
|
|
history = customer_client.get(f"/customers/{customer_id}/history")
|
|
|
|
assert history.status_code == 200
|
|
payload = history.json()
|
|
assert payload["customer"]["customer_id"] == customer_id
|
|
assert payload["summary"]["open_cases"] == 2
|
|
assert payload["summary"]["primary_phone"] == "+77010000019"
|
|
assert payload["summary"]["primary_telegram_thread_id"] == "tgt_history_customer"
|
|
assert set(payload["summary"]["active_channels"]) == {"telegram", "voice"}
|
|
assert {item["interaction_id"] for item in payload["interactions"]} == {
|
|
voice_interaction_id,
|
|
telegram_interaction_id,
|
|
}
|
|
assert payload["telegram_threads"][0]["thread_id"] == "tgt_history_customer"
|
|
assert payload["live_calls"][0]["call_id"] == "call_history_customer"
|
|
assert payload["live_calls"][0]["has_recording"] is True
|
|
titles = [item["title"] for item in payload["history"]]
|
|
assert "Telegram диалог" in titles
|
|
assert "Обращение назначено" in titles
|
|
assert any(title.startswith("Звонок •") for title in titles)
|
|
assert any(item["body"] == "Клиент просит уточнить статус заказа" for item in payload["history"])
|
|
assert any("оператор" in (item["note"] or "") for item in payload["history"])
|
|
|
|
|
|
def test_operator_can_fix_customer_name_and_sync_voice_context():
|
|
customer_client = TestClient(customer_app)
|
|
interaction_client = TestClient(interaction_app)
|
|
|
|
customer = customer_client.post(
|
|
"/customers",
|
|
json={
|
|
"display_name": "Номер 1001",
|
|
"phones": ["+77010001001"],
|
|
"preferred_phone": "+77010001001",
|
|
},
|
|
)
|
|
assert customer.status_code == 200
|
|
customer_id = customer.json()["customer_id"]
|
|
|
|
interaction = interaction_client.post(
|
|
"/interactions",
|
|
json={
|
|
"channel": "voice",
|
|
"subject": "Inbound call 1001",
|
|
"customer_id": customer_id,
|
|
"queue_id": "q_voice",
|
|
"priority": 2,
|
|
},
|
|
headers={"X-User": "operator", "X-Role": "operator"},
|
|
)
|
|
assert interaction.status_code == 200
|
|
interaction_id = interaction.json()["interaction_id"]
|
|
|
|
session = get_session()
|
|
try:
|
|
session.add(
|
|
CustomerExternalIdentity(
|
|
identity_id="cei_voice_fix_name",
|
|
customer_id=customer_id,
|
|
channel="voice",
|
|
external_subject="+77010001001",
|
|
display_name_snapshot="Номер 1001",
|
|
created_at="2026-04-05T09:00:00+00:00",
|
|
updated_at="2026-04-05T09:00:00+00:00",
|
|
)
|
|
)
|
|
session.add(
|
|
AsteriskCallLinkRow(
|
|
call_id="call_fix_name_customer",
|
|
linked_id="linked_fix_name_customer",
|
|
queue_code="voice_support",
|
|
queue_id="q_voice",
|
|
interaction_id=interaction_id,
|
|
caller_number="+77010001001",
|
|
caller_name="Номер 1001",
|
|
status="active",
|
|
telephony_status="connected",
|
|
claimed_by_user="operator_a",
|
|
claimed_at="2026-04-05T09:01:00+00:00",
|
|
operator_extension="2001",
|
|
channel_name="PJSIP/2001-000010",
|
|
voice_session_id="vas_fix_name_customer",
|
|
ai_session_id="ais_fix_name_customer",
|
|
ai_state="human_owned",
|
|
ai_handoff_reason="Оператор уточняет имя клиента",
|
|
ai_last_model_at="2026-04-05T09:02:00+00:00",
|
|
voice_start_language="ru",
|
|
customer_name_status="name_followup_required",
|
|
customer_name_value="Айдос?",
|
|
customer_name_source="voice_followup",
|
|
customer_name_resolved_at="2026-04-05T09:02:00+00:00",
|
|
started_at="2026-04-05T09:00:00+00:00",
|
|
connected_at="2026-04-05T09:01:00+00:00",
|
|
ended_at=None,
|
|
updated_at="2026-04-05T09:02:00+00:00",
|
|
)
|
|
)
|
|
session.add(
|
|
VoiceAISessionRow(
|
|
session_id="vas_fix_name_customer",
|
|
call_id="call_fix_name_customer",
|
|
linked_id="linked_fix_name_customer",
|
|
interaction_id=interaction_id,
|
|
customer_id=customer_id,
|
|
queue_id="q_voice",
|
|
ai_session_id="ais_fix_name_customer",
|
|
agent_profile="voice_support",
|
|
language="ru",
|
|
asr_provider="mock",
|
|
tts_provider="mock",
|
|
status="handoff_requested",
|
|
handoff_reason="Оператор уточняет имя клиента",
|
|
handoff_target_queue_id="q_voice",
|
|
voice_start_language="ru",
|
|
customer_name_status="name_followup_required",
|
|
customer_name_value="Айдос?",
|
|
customer_name_source="voice_followup",
|
|
customer_name_resolved_at="2026-04-05T09:02:00+00:00",
|
|
media_uuid=None,
|
|
media_status=None,
|
|
media_connected_at=None,
|
|
media_ended_at=None,
|
|
last_media_frame_at=None,
|
|
disclosure_played_at=None,
|
|
last_user_utterance_at=None,
|
|
last_ai_reply_at=None,
|
|
started_at="2026-04-05T09:00:00+00:00",
|
|
updated_at="2026-04-05T09:02:00+00:00",
|
|
ended_at=None,
|
|
)
|
|
)
|
|
session.commit()
|
|
finally:
|
|
session.close()
|
|
|
|
updated = customer_client.patch(
|
|
f"/customers/{customer_id}",
|
|
json={"display_name": "Айдос", "source": "manual"},
|
|
)
|
|
assert updated.status_code == 200
|
|
assert updated.json()["display_name"] == "Айдос"
|
|
|
|
session = get_session()
|
|
try:
|
|
customer_row = session.query(Customer).filter(Customer.customer_id == customer_id).one()
|
|
identity_row = session.query(CustomerExternalIdentity).filter(CustomerExternalIdentity.customer_id == customer_id).one()
|
|
call_row = session.query(AsteriskCallLinkRow).filter(AsteriskCallLinkRow.call_id == "call_fix_name_customer").one()
|
|
voice_row = session.query(VoiceAISessionRow).filter(VoiceAISessionRow.call_id == "call_fix_name_customer").one()
|
|
assert customer_row.display_name == "Айдос"
|
|
assert identity_row.display_name_snapshot == "Айдос"
|
|
assert call_row.caller_name == "Айдос"
|
|
assert call_row.customer_name_status == "name_obtained"
|
|
assert call_row.customer_name_value == "Айдос"
|
|
assert call_row.customer_name_source == "manual"
|
|
assert voice_row.customer_name_status == "name_obtained"
|
|
assert voice_row.customer_name_value == "Айдос"
|
|
assert voice_row.customer_name_source == "manual"
|
|
finally:
|
|
session.close()
|
|
|
|
history = customer_client.get(f"/customers/{customer_id}/history")
|
|
assert history.status_code == 200
|
|
assert history.json()["customer"]["display_name"] == "Айдос"
|
|
assert history.json()["live_calls"][0]["caller_name"] == "Айдос"
|
|
|
|
|
|
def test_queue_rules_and_route():
|
|
routing_client = TestClient(routing_app)
|
|
|
|
created = routing_client.post(
|
|
"/queues",
|
|
json={
|
|
"name": "General Queue",
|
|
"description": "Main line",
|
|
"rules": [
|
|
{
|
|
"channel": "voice",
|
|
"priority": 2,
|
|
"strategy": "round_robin",
|
|
"sla_seconds": 45,
|
|
}
|
|
],
|
|
},
|
|
headers={"X-User": "admin", "X-Role": "admin"},
|
|
)
|
|
assert created.status_code == 200
|
|
queue_id = created.json()["queue_id"]
|
|
|
|
route = routing_client.post(
|
|
f"/queues/{queue_id}/route?channel=voice&priority=2",
|
|
headers={"X-User": "supervisor", "X-Role": "supervisor"},
|
|
)
|
|
assert route.status_code == 200
|
|
assert route.json()["sla_seconds"] == 45
|
|
|
|
|
|
def test_list_queues_tolerates_legacy_rules_json_object():
|
|
routing_client = TestClient(routing_app)
|
|
session = get_session()
|
|
try:
|
|
session.add(
|
|
Queue(
|
|
queue_id="que_legacy_rules",
|
|
name="Legacy Queue",
|
|
description="Created before rules_json normalization",
|
|
rules_json="{}",
|
|
created_at="2026-03-15T00:00:00+00:00",
|
|
)
|
|
)
|
|
session.commit()
|
|
finally:
|
|
session.close()
|
|
|
|
listed = routing_client.get(
|
|
"/queues",
|
|
headers={"X-User": "admin", "X-Role": "admin"},
|
|
)
|
|
assert listed.status_code == 200
|
|
payload = listed.json()
|
|
queue = next(item for item in payload if item["queue_id"] == "que_legacy_rules")
|
|
assert queue["rules"] == []
|
|
|
|
|
|
def test_queue_route_requires_privileged_role():
|
|
routing_client = TestClient(routing_app)
|
|
|
|
created = routing_client.post(
|
|
"/queues",
|
|
json={
|
|
"name": "Guarded Queue",
|
|
"description": "RBAC",
|
|
"rules": [
|
|
{
|
|
"channel": "voice",
|
|
"priority": 3,
|
|
"strategy": "round_robin",
|
|
"sla_seconds": 30,
|
|
}
|
|
],
|
|
},
|
|
headers={"X-User": "admin", "X-Role": "admin"},
|
|
)
|
|
assert created.status_code == 200
|
|
|
|
denied = routing_client.post(
|
|
f"/queues/{created.json()['queue_id']}/route?channel=voice&priority=3",
|
|
headers={"X-User": "operator", "X-Role": "operator"},
|
|
)
|
|
assert denied.status_code == 403
|
|
|
|
|
|
def test_admin_can_delete_queue():
|
|
routing_client = TestClient(routing_app)
|
|
|
|
created = routing_client.post(
|
|
"/queues",
|
|
json={
|
|
"name": "Disposable Queue",
|
|
"description": "To delete",
|
|
"rules": [
|
|
{
|
|
"channel": "voice",
|
|
"priority": 3,
|
|
"strategy": "round_robin",
|
|
"sla_seconds": 30,
|
|
}
|
|
],
|
|
},
|
|
headers={"X-User": "admin", "X-Role": "admin"},
|
|
)
|
|
assert created.status_code == 200
|
|
queue_id = created.json()["queue_id"]
|
|
|
|
deleted = routing_client.delete(
|
|
f"/queues/{queue_id}",
|
|
headers={"X-User": "admin", "X-Role": "admin"},
|
|
)
|
|
assert deleted.status_code == 200
|
|
assert deleted.json()["deleted"] is True
|
|
|
|
route = routing_client.post(
|
|
f"/queues/{queue_id}/route?channel=voice&priority=3",
|
|
headers={"X-User": "admin", "X-Role": "admin"},
|
|
)
|
|
assert route.status_code == 404
|
|
|
|
|
|
def test_supervisor_cannot_delete_queue():
|
|
routing_client = TestClient(routing_app)
|
|
|
|
created = routing_client.post(
|
|
"/queues",
|
|
json={
|
|
"name": "Protected Queue",
|
|
"description": "RBAC delete",
|
|
"rules": [
|
|
{
|
|
"channel": "voice",
|
|
"priority": 3,
|
|
"strategy": "round_robin",
|
|
"sla_seconds": 30,
|
|
}
|
|
],
|
|
},
|
|
headers={"X-User": "admin", "X-Role": "admin"},
|
|
)
|
|
assert created.status_code == 200
|
|
|
|
denied = routing_client.delete(
|
|
f"/queues/{created.json()['queue_id']}",
|
|
headers={"X-User": "supervisor", "X-Role": "supervisor"},
|
|
)
|
|
assert denied.status_code == 403
|
|
|
|
|
|
def test_route_supports_completed_ivr_session_and_rejects_incomplete_session():
|
|
routing_client = TestClient(routing_app)
|
|
ivr_client = TestClient(ivr_app)
|
|
|
|
origin_queue = routing_client.post(
|
|
"/queues",
|
|
json={
|
|
"name": "IVR Origin Queue",
|
|
"description": "voice origin",
|
|
"rules": [
|
|
{
|
|
"channel": "voice",
|
|
"priority": 3,
|
|
"strategy": "round_robin",
|
|
"sla_seconds": 30,
|
|
}
|
|
],
|
|
},
|
|
headers={"X-User": "admin", "X-Role": "admin"},
|
|
)
|
|
assert origin_queue.status_code == 200
|
|
origin_queue_id = origin_queue.json()["queue_id"]
|
|
|
|
resolved_queue = routing_client.post(
|
|
"/queues",
|
|
json={
|
|
"name": "IVR Resolved Queue",
|
|
"description": "voice support",
|
|
"rules": [
|
|
{
|
|
"channel": "voice",
|
|
"priority": 3,
|
|
"strategy": "round_robin",
|
|
"sla_seconds": 55,
|
|
}
|
|
],
|
|
},
|
|
headers={"X-User": "admin", "X-Role": "admin"},
|
|
)
|
|
assert resolved_queue.status_code == 200
|
|
resolved_queue_id = resolved_queue.json()["queue_id"]
|
|
|
|
flow = ivr_client.post(
|
|
"/ivr/flows",
|
|
json={
|
|
"name": "Route IVR",
|
|
"description": "route override",
|
|
"queue_id": origin_queue_id,
|
|
"entry_node_id": "root",
|
|
"flow_json": {
|
|
"nodes": [
|
|
{
|
|
"node_id": "root",
|
|
"prompt_text": "Press 2 for support",
|
|
"is_terminal": False,
|
|
"invalid_target_node_id": "root",
|
|
"options": [{"digit": "2", "target_node_id": "support"}],
|
|
},
|
|
{
|
|
"node_id": "support",
|
|
"prompt_text": "Support queue",
|
|
"is_terminal": True,
|
|
"outcome_code": "support_route",
|
|
"resolved_queue_id": resolved_queue_id,
|
|
"resolved_queue_code": "support",
|
|
"options": [],
|
|
},
|
|
]
|
|
},
|
|
"is_active": True,
|
|
},
|
|
headers={"X-User": "admin", "X-Role": "admin"},
|
|
)
|
|
assert flow.status_code == 200
|
|
|
|
active_session = ivr_client.post(
|
|
"/ivr/sessions/start",
|
|
json={"call_id": "call_route_ivr", "queue_id": origin_queue_id},
|
|
headers={"X-User": "supervisor", "X-Role": "supervisor"},
|
|
)
|
|
assert active_session.status_code == 200
|
|
session_id = active_session.json()["session"]["session_id"]
|
|
|
|
incomplete = routing_client.post(
|
|
f"/queues/{origin_queue_id}/route?channel=voice&priority=3&ivr_session_id={session_id}",
|
|
headers={"X-User": "supervisor", "X-Role": "supervisor"},
|
|
)
|
|
assert incomplete.status_code == 400
|
|
|
|
stepped = ivr_client.post(
|
|
f"/ivr/sessions/{session_id}/dtmf",
|
|
json={"digit": "2"},
|
|
headers={"X-User": "supervisor", "X-Role": "supervisor"},
|
|
)
|
|
assert stepped.status_code == 200
|
|
assert stepped.json()["session"]["status"] == "completed"
|
|
|
|
routed = routing_client.post(
|
|
f"/queues/{origin_queue_id}/route?channel=voice&priority=3&ivr_session_id={session_id}",
|
|
headers={"X-User": "supervisor", "X-Role": "supervisor"},
|
|
)
|
|
assert routed.status_code == 200
|
|
payload = routed.json()
|
|
assert payload["queue_id"] == resolved_queue_id
|
|
assert payload["original_queue_id"] == origin_queue_id
|
|
assert payload["resolved_queue_id"] == resolved_queue_id
|
|
assert payload["ivr_session_id"] == session_id
|
|
assert payload["ivr_outcome_code"] == "support_route"
|
|
assert payload["sla_seconds"] == 55
|