Projeto do Agent Contas ORACLE

This commit is contained in:
2026-08-19 09:35:50 -03:00
commit 950a2bcd33
1366 changed files with 177217 additions and 0 deletions

View File

@@ -0,0 +1,346 @@
from __future__ import annotations
import pytest
def _result(name: str, status: str, output: dict, *, error: str | None = None):
return {
"execution_id": f"exec-{name}",
"workflow_name": name,
"workflow_version": 1,
"status": status,
"output": output,
"state": {},
"trace": [],
"error": error,
"error_details": None,
}
class _Service:
def consultar_faturas(self, *, msisdn: str):
return {
"billingProfile": {"customer": {"document": "12345678901", "customerId": "344575482"}},
"paymentItems": [{"invoiceId": "5536806781", "dueDate": "2025-08-11"}],
}
@pytest.mark.asyncio
async def test_success_implicito_por_cancelados_sem_flag_success(monkeypatch):
import contas_mcp.servers.contas_mcp_server.main as main
class Runtime:
def __init__(self): self.i = 0
async def arun(self, name, payload, execution_id=None):
self.i += 1
if self.i == 1:
return _result(name, "COMPLETED", {"cancelar_vas_avulso": {
"cancelados": [{"msisdn": payload["msisdn"], "servico": "Tamboro Mensal", "valor": "14,99"}],
"itens_para_contestacao": [{"msisdn": payload["msisdn"], "subject": "Tamboro Mensal"}],
}})
return _result(name, "COMPLETED", {
"registrar_protocolo": {"protocolo_id": "PRT-D2EFF9B40B", "protocol_closed": True},
"abrir_contestacao_cliente": {"success": True, "contested_items": [{"itemName": "Tamboro Mensal", "status": "INICIADA"}]},
})
runtime = Runtime()
monkeypatch.setattr(main, "get_workflow_runtime", lambda: runtime)
monkeypatch.setattr(main, "service", _Service())
result = await main._run_cancelamento_com_contestacao({"msisdn": "5511999999999", "subject": "Tamboro Mensal", "valor": "14,99"})
assert result["success"] is True
assert result["protocol_closed"] is True
assert result["contestacao_protocol"] == "PRT-D2EFF9B40B"
@pytest.mark.asyncio
async def test_lista_explicita_vazia_de_candidatos_nao_faz_fallback_para_cancelados(monkeypatch):
import contas_mcp.servers.contas_mcp_server.main as main
class Runtime:
def __init__(self): self.calls = []
async def arun(self, name, payload, execution_id=None):
self.calls.append(name)
return _result(name, "COMPLETED", {"cancelar_vas_avulso": {
"cancelados": [{"msisdn": payload["msisdn"], "servico": "TIM Fashion Mensal"}],
"contestation_candidates": [],
}})
runtime = Runtime()
monkeypatch.setattr(main, "get_workflow_runtime", lambda: runtime)
result = await main._run_cancelamento_com_contestacao({"msisdn": "5511999999999", "subject": "TIM Fashion Mensal", "valor": "14,99"})
assert runtime.calls == ["cancelamento_vas_avulso"]
assert result["success"] is True
assert "contestacao_workflow" not in result
@pytest.mark.asyncio
async def test_no_match_nao_contesta_valor_nem_chama_contestacao(monkeypatch):
import contas_mcp.servers.contas_mcp_server.main as main
class Runtime:
def __init__(self): self.calls = []
async def arun(self, name, payload, execution_id=None):
self.calls.append(name)
return _result(name, "COMPLETED", {"cancelar_vas_avulso": {
"results": [{"success": False, "subject": "Inexistente", "error": "serviço não encontrado", "reason": "service_not_found"}],
"contestation_candidates": [],
"nao_encontrados": [{"servico": "Inexistente"}],
}})
runtime = Runtime()
monkeypatch.setattr(main, "get_workflow_runtime", lambda: runtime)
result = await main._run_cancelamento_com_contestacao({"msisdn": "5511999999999", "subject": "Inexistente", "valor": "99,99"})
assert runtime.calls == ["cancelamento_vas_avulso"]
assert result["success"] is False
assert "contestacao_workflow" not in result
assert "99,99" not in result["mensagem"]
@pytest.mark.asyncio
async def test_protocol_closed_da_contestacao_e_preservado(monkeypatch):
import contas_mcp.servers.contas_mcp_server.main as main
class Runtime:
def __init__(self): self.i = 0
async def arun(self, name, payload, execution_id=None):
self.i += 1
if self.i == 1:
return _result(name, "COMPLETED", {"cancelar_vas_avulso": {
"results": [{"success": True, "subject": "TIM Fashion Mensal", "msisdn": payload["msisdn"]}],
"contestation_candidates": [{"success": True, "subject": "TIM Fashion Mensal", "msisdn": payload["msisdn"]}],
}})
return _result(name, "COMPLETED", {
"registrar_protocolo": {"protocolo_id": "PRT-200", "protocol_closed": True},
"abrir_contestacao_cliente": {"success": True, "contested_items": [{"itemName": "TIM Fashion Mensal", "status": "INICIADA"}], "protocol_closed": True},
})
runtime = Runtime()
monkeypatch.setattr(main, "get_workflow_runtime", lambda: runtime)
monkeypatch.setattr(main, "service", _Service())
result = await main._run_cancelamento_com_contestacao({"msisdn": "5511999999999", "subject": "TIM Fashion Mensal", "valor": "14,99"})
assert result["success"] is True
assert result["protocol_closed"] is True
assert result["contestacao_protocol"] == "PRT-200"
@pytest.mark.asyncio
async def test_sms_flag_interno_nao_vaza_no_contrato_externo(monkeypatch):
import contas_mcp.servers.contas_mcp_server.main as main
class Runtime:
def __init__(self): self.i = 0
async def arun(self, name, payload, execution_id=None):
self.i += 1
if self.i == 1:
return _result(name, "COMPLETED", {"cancelar_vas_avulso": {
"results": [{"success": True, "subject": "Conteudo Premium", "msisdn": payload["msisdn"]}],
"contestation_candidates": [{"success": True, "subject": "Conteudo Premium", "msisdn": payload["msisdn"]}],
}})
return _result(name, "COMPLETED", {
"registrar_protocolo": {"protocolo_id": "PRT-201"},
"abrir_contestacao_cliente": {"success": True},
"enviar_sms": {"success": True, "sms_sent": True},
})
runtime = Runtime()
monkeypatch.setattr(main, "get_workflow_runtime", lambda: runtime)
monkeypatch.setattr(main, "service", _Service())
result = await main._run_cancelamento_com_contestacao({"msisdn": "5511988887777", "subject": "Conteudo Premium", "valor": "9,90"})
assert result["contestacao_protocol"] == "PRT-201"
assert "sms_sent" not in result
assert result["sms_not_send_error"] is False
@pytest.mark.asyncio
async def test_cancelamento_com_protocolo_sem_item_contestado_continua_resolvido(monkeypatch):
import contas_mcp.servers.contas_mcp_server.main as main
class Runtime:
def __init__(self): self.i = 0
async def arun(self, name, payload, execution_id=None):
self.i += 1
if self.i == 1:
return _result(name, "COMPLETED", {"cancelar_vas_avulso": {
"cancelados": [{"msisdn": payload["msisdn"], "servico": "Tamboro Mensal"}],
"itens_para_contestacao": [{"msisdn": payload["msisdn"], "subject": "Tamboro Mensal"}],
"protocolos_por_linha": [{"msisdn": payload["msisdn"], "protocolo_id": "PRT-CANCEL-1"}],
}})
return _result(name, "COMPLETED", {
"registrar_protocolo": {"protocolo_id": "PRT-CONTEST-1"},
"abrir_contestacao_cliente": {"success": True, "contested_items": [], "not_contested_items": [], "contested_invoice_amount_open": "0"},
})
runtime = Runtime()
monkeypatch.setattr(main, "get_workflow_runtime", lambda: runtime)
monkeypatch.setattr(main, "service", _Service())
result = await main._run_cancelamento_com_contestacao({"msisdn": "5511999999999", "subject": "Tamboro Mensal", "valor": "14,99"})
assert result["success"] is True
assert result["recomenda_finalizacao"] is True
assert result["status_finalizacao_sugerido"] == "resolvido"
assert result["protocols_for_response"] == ["PRT-CANCEL-1", "PRT-CONTEST-1"]
@pytest.mark.asyncio
async def test_falha_sms_e_propagada_sem_expor_sms_sent(monkeypatch):
import contas_mcp.servers.contas_mcp_server.main as main
class Runtime:
def __init__(self): self.i = 0
async def arun(self, name, payload, execution_id=None):
self.i += 1
if self.i == 1:
return _result(name, "COMPLETED", {"cancelar_vas_avulso": {
"results": [{"success": True, "subject": "TIM Fashion Mensal", "msisdn": payload["msisdn"]}],
"contestation_candidates": [{"success": True, "subject": "TIM Fashion Mensal", "msisdn": payload["msisdn"]}],
}})
return _result(name, "COMPLETED", {
"registrar_protocolo": {"protocolo_id": "PRT-101"},
"abrir_contestacao_cliente": {"success": True},
"enviar_sms": {"success": False, "sms_sent": False, "sms_not_send_error": True},
})
runtime = Runtime()
monkeypatch.setattr(main, "get_workflow_runtime", lambda: runtime)
monkeypatch.setattr(main, "service", _Service())
result = await main._run_cancelamento_com_contestacao({"msisdn": "5511999999999", "subject": "TIM Fashion Mensal", "valor": "14,99"})
assert result["success"] is True
assert result["sms_not_send_error"] is True
assert "sms_sent" not in result
@pytest.mark.asyncio
async def test_candidato_explicito_e_contestado_mesmo_sem_cancelamento_bem_sucedido(monkeypatch):
import contas_mcp.servers.contas_mcp_server.main as main
class Runtime:
def __init__(self): self.i = 0; self.calls=[]
async def arun(self, name, payload, execution_id=None):
self.i += 1; self.calls.append((name, payload))
if self.i == 1:
return _result(name, "COMPLETED", {"cancelar_vas_avulso": {
"results": [{"success": False, "subject": "Inexistente", "reason": "service_not_found", "error": "não encontrado"}],
"contestation_candidates": [{"success": False, "subject": "Inexistente", "msisdn": payload["msisdn"], "iu_reason": "service_not_found"}],
}})
return _result(name, "COMPLETED", {
"registrar_protocolo": {"protocolo_id": "PRT-CANDIDATO"},
"abrir_contestacao_cliente": {"success": True, "contested_items": [{"itemName": "Inexistente", "status": "INICIADA"}]},
})
runtime = Runtime()
monkeypatch.setattr(main, "get_workflow_runtime", lambda: runtime)
monkeypatch.setattr(main, "service", _Service())
result = await main._run_cancelamento_com_contestacao({"msisdn": "5511999999999", "subject": "Inexistente", "valor": "14,99"})
assert [x[0] for x in runtime.calls] == ["cancelamento_vas_avulso", "contestacao_tool"]
assert runtime.calls[1][1]["items"][0]["itemName"] == "Inexistente"
assert runtime.calls[1][1]["items"][0]["claimedAmount"] == "14,99"
assert result["contestacao_protocol"] == "PRT-CANDIDATO"
@pytest.mark.asyncio
@pytest.mark.parametrize("reason,error", [("block_vas_failed", "Timeout no bloqueio"), ("cancel_vas_failed", "Erro no cancelamento")])
async def test_falha_parcial_rt01_ainda_dispara_rt02(monkeypatch, reason, error):
import contas_mcp.servers.contas_mcp_server.main as main
class Runtime:
def __init__(self): self.i=0; self.calls=[]
async def arun(self, name, payload, execution_id=None):
self.i += 1; self.calls.append((name,payload))
if self.i == 1:
return _result(name, "COMPLETED", {"cancelar_vas_avulso": {
"results": [{"success": False, "subject": "TIM Fashion Mensal", "reason": reason, "error": error}],
"nao_cancelados": [{"servico": "TIM Fashion Mensal", "motivo": reason, "erro": error}],
"contestation_candidates": [{"subject": "TIM Fashion Mensal", "msisdn": payload["msisdn"], "motivo": reason, "erro": error}],
}})
return _result(name, "COMPLETED", {
"registrar_protocolo": {"protocolo_id": "PRT-RT02"},
"abrir_contestacao_cliente": {"success": True, "contested_items": [{"itemName": "TIM Fashion Mensal", "status": "INICIADA"}]},
})
runtime=Runtime()
monkeypatch.setattr(main,"get_workflow_runtime",lambda:runtime)
monkeypatch.setattr(main,"service",_Service())
result=await main._run_cancelamento_com_contestacao({"msisdn":"5511999999999","subject":"TIM Fashion Mensal","valor":"14,99"})
assert [x[0] for x in runtime.calls] == ["cancelamento_vas_avulso","contestacao_tool"]
assert result["contestacao_protocol"] == "PRT-RT02"
@pytest.mark.asyncio
async def test_holder_msisdn_nao_e_trocado_pela_linha_dependente(monkeypatch):
import contas_mcp.servers.contas_mcp_server.main as main
class Runtime:
def __init__(self): self.i=0; self.calls=[]
async def arun(self,name,payload,execution_id=None):
self.i += 1; self.calls.append((name,payload))
if self.i == 1:
return _result(name,"COMPLETED",{"cancelar_vas_avulso":{
"results":[{"success":True,"subject":"TIM Fashion Mensal","msisdn":"5511888880000"}],
"contestation_candidates":[{"success":True,"subject":"TIM Fashion Mensal","msisdn":"5511888880000"}],
}})
return _result(name,"COMPLETED",{"registrar_protocolo":{"protocolo_id":"PRT-FAM"},"abrir_contestacao_cliente":{"success":True}})
runtime=Runtime()
monkeypatch.setattr(main,"get_workflow_runtime",lambda:runtime)
monkeypatch.setattr(main,"service",_Service())
await main._run_cancelamento_com_contestacao({
"msisdn":"5511888880000",
"holder_msisdn":"5511999999999",
"items":[{"msisdn":"5511888880000","name":"TIM Fashion Mensal","value":"14,99"}],
})
assert runtime.calls[0][1]["msisdn"] == "5511999999999"
assert runtime.calls[1][1]["msisdn"] == "5511999999999"
assert runtime.calls[1][1]["dependent_invoice_item"] is True
@pytest.mark.asyncio
async def test_item_do_titular_nao_e_marcado_como_dependente(monkeypatch):
import contas_mcp.servers.contas_mcp_server.main as main
class Runtime:
def __init__(self): self.i=0; self.calls=[]
async def arun(self,name,payload,execution_id=None):
self.i += 1; self.calls.append((name,payload))
if self.i == 1:
return _result(name,"COMPLETED",{"cancelar_vas_avulso":{
"results":[{"success":True,"subject":"TIM Games","msisdn":"5511999999999"}],
"contestation_candidates":[{"success":True,"subject":"TIM Games","msisdn":"5511999999999"}],
}})
return _result(name,"COMPLETED",{"registrar_protocolo":{"protocolo_id":"PRT-TIT"},"abrir_contestacao_cliente":{"success":True}})
runtime=Runtime()
monkeypatch.setattr(main,"get_workflow_runtime",lambda:runtime)
monkeypatch.setattr(main,"service",_Service())
await main._run_cancelamento_com_contestacao({"msisdn":"5511999999999","subject":"TIM Games","valor":"9,90"})
assert runtime.calls[1][1]["dependent_invoice_item"] is False
@pytest.mark.asyncio
async def test_titular_e_dependentes_permancem_em_uma_unica_contestacao(monkeypatch):
import contas_mcp.servers.contas_mcp_server.main as main
class Runtime:
def __init__(self): self.i=0; self.calls=[]
async def arun(self,name,payload,execution_id=None):
self.i += 1; self.calls.append((name,payload))
if self.i == 1:
cands=[
{"success":True,"subject":"TIM Games","msisdn":"5511999999999"},
{"success":True,"subject":"TIM Fashion Mensal","msisdn":"5511888880000"},
{"success":True,"subject":"Tamboro Mensal","msisdn":"5511777770000"},
]
return _result(name,"COMPLETED",{"cancelar_vas_avulso":{"results":cands,"contestation_candidates":cands}})
return _result(name,"COMPLETED",{"registrar_protocolo":{"protocolo_id":"PRT-FAM-3"},"abrir_contestacao_cliente":{"success":True}})
runtime=Runtime()
monkeypatch.setattr(main,"get_workflow_runtime",lambda:runtime)
monkeypatch.setattr(main,"service",_Service())
await main._run_cancelamento_com_contestacao({
"msisdn":"5511999999999",
"items":[
{"msisdn":"5511999999999","name":"TIM Games","value":"9,90"},
{"msisdn":"5511888880000","name":"TIM Fashion Mensal","value":"14,99"},
{"msisdn":"5511777770000","name":"Tamboro Mensal","value":"8,50"},
],
})
assert [x[0] for x in runtime.calls] == ["cancelamento_vas_avulso","contestacao_tool"]
contest=runtime.calls[1][1]
assert contest["msisdn"] == "5511999999999"
assert {x["itemName"] for x in contest["items"]} == {"TIM Games","TIM Fashion Mensal","Tamboro Mensal"}
assert contest["dependent_invoice_item"] is True