Projeto do Agent Contas ORACLE
This commit is contained in:
352
tests/migration/test_original_vas_cancellation_message.py
Normal file
352
tests/migration/test_original_vas_cancellation_message.py
Normal file
@@ -0,0 +1,352 @@
|
||||
"""Paridade do compositor determinístico com o prompt
|
||||
``fluxo_vas_cancelamento_resposta_cliente`` (regras 1.1, 2–8).
|
||||
|
||||
Os goldens de sucesso/erro/SMS vêm dos próprios exemplos do prompt em
|
||||
``builtin/prompts/vas_cancellation_response.yaml``.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from app.domain.contas.vas_cancellation_message import (
|
||||
compose_vas_cancellation_message,
|
||||
)
|
||||
|
||||
PRT = "PRT475D8F1C1F"
|
||||
|
||||
|
||||
def test_regra_3_sucesso_com_sms_singular() -> None:
|
||||
# Exemplo do prompt: "Exemplo (sucesso com SMS)".
|
||||
msg = compose_vas_cancellation_message(
|
||||
{
|
||||
"contested_items": [{"itemName": "Tamboro Mensal"}],
|
||||
"contested_invoice_amount_open": "22,00",
|
||||
"sms_sent": True,
|
||||
"sms_not_send_error": False,
|
||||
"msisdn": "11999999999",
|
||||
"contestacao_protocol": PRT,
|
||||
}
|
||||
)
|
||||
assert msg == (
|
||||
"O cancelamento foi concluído com sucesso. O valor contestado foi retirado "
|
||||
"da sua fatura. Enviamos um SMS com o novo código de barras e o valor "
|
||||
"atualizado. O prazo para pagamento é de 4 dias. "
|
||||
f"Seu número de protocolo é {PRT}."
|
||||
)
|
||||
|
||||
|
||||
def test_regra_3_e_6_2_falha_no_sms_plural() -> None:
|
||||
# Exemplo do prompt: "Exemplo (sucesso com falha no SMS)".
|
||||
msg = compose_vas_cancellation_message(
|
||||
{
|
||||
"contested_items": [
|
||||
{"itemName": "Tamboro Mensal"},
|
||||
{"itemName": "TIM Fashion"},
|
||||
],
|
||||
"contested_invoice_amount_open": "22,00",
|
||||
"sms_sent": True,
|
||||
"sms_not_send_error": True,
|
||||
"msisdn": "11999999999",
|
||||
"contestacao_protocol": PRT,
|
||||
}
|
||||
)
|
||||
assert msg == (
|
||||
"O cancelamento dos itens foi concluído com sucesso. O valor total de "
|
||||
"R$ 22,00 foi retirado da sua fatura. Identificamos uma instabilidade no "
|
||||
"envio do SMS com o novo código para pagamento. Você pode consultar o "
|
||||
"código atualizado e o prazo de pagamento diretamente no app Meu TIM. "
|
||||
f"Seu número de protocolo é {PRT}."
|
||||
)
|
||||
|
||||
|
||||
def test_regra_2_credito_plural_com_regra_5() -> None:
|
||||
# Baseado no "Exemplo (crédito em fatura)" do prompt; o conector "No entanto,"
|
||||
# era floreio do LLM — a forma determinística segue o template literal da regra 5.
|
||||
msg = compose_vas_cancellation_message(
|
||||
{
|
||||
"contested_items": [
|
||||
{"itemName": "Aluguel de Série 1"},
|
||||
{"itemName": "Aluguel de Série 2"},
|
||||
],
|
||||
"not_contested_items": [{"itemName": "Tamboro Mensal"}],
|
||||
"contested_invoice_amount_open": "22,00",
|
||||
"sms_sent": False,
|
||||
"contestacao_protocol": PRT,
|
||||
}
|
||||
)
|
||||
assert msg == (
|
||||
"O cancelamento dos itens foi feito com sucesso. O crédito no valor total "
|
||||
"de R$ 22,00 ficou registrado como crédito para sua próxima fatura. "
|
||||
"Não foi possível contestar o item Tamboro Mensal. "
|
||||
f"Seu número de protocolo é {PRT}."
|
||||
)
|
||||
|
||||
|
||||
def test_regra_2_credito_singular() -> None:
|
||||
msg = compose_vas_cancellation_message(
|
||||
{
|
||||
"contested_items": [{"itemName": "Tamboro Mensal"}],
|
||||
"contested_invoice_amount_open": "22,00",
|
||||
"sms_sent": False,
|
||||
"contestacao_protocol": PRT,
|
||||
}
|
||||
)
|
||||
assert msg == (
|
||||
"O cancelamento do item Tamboro Mensal no valor de R$ 22,00 foi concluído "
|
||||
"com sucesso. O valor contestado ficou registrado como crédito para sua "
|
||||
f"próxima fatura. Seu número de protocolo é {PRT}."
|
||||
)
|
||||
|
||||
|
||||
def test_regra_8_erro_terminal_copia_literal_e_protocolo() -> None:
|
||||
# Exemplo do prompt: "Exemplo (falha, usando um dos textos de ...)".
|
||||
erro = (
|
||||
"Verifiquei aqui que este item já foi contestado anteriormente, por isso "
|
||||
"não consigo seguir com uma nova contestação. Peço que aguarde um instante."
|
||||
)
|
||||
msg = compose_vas_cancellation_message(
|
||||
{
|
||||
"contestation_error_description": erro,
|
||||
"contestacao_protocol": PRT,
|
||||
}
|
||||
)
|
||||
assert msg == f"{erro} Seu número de protocolo é {PRT}."
|
||||
|
||||
|
||||
def test_regra_8_adiciona_ponto_quando_texto_nao_termina_em_pontuacao() -> None:
|
||||
msg = compose_vas_cancellation_message(
|
||||
{
|
||||
"contestation_error_description": "Não foi possível processar",
|
||||
"contestacao_protocol": PRT,
|
||||
}
|
||||
)
|
||||
assert msg == f"Não foi possível processar. Seu número de protocolo é {PRT}."
|
||||
|
||||
|
||||
def test_regra_8_item_ja_contestado_informa_cancelamentos_realizados() -> None:
|
||||
msg = compose_vas_cancellation_message(
|
||||
{
|
||||
"cancelados": [{"servico": "A"}, {"servico": "B"}],
|
||||
"contestation_error_description": (
|
||||
"Verifiquei aqui que estes itens já foram contestados anteriormente, "
|
||||
"por isso não consigo seguir com uma nova contestação."
|
||||
),
|
||||
"contestacao_protocol": PRT,
|
||||
}
|
||||
)
|
||||
assert msg == (
|
||||
"O cancelamento dos itens A e B foi concluído com sucesso. "
|
||||
"Verifiquei aqui que estes itens já foram contestados anteriormente, "
|
||||
"por isso não consigo seguir com uma nova contestação. "
|
||||
f"Seu número de protocolo é {PRT}."
|
||||
)
|
||||
|
||||
|
||||
def test_regra_1_1_misto_credito() -> None:
|
||||
msg = compose_vas_cancellation_message(
|
||||
{
|
||||
"itens_ja_contestados": ["Tamboro Mensal"],
|
||||
"contested_items": [{"itemName": "TIM Fashion"}],
|
||||
"contested_invoice_amount_open": "10,00",
|
||||
"sms_sent": False,
|
||||
"contestacao_protocol": PRT,
|
||||
}
|
||||
)
|
||||
assert msg == (
|
||||
"Identifiquei que o item Tamboro Mensal já havia sido contestado "
|
||||
"anteriormente e, por esse motivo, não é possível registrar uma nova "
|
||||
"contestação para ele. Já a contestação do item TIM Fashion no valor de "
|
||||
"R$ 10,00 foi concluída com sucesso. O valor contestado ficou registrado "
|
||||
f"como crédito para sua próxima fatura. Seu número de protocolo é {PRT}."
|
||||
)
|
||||
|
||||
|
||||
def test_regra_1_1_misto_com_sms_aplica_regra_6() -> None:
|
||||
msg = compose_vas_cancellation_message(
|
||||
{
|
||||
"itens_ja_contestados": ["A", "B"],
|
||||
"contested_items": [{"itemName": "C"}, {"itemName": "D"}],
|
||||
"contested_invoice_amount_open": "30,00",
|
||||
"sms_sent": True,
|
||||
"sms_not_send_error": False,
|
||||
"msisdn": "11988887777",
|
||||
"contestacao_protocol": PRT,
|
||||
}
|
||||
)
|
||||
assert msg == (
|
||||
"O cancelamento foi concluído com sucesso. O valor contestado foi retirado "
|
||||
"da sua fatura. Enviamos um SMS com o novo código de barras e o valor "
|
||||
"atualizado. O prazo para pagamento é de 4 dias. "
|
||||
f"Seu número de protocolo é {PRT}."
|
||||
)
|
||||
|
||||
|
||||
def test_sms_enviado_prevalece_quando_contested_items_esta_vazio() -> None:
|
||||
# Regressão do trace 6ae8bd24: SMS efetivamente enviado nunca vira crédito.
|
||||
msg = compose_vas_cancellation_message(
|
||||
{
|
||||
"itens_ja_contestados": ["Tamboro Mensal"],
|
||||
"contested_items": [],
|
||||
"contested_invoice_amount_open": "22,00",
|
||||
"sms_sent": True,
|
||||
"contestacao_protocol": PRT,
|
||||
}
|
||||
)
|
||||
assert msg == (
|
||||
"O cancelamento foi concluído com sucesso. O valor contestado foi retirado "
|
||||
"da sua fatura. Enviamos um SMS com o novo código de barras e o valor "
|
||||
"atualizado. O prazo para pagamento é de 4 dias. "
|
||||
f"Seu número de protocolo é {PRT}."
|
||||
)
|
||||
assert "próxima fatura" not in msg
|
||||
|
||||
|
||||
def test_regra_7_protocolos_multiplos() -> None:
|
||||
msg = compose_vas_cancellation_message(
|
||||
{
|
||||
"contested_items": [{"itemName": "Tamboro Mensal"}],
|
||||
"contested_invoice_amount_open": "22,00",
|
||||
"sms_sent": False,
|
||||
"contestacao_protocols": ["PRTAAA", "PRTBBB"],
|
||||
}
|
||||
)
|
||||
assert msg.endswith("Seus números de protocolo são PRTAAA e PRTBBB.")
|
||||
|
||||
|
||||
def test_no_match_credito_todos_nao_encontrados_singular() -> None:
|
||||
# Cenário 2 (crédito): serviço não localizado na plataforma; valor contestado.
|
||||
msg = compose_vas_cancellation_message(
|
||||
{
|
||||
"success": True,
|
||||
"contested_items": [{"itemName": "Tamboro Mensal"}],
|
||||
"nao_encontrados": [{"servico": "Tamboro Mensal"}],
|
||||
"cancelados": [],
|
||||
"contested_invoice_amount_open": "22,00",
|
||||
"sms_sent": False,
|
||||
"contestacao_protocol": PRT,
|
||||
}
|
||||
)
|
||||
assert msg == (
|
||||
"O valor total de R$ 22,00 ficou registrado como crédito para sua próxima "
|
||||
"fatura. Sobre o cancelamento do serviço Tamboro Mensal, não consegui "
|
||||
"realizar por aqui. Você pode cancelar pelo App Meu TIM, na opção "
|
||||
f"Gerenciar Benefícios. Seu número de protocolo é {PRT}."
|
||||
)
|
||||
|
||||
|
||||
def test_no_match_sms_todos_nao_encontrados() -> None:
|
||||
# Cenário 2 (SMS/boleto): valor retirado + SMS + no-match.
|
||||
msg = compose_vas_cancellation_message(
|
||||
{
|
||||
"success": True,
|
||||
"contested_items": [{"itemName": "Tamboro Mensal"}],
|
||||
"nao_encontrados": [{"servico": "Tamboro Mensal"}],
|
||||
"cancelados": [],
|
||||
"contested_invoice_amount_open": "22,00",
|
||||
"sms_sent": True,
|
||||
"sms_not_send_error": False,
|
||||
"msisdn": "11999999999",
|
||||
"contestacao_protocol": PRT,
|
||||
}
|
||||
)
|
||||
assert msg == (
|
||||
"O valor total de R$ 22,00 foi retirado da sua fatura. Enviamos um SMS com "
|
||||
"o novo código de barras e o valor atualizado. O prazo para pagamento é de "
|
||||
"4 dias. Sobre o cancelamento do serviço Tamboro "
|
||||
"Mensal, não consegui realizar por aqui. Você pode cancelar pelo App Meu "
|
||||
f"TIM, na opção Gerenciar Benefícios. Seu número de protocolo é {PRT}."
|
||||
)
|
||||
|
||||
|
||||
def test_no_match_misto_credito() -> None:
|
||||
# Cenário 3 (crédito): A,B,C cancelados; D,E,F no-match.
|
||||
msg = compose_vas_cancellation_message(
|
||||
{
|
||||
"success": True,
|
||||
"contested_items": [{"itemName": x} for x in ("A", "B", "C", "D", "E", "F")],
|
||||
"cancelados": [{"servico": x} for x in ("A", "B", "C")],
|
||||
"nao_encontrados": [{"servico": x} for x in ("D", "E", "F")],
|
||||
"contested_invoice_amount_open": "60,00",
|
||||
"sms_sent": False,
|
||||
"contestacao_protocol": PRT,
|
||||
}
|
||||
)
|
||||
assert msg == (
|
||||
"O valor total de R$ 60,00 ficou registrado como crédito para sua próxima "
|
||||
"fatura. O cancelamento dos serviços A, B e C foram realizados com sucesso. "
|
||||
"Não consegui realizar por aqui o cancelamento dos serviços D, E e F. Você "
|
||||
"pode cancelá-los pelo App Meu TIM, na opção Gerenciar Benefícios. "
|
||||
f"Seu número de protocolo é {PRT}."
|
||||
)
|
||||
|
||||
|
||||
def test_no_match_misto_sms() -> None:
|
||||
# Cenário 3 (SMS/boleto).
|
||||
msg = compose_vas_cancellation_message(
|
||||
{
|
||||
"success": True,
|
||||
"contested_items": [{"itemName": x} for x in ("A", "B", "C", "D", "E", "F")],
|
||||
"cancelados": [{"servico": x} for x in ("A", "B", "C")],
|
||||
"nao_encontrados": [{"servico": x} for x in ("D", "E", "F")],
|
||||
"contested_invoice_amount_open": "60,00",
|
||||
"sms_sent": True,
|
||||
"sms_not_send_error": False,
|
||||
"msisdn": "11988887777",
|
||||
"contestacao_protocol": PRT,
|
||||
}
|
||||
)
|
||||
assert msg == (
|
||||
"O valor total de R$ 60,00 foi retirado da sua fatura. Enviamos um SMS com "
|
||||
"o novo código de barras e o valor atualizado. O prazo para pagamento é de "
|
||||
"4 dias. O cancelamento dos serviços A, B e C foram "
|
||||
"realizados com sucesso. Não consegui realizar por aqui o cancelamento dos "
|
||||
"serviços D, E e F. Você pode cancelá-los pelo App Meu TIM, na opção "
|
||||
f"Gerenciar Benefícios. Seu número de protocolo é {PRT}."
|
||||
)
|
||||
|
||||
|
||||
def test_no_match_misto_singular_cada_lista() -> None:
|
||||
# Concordância: 1 cancelado + 1 no-match.
|
||||
msg = compose_vas_cancellation_message(
|
||||
{
|
||||
"success": True,
|
||||
"contested_items": [{"itemName": "A"}, {"itemName": "B"}],
|
||||
"cancelados": [{"servico": "A"}],
|
||||
"nao_encontrados": [{"servico": "B"}],
|
||||
"contested_invoice_amount_open": "20,00",
|
||||
"sms_sent": False,
|
||||
"contestacao_protocol": PRT,
|
||||
}
|
||||
)
|
||||
assert msg == (
|
||||
"O valor total de R$ 20,00 ficou registrado como crédito para sua próxima "
|
||||
"fatura. O cancelamento do serviço A foi realizado com sucesso. Não "
|
||||
"consegui realizar por aqui o cancelamento do serviço B. Você pode "
|
||||
f"cancelá-lo pelo App Meu TIM, na opção Gerenciar Benefícios. Seu número "
|
||||
f"de protocolo é {PRT}."
|
||||
)
|
||||
|
||||
|
||||
def test_sucesso_sem_itens_detalhados_gera_confirmacao_generica() -> None:
|
||||
# Sem itens contestados/não contestados/já contestados, mas cancelamento OK:
|
||||
# confirmação neutra + protocolo (regra 7).
|
||||
msg = compose_vas_cancellation_message(
|
||||
{
|
||||
"success": True,
|
||||
"contested_items": [],
|
||||
"not_contested_items": [],
|
||||
"contestacao_protocol": PRT,
|
||||
}
|
||||
)
|
||||
assert msg == f"O cancelamento foi concluído com sucesso. Seu número de protocolo é {PRT}."
|
||||
|
||||
|
||||
def test_sucesso_sem_itens_e_sem_protocolo_so_confirmacao() -> None:
|
||||
# Protocolo de cancelamento (não de contestação) é anexado pelo backend, não aqui.
|
||||
msg = compose_vas_cancellation_message({"success": True, "contested_items": []})
|
||||
assert msg == "O cancelamento foi concluído com sucesso."
|
||||
|
||||
|
||||
def test_payload_nao_classificavel_sem_sucesso_retorna_vazio() -> None:
|
||||
assert compose_vas_cancellation_message({}) == ""
|
||||
assert compose_vas_cancellation_message({"success": False}) == ""
|
||||
assert compose_vas_cancellation_message("nao-mapping") == "" # type: ignore[arg-type]
|
||||
Reference in New Issue
Block a user