267 lines
10 KiB
Python
267 lines
10 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
|
|
from agent_framework.analytics.tim_payload_mapper import map_analytics_event_to_tim_flat_payload
|
|
from app.domain.contas.workflow_actions import build_contas_workflow_actions
|
|
|
|
|
|
def _event(result: dict, code: str) -> dict:
|
|
return next(x for x in result.get("business_events", []) if x.get("code") == code)
|
|
|
|
|
|
def test_contestacao_vaa_metadata_preserva_contexto_tim_original():
|
|
class Client:
|
|
def billing_analysis(self, msisdn):
|
|
return {"currentInvoice": [{"desc": "Serviços de valor adicionado", "items": [{"desc": "VAS Musica", "value": 29.9, "classe": "avulso"}]}]}
|
|
|
|
def contestar(self, payload):
|
|
return {"barcode": "8469", "itemsResponse": []}
|
|
|
|
class Service:
|
|
client = Client()
|
|
|
|
action = build_contas_workflow_actions(Service()).get("abrir_contestacao_cliente")
|
|
state = {
|
|
"input": {
|
|
"session_id": "sess-1",
|
|
"message_id": "msg-1",
|
|
"ura_call_id": "CALL-123",
|
|
"channel_id": "URA",
|
|
}
|
|
}
|
|
result = action(
|
|
{
|
|
"msisdn": "11999998888",
|
|
"protocolo_id": "PRT-11",
|
|
"invoice_id": "3000135012",
|
|
"servico": "VAS Musica",
|
|
"valor": 29.9,
|
|
},
|
|
state,
|
|
)
|
|
payload = _event(result, "VAA.006")["payload"]
|
|
assert payload["agentId"] == "contas"
|
|
assert payload["channelId"] == "URA"
|
|
assert payload["gsm"] == "11999998888"
|
|
assert payload["uraCallId"] == "CALL-123"
|
|
assert payload["sessionId"] == "sess-1"
|
|
assert payload["messageId"] == "msg-1"
|
|
assert payload["agentProtocolId"] == "PRT-11"
|
|
assert payload["adjustedProtocol"] == "PRT-11"
|
|
assert payload["billingId"] == "3000135012"
|
|
assert payload["adjustedItems"] == ["VAS Musica"]
|
|
assert payload["adjustedItemsAmount"] == ["29.9"]
|
|
assert json.loads(payload["agentSpecificData"]) == {
|
|
"adjustedItems": ["VAS Musica"],
|
|
"adjustedItemsAmount": ["29.9"],
|
|
}
|
|
|
|
|
|
def test_contestacao_vaa007_falha_preserva_protocolo_e_itens():
|
|
class Client:
|
|
def billing_analysis(self, msisdn):
|
|
return {"currentInvoice": [{"desc": "Serviços de valor adicionado", "items": [{"desc": "VAS Musica", "value": 29.9, "classe": "avulso"}]}]}
|
|
|
|
def contestar(self, payload):
|
|
raise RuntimeError("falha provider")
|
|
|
|
class Service:
|
|
client = Client()
|
|
|
|
action = build_contas_workflow_actions(Service()).get("abrir_contestacao_cliente")
|
|
result = action(
|
|
{"msisdn": "119", "protocolo_id": "PRT-11", "invoice_id": "INV-1", "servico": "VAS Musica", "valor": 29.9},
|
|
{"input": {"channel_id": "URA", "ura_call_id": "CALL-1"}},
|
|
)
|
|
payload = _event(result, "VAA.007")["payload"]
|
|
assert payload["agentProtocolId"] == "PRT-11"
|
|
assert payload["adjustedProtocol"] == "PRT-11"
|
|
assert payload["adjustedItems"] == ["VAS Musica"]
|
|
assert payload["adjustedItemsAmount"] == ["29.9"]
|
|
|
|
|
|
def test_tim_payload_mapper_aceita_agent_specific_data_json_string_historico():
|
|
mapped = map_analytics_event_to_tim_flat_payload(
|
|
"VAA.006",
|
|
{
|
|
"eventType": "VAA.006",
|
|
"payload": {
|
|
"tag": "VAA.006",
|
|
"agentProtocolId": "P1",
|
|
"agentSpecificData": '{"billingId":"3000131180","item":"VAS"}',
|
|
},
|
|
},
|
|
)
|
|
assert mapped["agentProtocolId"] == "P1"
|
|
assert mapped["agentSpecificData"] == {"billingId": "3000131180", "item": "VAS"}
|
|
|
|
|
|
def test_finalizacao_cvn010_preserva_protocolos_e_contexto_ura():
|
|
from app.domain.contas.service import ContasDomainService
|
|
|
|
class Client:
|
|
def abrir_protocolo(self, payload):
|
|
return {"interactionProtocol": "INFO-123"}
|
|
def status_sr(self, payload):
|
|
return {"ok": True}
|
|
|
|
action = build_contas_workflow_actions(ContasDomainService(client=Client())).get("finalizar_atendimento_action")
|
|
result = action(
|
|
{
|
|
"status": "resolvido",
|
|
"summary": "ok",
|
|
"msisdn": "11999999999",
|
|
"social_sec_no": "123",
|
|
"session_id": "sess-final",
|
|
"message_id": "msg-final",
|
|
"ura_call_id": "CALL-FINAL",
|
|
"ura_protocol_id": "URA-123",
|
|
"channel_id": "URA",
|
|
"invoice_id": "3000131180",
|
|
"business_workflows_executed": ["invoice_explanation"],
|
|
"invoice_explanation_base": "Explicação.",
|
|
},
|
|
{"input": {}},
|
|
)
|
|
payload = _event(result, "CVN.010")["payload"]
|
|
assert payload["agentProtocolId"] == "INFO-123"
|
|
assert payload["adjustedProtocol"] == "INFO-123"
|
|
assert payload["uraProtocolId"] == "URA-123"
|
|
assert payload["uraCallId"] == "CALL-FINAL"
|
|
assert payload["channelId"] == "URA"
|
|
assert payload["billingId"] == "3000131180"
|
|
assert json.loads(payload["agentSpecificData"]) == {"billingId": "3000131180"}
|
|
|
|
|
|
def test_vas_estrategico_veb_preserva_turno_e_contexto_ura():
|
|
class Client:
|
|
def abrir_protocolo(self, payload):
|
|
return {"interactionProtocol": "VEB-123"}
|
|
|
|
class Service:
|
|
client = Client()
|
|
|
|
action = build_contas_workflow_actions(Service()).get("registrar_atendimento_vas_estrategico")
|
|
result = action(
|
|
{
|
|
"linhas": [{"msisdn": "5511999999999"}],
|
|
"mensagem_base": "Informação sobre cancelamento.",
|
|
},
|
|
{
|
|
"input": {
|
|
"resposta_usuario": "NAO",
|
|
"customer_message": "Não quero manter o serviço.",
|
|
"ura_call_id": "155344608",
|
|
"channel_id": "ura",
|
|
"message_id": "msg-005",
|
|
}
|
|
},
|
|
)
|
|
codes = [x["code"] for x in result["business_events"]]
|
|
assert codes == ["VEB.004", "VEB.006", "VEB.007"]
|
|
payload = _event(result, "VEB.004")["payload"]
|
|
assert payload["uraCallId"] == "155344608"
|
|
assert payload["channelId"] == "ura"
|
|
assert payload["messageId"] == "msg-005"
|
|
assert payload["customerMessage"] == "NAO"
|
|
assert payload["llmResponse"] == "Informação sobre cancelamento."
|
|
|
|
|
|
def test_finalizacao_mpi006_e_sad001_preservam_metadata_conversacional():
|
|
from app.domain.contas.service import ContasDomainService
|
|
|
|
class Client:
|
|
def abrir_protocolo(self, payload):
|
|
return {"interactionProtocol": "INFO-321"}
|
|
def status_sr(self, payload):
|
|
return {"ok": True}
|
|
|
|
action = build_contas_workflow_actions(ContasDomainService(client=Client())).get("finalizar_atendimento_action")
|
|
result = action(
|
|
{
|
|
"status": "resolvido",
|
|
"summary": "Cliente entendeu.",
|
|
"msisdn": "11999999999",
|
|
"invoice_id": "3000131180",
|
|
"business_workflows_executed": ["invoice_explanation"],
|
|
"invoice_explanation_base": "Explicação dos valores da fatura",
|
|
"customer_message": "Sim, entendi",
|
|
"llm_response": "Perfeito, fico feliz em ajudar.",
|
|
"message_id": "msg-final",
|
|
"session_id": "sess-final",
|
|
"channel_id": "URA",
|
|
"ura_call_id": "CALL-FINAL",
|
|
"session_end_at": "2026-06-11T12:00:00Z",
|
|
},
|
|
{"input": {}},
|
|
)
|
|
mpi = _event(result, "MPI.006")["payload"]
|
|
assert mpi["customerMessage"] == "Sim, entendi"
|
|
assert mpi["llmResponse"] == "Perfeito, fico feliz em ajudar."
|
|
assert mpi["billingId"] == "3000131180"
|
|
assert mpi["messageId"] == "msg-final"
|
|
assert mpi["sessionId"] == "sess-final"
|
|
|
|
sad = _event(result, "SAD.001")["payload"]
|
|
assert sad["llmResponse"] == "Perfeito, fico feliz em ajudar."
|
|
assert sad["messageId"] == "msg-final"
|
|
assert sad["sessionEndAt"] == 1781179200000
|
|
|
|
|
|
def test_vas_bundle_nao_defera_protocolo_e_emite_veb005():
|
|
class Client:
|
|
def __init__(self): self.calls = 0
|
|
def abrir_protocolo(self, payload):
|
|
self.calls += 1
|
|
return {"interactionProtocol": "SHOULD-NOT-HAPPEN"}
|
|
|
|
client = Client()
|
|
class Service: pass
|
|
service = Service(); service.client = client
|
|
action = build_contas_workflow_actions(service).get("registrar_atendimento_vas_estrategico")
|
|
result = action(
|
|
{
|
|
"linhas": [{"msisdn": "5511999999999", "bundle_names": ["TIM Segurança"]}],
|
|
"mensagem_base": "Bundle incluso no plano.",
|
|
},
|
|
{"input": {"resposta_usuario": "NAO", "ura_call_id": "CALL-1", "channel_id": "URA", "message_id": "M1"}},
|
|
)
|
|
assert [x["code"] for x in result["business_events"]] == ["VEB.004", "VEB.005", "VEB.007"]
|
|
assert client.calls == 0
|
|
assert result["protocol_closed"] is False
|
|
assert result["protocol_deferred_to_finalization"] is True
|
|
assert result["suppress_cvn_protocol_ic"] is True
|
|
assert result["informational_vas_types"] == ["bundle"]
|
|
|
|
|
|
def test_invoice_explanation_mpi005_preserva_turno_e_contexto():
|
|
class Service: pass
|
|
action = build_contas_workflow_actions(Service()).get("registrar_atendimento_invoice_explanation")
|
|
result = action(
|
|
{"caminho": "NAO", "invoice_id": "INV-9"},
|
|
{"input": {"customer_message": "Ainda não entendi", "message_id": "M9", "session_id": "S9", "channel_id": "URA", "ura_call_id": "C9"}},
|
|
)
|
|
payload = _event(result, "MPI.005")["payload"]
|
|
assert payload["customerMessage"] == "Ainda não entendi"
|
|
assert payload["messageId"] == "M9"
|
|
assert payload["sessionId"] == "S9"
|
|
assert payload["channelId"] == "URA"
|
|
assert payload["uraCallId"] == "C9"
|
|
assert payload["billingId"] == "INV-9"
|
|
|
|
|
|
def test_pro_rata_mpi010_preserva_contexto_do_turno():
|
|
class Service: pass
|
|
action = build_contas_workflow_actions(Service()).get("preparar_pro_rata")
|
|
result = action(
|
|
{"planos": [], "msisdn": "11999999999"},
|
|
{"input": {"customer_message": "Por que veio proporcional?", "message_id": "MP1", "session_id": "SP1", "channel_id": "web"}},
|
|
)
|
|
payload = _event(result, "MPI.010")["payload"]
|
|
assert payload["gsm"] == "11999999999"
|
|
assert payload["customerMessage"] == "Por que veio proporcional?"
|
|
assert payload["messageId"] == "MP1"
|
|
assert payload["sessionId"] == "SP1"
|
|
assert payload["channelId"] == "web"
|