Files
agent_contas/tests/migration/original_test_ausencia_oferta_proativa.py
2026-08-21 10:44:34 -03:00

111 lines
4.0 KiB
Python

import os
import pytest
os.environ.setdefault("USE_MOCK_LLM", "true")
from agent_framework.guardrails.calibrated import ausencia_oferta_proativa
from agent_framework.guardrails.calibrated._compat import RailResult
from agent_framework.guardrails.calibrated.prompts.ausencia_oferta_proativa import (
build_aoferta_prompt,
)
# --------------------------------------------------------------------------- #
# Contrato de saída (2026-08-05): {allowed, reason}, reason vazio se allowed #
# --------------------------------------------------------------------------- #
def test_prompt_aoferta_nao_pede_label():
# O `label` nunca existiu no prompt real (só no mock); o contrato é
# {allowed, reason}, como o FRASEOLOGIA.
prompt = build_aoferta_prompt("Gostaria de cancelar esse serviço?")
assert '"label"' not in prompt
assert '"allowed": true ou false' in prompt
def test_prompt_aoferta_exige_reason_vazio_quando_allowed():
prompt = build_aoferta_prompt("Gostaria de cancelar esse serviço?")
assert "se allowed=true: string vazia" in prompt
assert "se allowed=true: explicacao curta" not in prompt
def test_prompt_aoferta_injeta_texto_e_contexto():
prompt = build_aoferta_prompt("posso cancelar?", "\nHistorico:\n[user] oi\n")
assert "posso cancelar?" in prompt
assert "[user] oi" in prompt
@pytest.fixture(autouse=True)
def _force_mock(monkeypatch):
monkeypatch.setenv("USE_MOCK_LLM", "true")
def test_resposta_neutra_eh_permitida():
r = ausencia_oferta_proativa("Posso te ajudar com mais alguma coisa?")
assert isinstance(r, RailResult)
assert r.allowed is True
assert r.code == "AOFERTA"
assert r.mechanism == "llm_supervisor"
assert r.data is not None
assert r.data.get("label") == "OFERTA_OK"
def test_oferta_proativa_indevida_bloqueia():
r = ausencia_oferta_proativa(
"Ja que esta cancelando um, quer aproveitar e cancelar os outros?",
)
assert r.allowed is False
assert r.code == "AOFERTA"
assert r.mechanism == "llm_supervisor"
assert r.data.get("label") == "OFERTA_PROATIVA_INDEVIDA"
def test_padrao_aproveita_e_bloqueia():
r = ausencia_oferta_proativa("Quer aproveitar e cancelar os outros?")
assert r.allowed is False
assert r.code == "AOFERTA"
def test_confirmacao_cancelamento_apos_nao_reconhecimento_eh_permitida():
# Cliente nao-reconheceu os servicos em foco ("Nao contratei esses
# servicos") e o agente confirma o cancelamento dos MESMOS itens. Per
# classificador v9 + orquestrador, isso e action_request -> a confirmacao
# deve ser OFERTA_OK, nao oferta proativa.
#
# NOTA: em USE_MOCK_LLM=true o mock decide AOFERTA por string-match no texto
# do agente (sem gatilho de upsell -> OFERTA_OK), ignorando o contexto. Este
# teste documenta a intencao; a validacao real do prompt e via LLM/cenarios
# (automated_conversation_test cy0004/cy0005/cy0008).
r = ausencia_oferta_proativa(
"Podemos seguir com o cancelamento dos serviços VOD + Canais Abertos, "
"no valor de R$ 19,90, e Aluguel de Filme 1, no valor de R$ 19,90, "
"e providenciar a retirada dos valores da fatura, você confirma?",
)
assert r.allowed is True
assert r.code == "AOFERTA"
assert r.data.get("label") == "OFERTA_OK"
def test_upsell_continua_bloqueando_apos_mudanca():
# Contraprova: a mudanca para reconhecer nao-reconhecimento NAO pode liberar
# marcadores de upsell explicitos.
r = ausencia_oferta_proativa(
"Ja que esta cancelando esse, que tal tambem cancelar o outro servico?",
)
assert r.allowed is False
assert r.code == "AOFERTA"
assert r.data.get("label") == "OFERTA_PROATIVA_INDEVIDA"
def test_prompt_aoferta_confirmacao_nao_exige_repetir_justificativa():
prompt = build_aoferta_prompt(
"Você confirma cancelar o TIM Music?",
"\nHistorico da conversa:\n[user] Quero cancelar o TIM Music\n",
)
assert "A confirmacao NAO precisa repetir a justificativa" in prompt
assert "o pedido transacional anterior basta" in prompt