Projeto do Agent Contas ORACLE
This commit is contained in:
324
tests/migration/test_finalization_parity_extended.py
Normal file
324
tests/migration/test_finalization_parity_extended.py
Normal file
@@ -0,0 +1,324 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from app.domain.contas.service import ContasDomainService
|
||||
from app.domain.contas.workflow_actions import build_contas_workflow_actions
|
||||
|
||||
|
||||
class _Client:
|
||||
def __init__(self, *, protocol="INFO-123", fail=False):
|
||||
self.protocol = protocol
|
||||
self.fail = fail
|
||||
self.protocol_calls = []
|
||||
self.status_calls = []
|
||||
def abrir_protocolo(self, payload):
|
||||
self.protocol_calls.append(dict(payload))
|
||||
if self.fail:
|
||||
raise RuntimeError("falha protocolo")
|
||||
return {"interactionProtocol": self.protocol}
|
||||
def status_sr(self, payload):
|
||||
self.status_calls.append(dict(payload)); return {"ok": True}
|
||||
|
||||
|
||||
def _action(client):
|
||||
service = ContasDomainService(client=client)
|
||||
reg = build_contas_workflow_actions(service)
|
||||
return reg.get("finalizar_atendimento_action")
|
||||
|
||||
|
||||
def _codes(result):
|
||||
return [x["code"] for x in result.get("business_events") or []]
|
||||
|
||||
|
||||
def test_invoice_explanation_finalization_orders_events_and_returns_alias():
|
||||
client = _Client()
|
||||
result = _action(client)({
|
||||
"status": "resolvido", "summary": "Atendimento concluído.",
|
||||
"msisdn": "11999999999", "social_sec_no": "123", "message_id": "m1",
|
||||
"business_workflows_executed": ["invoice_explanation"],
|
||||
"invoice_explanation_base": "Explicação dos valores da fatura.",
|
||||
}, {"input": {}})
|
||||
assert result["finalizacao_protocol"] == "INFO-123"
|
||||
assert _codes(result) == ["CVN.008", "RCT.085", "CVN.010", "MPI.006", "SAD.001"]
|
||||
|
||||
|
||||
def test_invoice_explanation_negative_emits_cvn009_mpi005_without_protocol_when_no_notes():
|
||||
client = _Client()
|
||||
result = _action(client)({
|
||||
"status": "nao_resolvido", "summary": "Cliente não concordou.",
|
||||
"business_workflows_executed": ["invoice_explanation"],
|
||||
}, {"input": {}})
|
||||
assert client.protocol_calls == []
|
||||
assert _codes(result) == ["CVN.009", "MPI.005", "SAD.001"]
|
||||
|
||||
|
||||
def test_handoff_suppression_emits_only_sad001():
|
||||
client = _Client()
|
||||
result = _action(client)({
|
||||
"status": "nao_resolvido", "summary": "ATH",
|
||||
"business_workflows_executed": ["invoice_explanation"],
|
||||
"invoice_explanation_base": "Explicação.",
|
||||
"suppress_cvn_009_on_finalize": True,
|
||||
"suppress_cvn_lookup_on_finalize": True,
|
||||
"suppress_informational_protocol_on_finalize": True,
|
||||
}, {"input": {}})
|
||||
assert client.protocol_calls == []
|
||||
assert _codes(result) == ["SAD.001"]
|
||||
|
||||
|
||||
def test_protocol_failure_does_not_fail_finalization_and_emits_negative_events():
|
||||
client = _Client(fail=True)
|
||||
result = _action(client)({
|
||||
"status": "resolvido", "summary": "ok", "msisdn": "119",
|
||||
"business_workflows_executed": ["invoice_explanation"],
|
||||
"invoice_explanation_base": "Explicação da fatura.",
|
||||
}, {"input": {}})
|
||||
assert result["success"] is True
|
||||
assert "finalizacao_protocol" not in result
|
||||
assert _codes(result) == ["CVN.008", "RCT.086", "CVN.011", "MPI.006", "SAD.001"]
|
||||
|
||||
|
||||
def test_transactional_workflow_does_not_emit_informational_acceptance():
|
||||
client = _Client()
|
||||
result = _action(client)({
|
||||
"status": "resolvido", "summary": "concluído", "protocol_closed": True,
|
||||
"protocol_number": "PRT-1", "business_workflows_executed": ["contestacao_tool"],
|
||||
}, {"input": {}})
|
||||
assert _codes(result) == ["SAD.001"]
|
||||
|
||||
|
||||
def test_sad_decision_tree_handoff_second_time():
|
||||
client = _Client()
|
||||
result = _action(client)({
|
||||
"status": "nao_resolvido", "summary": "ATH",
|
||||
"sad_exit_decision_enabled": True, "human_handoff": True, "ath_first_time": False,
|
||||
}, {"input": {}})
|
||||
assert _codes(result) == ["SAD.001", "SAD.003", "SAD.004", "SAD.006"]
|
||||
|
||||
def test_invoice_explanation_uses_canonical_notes_not_free_text():
|
||||
client = _Client()
|
||||
result = _action(client)({
|
||||
"status": "resolvido", "summary": "ok", "msisdn": "119",
|
||||
"business_workflows_executed": ["invoice_explanation"],
|
||||
"invoice_explanation_base": "Resumo qualquer vindo do prefetch e do LLM.",
|
||||
}, {"input": {}})
|
||||
assert client.protocol_calls[0]["serviceRequestNotes"] == "Explicação dos valores da fatura"
|
||||
assert result["finalizacao_protocol"] == "INFO-123"
|
||||
|
||||
|
||||
def test_strategic_partner_is_classified_without_invoice_detail():
|
||||
client = _Client()
|
||||
result = _action(client)({
|
||||
"status": "resolvido", "summary": "ok", "msisdn": "119",
|
||||
"informational_service_names": ["Paramount+ Mensal"],
|
||||
}, {"input": {}})
|
||||
assert client.protocol_calls[0]["serviceRequestNotes"] == "Explicação de VAS Estratégico"
|
||||
assert result["finalizacao_protocol"] == "INFO-123"
|
||||
|
||||
|
||||
def test_known_avulso_is_classified_without_invoice_detail():
|
||||
client = _Client()
|
||||
result = _action(client)({
|
||||
"status": "resolvido", "summary": "ok", "msisdn": "119",
|
||||
"informational_service_names": ["TIM Fashion Mensal"],
|
||||
}, {"input": {}})
|
||||
assert client.protocol_calls[0]["serviceRequestNotes"] == "Explicação de VAS Avulso"
|
||||
assert result["finalizacao_protocol"] == "INFO-123"
|
||||
|
||||
|
||||
def test_prefetch_invoice_context_guarantees_lookup_and_rt15_without_latched_workflow():
|
||||
client = _Client()
|
||||
result = _action(client)({
|
||||
"status": "resolvido_outros_assuntos",
|
||||
"summary": "Encerramento realizado pelo agente.",
|
||||
"msisdn": "11999999999",
|
||||
"invoice_id": "3000131181",
|
||||
"invoice_explanation_base": "Resumo da fatura carregado no prefetch.",
|
||||
}, {"input": {}})
|
||||
assert result["finalizacao_protocol"] == "INFO-123"
|
||||
assert client.protocol_calls[0]["serviceRequestNotes"] == "Explicação dos valores da fatura"
|
||||
assert _codes(result) == ["CVN.002", "CVN.006", "CVN.008", "RCT.085", "CVN.010", "MPI.006", "SAD.001"]
|
||||
|
||||
|
||||
def test_unresolved_transition_does_not_reemit_positive_cvn_or_mpi():
|
||||
client = _Client()
|
||||
result = _action(client)({
|
||||
"status": "resolvido",
|
||||
"summary": "Cliente aceitou no fluxo de negócio.",
|
||||
"invoice_id": "3000131182",
|
||||
"conversation_unresolved_transition_emitted": True,
|
||||
"invoice_explanation_base": "Resumo da fatura.",
|
||||
}, {"input": {}})
|
||||
assert _codes(result) == ["SAD.001"]
|
||||
|
||||
|
||||
def test_terminal_veb_resolution_suppresses_cvn_mpi_and_duplicate_rt15():
|
||||
client = _Client()
|
||||
result = _action(client)({
|
||||
"status": "resolvido",
|
||||
"summary": "Cliente aceitou a explicação.",
|
||||
"protocol_number": "VEB-123",
|
||||
"protocol_closed": True,
|
||||
"conversation_explanation_resolved": True,
|
||||
"suppress_cvn_protocol_ic": True,
|
||||
"informational_vas_types": ["bundle"],
|
||||
"informational_service_names": ["Pacote Europa Promocional"],
|
||||
}, {"input": {}})
|
||||
assert client.protocol_calls == []
|
||||
assert result["protocol_number"] == "VEB-123"
|
||||
assert _codes(result) == ["SAD.001"]
|
||||
|
||||
|
||||
def test_invoice_detail_conflict_overrides_saved_informational_type():
|
||||
client = _Client()
|
||||
result = _action(client)({
|
||||
"status": "resolvido", "summary": "ok", "msisdn": "119",
|
||||
"informational_service_names": ["TIM Fashion Mensal"],
|
||||
"informational_vas_types": ["bundle"],
|
||||
"invoice_detail": {"119": {"SVA Detalhe Total": [{"desc": "TIM Fashion Mensal", "value": 19.9, "classe": "avulso"}]}},
|
||||
}, {"input": {}})
|
||||
assert client.protocol_calls[0]["serviceRequestNotes"] == "Explicação de VAS Avulso"
|
||||
assert result["finalizacao_protocol"] == "INFO-123"
|
||||
|
||||
|
||||
def test_invoice_detail_without_match_ignores_saved_type_and_does_not_create_protocol():
|
||||
client = _Client()
|
||||
result = _action(client)({
|
||||
"status": "resolvido", "summary": "ok", "msisdn": "119",
|
||||
"informational_service_names": ["Serviço que não consta"],
|
||||
"informational_vas_types": ["bundle"],
|
||||
"invoice_detail": {"119": {"Serviços Bundle Inclusos": [{"desc": "Outro serviço", "value": 0}]}},
|
||||
}, {"input": {}})
|
||||
assert client.protocol_calls == []
|
||||
assert "finalizacao_protocol" not in result
|
||||
|
||||
|
||||
def test_rag_saved_type_is_used_when_invoice_detail_is_absent():
|
||||
client = _Client()
|
||||
result = _action(client)({
|
||||
"status": "resolvido", "summary": "ok", "msisdn": "119",
|
||||
"informational_service_names": ["Serviço explicado pelo RAG"],
|
||||
"informational_rag_vas_types": ["bundle"],
|
||||
}, {"input": {}})
|
||||
assert client.protocol_calls[0]["serviceRequestNotes"] == "Explicação de VAS Bundle"
|
||||
assert result["finalizacao_protocol"] == "INFO-123"
|
||||
|
||||
|
||||
def test_partial_invoice_inference_merges_saved_avulso_type():
|
||||
client = _Client()
|
||||
result = _action(client)({
|
||||
"status": "resolvido", "summary": "Cliente entendeu a explicação e agradeceu.", "msisdn": "119",
|
||||
"informational_service_names": ["Pacote Europa Promocional", "YouTube Premium Mensal", "Aluguel de Filme 3"],
|
||||
"informational_vas_types": ["avulso"],
|
||||
"invoice_detail": {"119": {
|
||||
"Serviços Bundle Inclusos": [{"desc": "Pacote Europa Promocional", "value": 0}],
|
||||
"Serviços Contratados de Terceiros": [{"desc": "YouTube Premium Mensal", "value": 24.9, "classe": "estrategico"}],
|
||||
}},
|
||||
}, {"input": {}})
|
||||
assert client.protocol_calls[0]["serviceRequestNotes"] == "Explicação de VAS Bundle, VAS Estratégico, VAS Avulso"
|
||||
assert result["finalizacao_protocol"] == "INFO-123"
|
||||
|
||||
|
||||
def test_existing_session_protocol_prevents_duplicate_informational_rt15():
|
||||
client = _Client()
|
||||
result = _action(client)({
|
||||
"status": "resolvido", "summary": "Cliente entendeu a explicação.",
|
||||
"customer_protocols": [{"number": "PRT-EXISTENTE"}],
|
||||
"informational_service_names": ["Paramount+ Mensal"],
|
||||
}, {"input": {}})
|
||||
assert client.protocol_calls == []
|
||||
assert "finalizacao_protocol" not in result
|
||||
|
||||
|
||||
def test_cpf_alias_is_forwarded_to_informational_protocol():
|
||||
client = _Client()
|
||||
_action(client)({
|
||||
"status": "resolvido", "summary": "Cliente entendeu a explicação.",
|
||||
"msisdn": "11999999999", "cpf": "12345678901",
|
||||
"informational_service_names": ["Paramount+ Mensal"],
|
||||
}, {"input": {}})
|
||||
assert client.protocol_calls[0]["socialSecNo"] == "12345678901"
|
||||
|
||||
|
||||
def test_three_informational_vas_types_have_canonical_order():
|
||||
client = _Client()
|
||||
_action(client)({
|
||||
"status": "resolvido", "summary": "Cliente entendeu.", "msisdn": "119",
|
||||
"informational_vas_types": ["avulso", "bundle", "estrategico"],
|
||||
}, {"input": {}})
|
||||
assert client.protocol_calls[0]["serviceRequestNotes"] == "Explicação de VAS Bundle, VAS Estratégico, VAS Avulso"
|
||||
|
||||
|
||||
def test_free_customer_phrase_without_evidence_does_not_create_protocol():
|
||||
client = _Client()
|
||||
result = _action(client)({
|
||||
"status": "resolvido", "summary": "Cliente entendeu a explicação.",
|
||||
"customer_message": "o que é incluído no plano, mas está sendo cobrado?",
|
||||
}, {"input": {}})
|
||||
assert client.protocol_calls == []
|
||||
assert "finalizacao_protocol" not in result
|
||||
|
||||
|
||||
def test_legacy_final_alias_normalizes_to_resolved_without_spurious_protocol():
|
||||
client = _Client()
|
||||
result = _action(client)({"status": "final", "summary": ""}, {"input": {}})
|
||||
assert result["status"] == "resolvido"
|
||||
assert client.protocol_calls == []
|
||||
|
||||
|
||||
def test_transactional_cancel_finalization_does_not_emit_cvn_or_mpi_even_with_summary():
|
||||
client = _Client()
|
||||
result = _action(client)({
|
||||
"status": "resolvido", "summary": "Cancelamento concluído.",
|
||||
"business_workflows_executed": ["cancelamento_vas_avulso"],
|
||||
"protocol_number": "CAN-123", "protocol_closed": True,
|
||||
}, {"input": {}})
|
||||
assert _codes(result) == ["SAD.001"]
|
||||
|
||||
|
||||
def test_strategic_notes_take_priority_over_prior_invoice_explanation_notes():
|
||||
client = _Client()
|
||||
result = _action(client)({
|
||||
"status": "resolvido",
|
||||
"summary": "Cliente entendeu a explicação e agradeceu.",
|
||||
"msisdn": "11999999999",
|
||||
"business_workflows_executed": ["invoice_explanation"],
|
||||
"invoice_explanation_base": "Explicação dos valores da fatura.",
|
||||
"informational_service_names": ["YouTube Premium Mensal"],
|
||||
"informational_vas_types": ["estrategico"],
|
||||
}, {"input": {}})
|
||||
assert result["finalizacao_protocol"] == "INFO-123"
|
||||
assert client.protocol_calls[0]["serviceRequestNotes"] == "Explicação de VAS Estratégico"
|
||||
|
||||
|
||||
def test_deferred_bundle_and_strategic_items_create_combined_rt15_without_workflow_latch():
|
||||
client = _Client()
|
||||
result = _action(client)({
|
||||
"status": "resolvido",
|
||||
"summary": "Cliente entendeu a explicação e agradeceu.",
|
||||
"msisdn": "11999999999",
|
||||
"protocol_deferred_to_finalization": True,
|
||||
"informational_service_names": ["Pacote Europa Promocional", "YouTube Premium Mensal"],
|
||||
"informational_vas_types": ["bundle", "estrategico"],
|
||||
"invoice_detail": {
|
||||
"11999999999": {
|
||||
"Serviços Bundle Inclusos": [{"desc": "Pacote Europa Promocional", "value": 0}],
|
||||
"Serviços Contratados de Terceiros": [{"desc": "YouTube Premium Mensal", "value": 24.9, "classe": "estrategico"}],
|
||||
}
|
||||
},
|
||||
}, {"input": {}})
|
||||
assert result["finalizacao_protocol"] == "INFO-123"
|
||||
assert client.protocol_calls[0]["serviceRequestNotes"] == "Explicação de VAS Bundle, VAS Estratégico"
|
||||
|
||||
|
||||
def test_invoice_explanation_only_registers_protocol_at_finalization():
|
||||
client = _Client()
|
||||
result = _action(client)({
|
||||
"status": "resolvido",
|
||||
"summary": "Cliente aceitou a explicação.",
|
||||
"msisdn": "11999999999",
|
||||
"business_workflows_executed": ["invoice_explanation"],
|
||||
"invoice_explanation_base": "Explicação dos valores da fatura.",
|
||||
}, {"input": {}})
|
||||
assert len(client.protocol_calls) == 1
|
||||
assert result["finalizacao_protocol"] == "INFO-123"
|
||||
assert client.protocol_calls[0]["serviceRequestNotes"] == "Explicação dos valores da fatura"
|
||||
Reference in New Issue
Block a user