import os import pytest os.environ.setdefault("USE_MOCK_LLM", "true") from agent_framework.guardrails.calibrated import apply_output_rails, compliance_anatel from agent_framework.guardrails.calibrated._compat import RailResult # --------------------------------------------------------------------------- # Testes unitários diretos em compliance_anatel # --------------------------------------------------------------------------- def test_fluxo_nao_ajuste_passa_sem_verificar(): """Quando o fluxo não exige protocolo, o rail não deve bloquear.""" r = compliance_anatel("Sua fatura está disponível.", context={}) assert isinstance(r, RailResult) assert r.allowed is True assert r.code == "CMP" assert r.mechanism == "regex" def test_fluxo_ajuste_com_protocolo_passa(): r = compliance_anatel( "O ajuste foi realizado. Protocolo: 123456.", context={"tipo_fluxo": "ajuste"}, ) assert r.allowed is True assert r.code == "CMP" def test_fluxo_ajuste_sem_protocolo_bloqueia(): r = compliance_anatel( "O ajuste foi realizado com sucesso.", context={"tipo_fluxo": "ajuste"}, ) assert r.allowed is False assert r.code == "CMP" assert "protocolo" in r.reason.lower() def test_requer_protocolo_true_com_protocolo_passa(): r = compliance_anatel( "Cancelamento efetuado. Protocolo: 9876543.", context={"requer_protocolo": True}, ) assert r.allowed is True def test_requer_protocolo_true_sem_protocolo_bloqueia(): r = compliance_anatel( "Cancelamento efetuado com sucesso.", context={"requer_protocolo": True}, ) assert r.allowed is False @pytest.mark.parametrize("text", [ "Protocolo: 123456", "protocolo - 9876543", "PROTOCOLO: 1000001", "Seu protocolo é 200001.", "O número de protocolo: 654321 foi registrado.", ]) def test_formatos_de_protocolo_aceitos(text): r = compliance_anatel(text, context={"tipo_fluxo": "ajuste"}) assert r.allowed is True @pytest.mark.parametrize("text", [ "O ajuste foi feito.", "Tudo certo com sua solicitação.", "Protocolo: 12345", # apenas 5 dígitos — insuficiente "protocolo ABC123", # não começa com dígitos ]) def test_formatos_invalidos_ou_ausentes_bloqueiam(text): r = compliance_anatel(text, context={"tipo_fluxo": "ajuste"}) assert r.allowed is False # --------------------------------------------------------------------------- # Testes de integração via apply_output_rails # --------------------------------------------------------------------------- def test_pipeline_ajuste_sem_protocolo_nao_bloqueia_cmp(): d = apply_output_rails( text="O ajuste foi realizado com sucesso.", user_text="O ajuste foi realizado com sucesso.", tool_calls=None, context={"tipo_fluxo": "ajuste"}, ) assert d.allowed is True assert d.code is None assert all(r.code != "CMP" for r in d.results) def test_pipeline_ajuste_com_protocolo_passa(): d = apply_output_rails( text="Ajuste concluído. Protocolo: 123456.", user_text="Ajuste concluído. Protocolo: 123456.", tool_calls=None, context={"tipo_fluxo": "ajuste"}, ) assert d.allowed is True def test_pipeline_sem_context_nao_exige_protocolo(): """Sem context, CMP não deve bloquear — fluxo não é de ajuste.""" d = apply_output_rails( text="O ajuste foi realizado.", user_text="O ajuste foi realizado.", tool_calls=None, ) assert d.allowed is True assert all(r.code != "CMP" for r in d.results) def test_pipeline_cmp_nao_entra_nos_results(): """CMP roda no fallback determinístico do agente, fora do output rail.""" d = apply_output_rails( text="Posso te ajudar com mais alguma coisa?", user_text="Posso te ajudar com mais alguma coisa?", tool_calls=None, context={"tipo_fluxo": "consulta"}, ) assert all(r.code != "CMP" for r in d.results)