new feature: Integration with kbdb Autonomous

This commit is contained in:
T3782834
2026-08-26 10:15:59 -03:00
parent d7f270f52a
commit fc64c6ddd7
514 changed files with 3006 additions and 168 deletions

View File

@@ -0,0 +1,172 @@
from __future__ import annotations
import json
from pathlib import Path
import pytest
import yaml
from app.domain.contas.service import ContasDomainService
from app.domain.contas.workflow_actions import build_contas_workflow_actions
from contas_mcp.servers.contas_mcp_server import main as mcp
ROOT = Path(__file__).resolve().parents[2]
REQUESTED = {
"consultar_faturas", "consultar_plano", "invoice_explanation", "buscar_informacao",
"consultar_vas", "consultar_historico_vas", "cancelar_vas_avulso",
"tratar_vas_estrategico", "validar_contestacao", "contestar_cobranca",
"finalizar_atendimento", "consultar_status_solicitacao", "enviar_sms",
"recuperar_fatura_pdf", "pro_rata", "termino_desconto", "valor_divergente",
"retomar_workflow",
}
def test_todas_as_capabilities_solicitadas_estao_expostas_no_mcp():
assert REQUESTED <= set(mcp.TOOLS)
def test_todas_as_capabilities_solicitadas_estao_habilitadas_no_registry():
cfg = yaml.safe_load((ROOT / "config/tools.yaml").read_text(encoding="utf-8"))["tools"]
assert REQUESTED <= set(cfg)
assert all(cfg[name].get("enabled", True) for name in REQUESTED)
def test_buscar_informacao_preserva_tool_original_mas_delega_rag_ao_framework():
out = ContasDomainService().buscar_informacao(
queries=["Aya Audiobooks Premium o que é", "Food Balance o que é"]
)
assert out["requires_rag"] is True
assert out["source"] == "agent_framework.rag"
assert out["rag_queries"] == ["Aya Audiobooks Premium o que é", "Food Balance o que é"]
def test_consultar_plano_extrai_planos_da_evidencia_sem_llm():
billing = json.loads((ROOT / "app/domain/contas/fixtures/divergencia.json").read_text(encoding="utf-8"))
out = ContasDomainService().consultar_plano(msisdn="119", billing_analysis=billing)
assert out["count"] == 2
assert {p["name"] for p in out["plans"]} == {"TIM Black A 8.0", "TIM CTRL Redes Sociais 8.0"}
def test_invoice_explanation_preserva_detail_e_resumo_semantico_para_composicao():
svc = ContasDomainService()
detail = {"parsed_content": {"total_geral": 197.43}}
out = svc.invoice_explanation(
msisdn="119",
complete_invoices_payload={"paymentItems": []},
billing_analysis={"invoiceExplanation": "Base"},
invoice_detail=detail,
invoice_amount="197.43",
invoice_period="14/10 a 13/11",
)
assert out["invoice_detail"] == detail
assert out["invoice_amount"] == "197.43"
assert out["invoice_period"] == "14/10 a 13/11"
def test_invoice_explanation_restaura_composicao_llm_no_framework():
reg = build_contas_workflow_actions(ContasDomainService())
out = reg.get("formatar_invoice_explanation")(
{"explicacao_base": "TIM Fashion R$ 10,00", "invoice_detail": {}}, {"input": {}}
)
assert out["await_user_input"] is True
assert out["requires_llm_composition"] is True
assert "não invente" in out["response_instruction"]
assert "Com essa explicação, sanei sua dúvida?" in out["mensagem"]
def test_pro_rata_deriva_exatamente_dois_planos_do_pdf_sem_llm():
detail = json.loads((ROOT / "app/domain/contas/fixtures/invoice_pdf_include_danfe_true.json").read_text(encoding="utf-8"))
args = {"msisdn": "11999999999", "invoice_detail": detail}
plans = mcp._derive_pro_rata_plans(args)
assert len(plans) == 2
assert {p["desc"] for p in plans} == {"TIM Black A 8.0", "TIM CTRL Redes Sociais 8.0"}
assert mcp._prepare_pro_rata(args) is None
assert args["has_plano_controle"] is True
def test_pro_rata_falha_fechado_sem_exatamente_dois_planos():
args = {"msisdn": "119", "planos": [{"desc": "Plano único"}]}
out = mcp._prepare_pro_rata(args)
assert out["success"] is False
assert out["reason"] == "requires_exactly_two_plans"
assert out["plans_found"] == 1
@pytest.mark.asyncio
async def test_validar_contestacao_usa_mesma_cval_e_rejeita_valor_acima_da_fatura(monkeypatch):
billing = json.loads((ROOT / "app/domain/contas/fixtures/divergencia.json").read_text(encoding="utf-8"))
async def no_enrich(name, args):
return None
monkeypatch.setattr(mcp, "_enrich_invoice_context", no_enrich)
out = await mcp._validate_contestation({
"msisdn": "11999999999",
"subject": "TIM Fashion Mensal",
"valor": 150,
"motivo": "quero contestar TIM Fashion Mensal",
"billing_analysis": billing,
})
assert out["eligible"] is False
assert out["reason"] == "CVAL"
assert out["validation_log"][0]["erro"] == "valor_ajuste_maior_que_item"
@pytest.mark.asyncio
async def test_validar_contestacao_aprova_quando_item_e_valor_sao_comprovados(monkeypatch):
billing = json.loads((ROOT / "app/domain/contas/fixtures/divergencia.json").read_text(encoding="utf-8"))
async def no_enrich(name, args):
return None
monkeypatch.setattr(mcp, "_enrich_invoice_context", no_enrich)
out = await mcp._validate_contestation({
"msisdn": "11999999999",
"subject": "TIM Fashion Mensal",
"valor": 50,
"motivo": "quero contestar TIM Fashion Mensal",
"billing_analysis": billing,
})
assert out["eligible"] is True
assert out["status"] == "ELIGIBLE"
def test_termino_desconto_e_valor_divergente_preservam_semantica_do_original():
reg = build_contas_workflow_actions(ContasDomainService())
action = reg.get("formatar_capability_resposta")
end = action({"tipo": "termino_desconto", "nome_plano": "TIM Black", "msisdn": "11999999999"}, {"input": {}})
div = action({"tipo": "valor_divergente", "msisdn": "11999999999"}, {"input": {}})
assert "desconto de fidelidade do plano TIM Black" in end["mensagem"]
assert "período promocional contratado expirou" in end["mensagem"]
assert "alteração no valor do plano" in div["mensagem"]
assert "final 99" in div["mensagem"]
def test_status_solicitacao_nao_confunde_message_id_com_protocolo():
mapping = yaml.safe_load((ROOT / "config/mcp_parameter_mapping.yaml").read_text(encoding="utf-8"))["mcp_parameter_mapping"]["tools"]["consultar_status_solicitacao"]
assert "interaction_key" not in mapping.get("map", {})
assert "protocol" in mapping.get("extract", {})
def test_finalizacao_preserva_status_explicito_do_contrato_original():
cfg = yaml.safe_load((ROOT / "config/tools.yaml").read_text(encoding="utf-8"))["tools"]["finalizar_atendimento"]
assert "status" in cfg.get("requires", [])
mapping = yaml.safe_load((ROOT / "config/mcp_parameter_mapping.yaml").read_text(encoding="utf-8"))["mcp_parameter_mapping"]["tools"]["finalizar_atendimento"]
assert "status" in mapping.get("extract", {})
class _DirectClient:
def consultar_vas(self, msisdn): return {"products": [{"name": "VAS"}]}
def historico_vas(self, msisdn): return {"services": [{"name": "OLD"}]}
def status_sr(self, payload): return {"protocolNumber": payload.get("protocolNumber"), "status": "Fechado"}
def sms(self, msisdn, message): return {"sent": True, "msisdn": msisdn, "message": message}
def secure_pdf(self, msisdn, invoice_id, customer_id=""): return {"invoiceId": invoice_id, "url": "secure"}
def test_tools_diretas_vas_historico_status_sms_pdf_mantem_contratos_de_dominio():
svc = ContasDomainService(client=_DirectClient()) # type: ignore[arg-type]
assert svc.consultar_vas(msisdn="119")["products"][0]["name"] == "VAS"
assert svc.consultar_historico_vas(msisdn="119")["services"][0]["name"] == "OLD"
assert svc.consultar_status_solicitacao(msisdn="119", protocol="P1")["protocolNumber"] == "P1"
assert svc.enviar_sms(msisdn="119", message="ok")["sent"] is True
assert svc.recuperar_fatura_pdf(msisdn="119", invoice_id="I1")["invoiceId"] == "I1"