Projeto do Agent Contas ORACLE
This commit is contained in:
198
tests/migration/test_invoice_context_framework_native.py
Normal file
198
tests/migration/test_invoice_context_framework_native.py
Normal file
@@ -0,0 +1,198 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
from agent_framework.cache.cache import InMemoryCache
|
||||
from app.domain.contas.invoice_context import InvoiceContextService
|
||||
|
||||
|
||||
class Client:
|
||||
def __init__(self):
|
||||
self.complete_calls = 0
|
||||
self.billing_calls = 0
|
||||
self.pdf_calls = 0
|
||||
|
||||
def consultar_faturas(self, msisdn):
|
||||
self.complete_calls += 1
|
||||
return {
|
||||
"billingProfile": {"customer": {"customerId": "C1"}},
|
||||
"paymentItems": [{"invoiceId": "I1"}],
|
||||
}
|
||||
|
||||
def billing_analysis(self, msisdn, **kwargs):
|
||||
self.billing_calls += 1
|
||||
return {"invoiceExplanation": "explicacao"}
|
||||
|
||||
def bill_pdf(self, msisdn, invoice_id, customer_id, **kwargs):
|
||||
self.pdf_calls += 1
|
||||
return {"119": {"SVA Detalhe Total": [{"desc": "Tamboro"}]}}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_invoice_context_reusa_cache_na_mesma_sessao():
|
||||
client = Client()
|
||||
svc = InvoiceContextService(client, InMemoryCache(), ttl_seconds=60) # type: ignore[arg-type]
|
||||
a = await svc.get(session_id="s1", msisdn="119", include_detail=True)
|
||||
b = await svc.get(session_id="s1", msisdn="119", include_detail=True)
|
||||
assert a.cache_hit is False
|
||||
assert b.cache_hit is True
|
||||
assert client.complete_calls == 1
|
||||
assert client.billing_calls == 1
|
||||
assert client.pdf_calls == 1
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_invoice_context_isola_cache_por_sessao():
|
||||
client = Client()
|
||||
svc = InvoiceContextService(client, InMemoryCache(), ttl_seconds=60) # type: ignore[arg-type]
|
||||
await svc.get(session_id="s1", msisdn="119")
|
||||
await svc.get(session_id="s2", msisdn="119")
|
||||
assert client.complete_calls == 2
|
||||
assert client.billing_calls == 2
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_invoice_context_sem_session_nao_compartilha_cache_global():
|
||||
client = Client()
|
||||
svc = InvoiceContextService(client, InMemoryCache(), ttl_seconds=60) # type: ignore[arg-type]
|
||||
await svc.get(session_id="", msisdn="119")
|
||||
await svc.get(session_id="", msisdn="119")
|
||||
assert client.complete_calls == 2
|
||||
assert client.billing_calls == 2
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_invoice_context_use_cache_false_forca_nova_busca():
|
||||
client = Client()
|
||||
svc = InvoiceContextService(client, InMemoryCache(), ttl_seconds=60) # type: ignore[arg-type]
|
||||
await svc.get(session_id="s1", msisdn="119")
|
||||
await svc.get(session_id="s1", msisdn="119", use_cache=False)
|
||||
assert client.complete_calls == 2
|
||||
assert client.billing_calls == 2
|
||||
|
||||
|
||||
def test_invoice_explanation_reusa_evidencia_prefetch_sem_nova_api():
|
||||
from app.domain.contas.service import ContasDomainService
|
||||
|
||||
class C(Client):
|
||||
def consultar_faturas(self, msisdn):
|
||||
raise AssertionError("nao deveria consultar novamente")
|
||||
def billing_analysis(self, msisdn, **kwargs):
|
||||
raise AssertionError("nao deveria consultar novamente")
|
||||
|
||||
service = ContasDomainService(client=C()) # type: ignore[arg-type]
|
||||
result = service.invoice_explanation(
|
||||
msisdn="119",
|
||||
complete_invoices_payload={"paymentItems": []},
|
||||
billing_analysis={"invoiceExplanation": "cache"},
|
||||
)
|
||||
assert result["billing_analysis"]["invoiceExplanation"] == "cache"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_invoice_context_singleflight_mesma_sessao_nao_duplica_chamadas():
|
||||
import asyncio
|
||||
|
||||
class Blocking(Client):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.release = asyncio.Event()
|
||||
|
||||
def consultar_faturas(self, msisdn):
|
||||
import time
|
||||
self.complete_calls += 1
|
||||
time.sleep(0.08)
|
||||
return {
|
||||
"billingProfile": {"customer": {"customerId": "C1"}},
|
||||
"paymentItems": [{"invoiceId": "I1"}],
|
||||
}
|
||||
|
||||
def billing_analysis(self, msisdn, **kwargs):
|
||||
import time
|
||||
self.billing_calls += 1
|
||||
time.sleep(0.08)
|
||||
return {"invoiceExplanation": "explicacao"}
|
||||
|
||||
client = Blocking()
|
||||
svc = InvoiceContextService(client, InMemoryCache(), ttl_seconds=60) # type: ignore[arg-type]
|
||||
a, b = await asyncio.gather(
|
||||
svc.get(session_id="s1", msisdn="119", message_id="m1"),
|
||||
svc.get(session_id="s1", msisdn="119", message_id="m1"),
|
||||
)
|
||||
assert client.complete_calls == 1
|
||||
assert client.billing_calls == 1
|
||||
assert a.complete_invoices == b.complete_invoices
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_invoice_context_prefetch_emite_cvn_uma_vez_por_sessao():
|
||||
client = Client()
|
||||
svc = InvoiceContextService(client, InMemoryCache(), ttl_seconds=60) # type: ignore[arg-type]
|
||||
first = await svc.get(session_id="s1", msisdn="119", message_id="msg-1", use_cache=False)
|
||||
second = await svc.get(session_id="s1", msisdn="119", message_id="msg-2", use_cache=False)
|
||||
assert [x["code"] for x in first.business_events or []] == ["CVN.002", "CVN.006"]
|
||||
assert second.business_events == []
|
||||
assert first.business_events[0]["payload"]["messageId"] == "msg-1"
|
||||
assert first.business_events[1]["payload"]["agentSpecificData"] == '{"billingId": "I1"}'
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_invoice_context_nova_sessao_pode_emitir_cvn_novamente():
|
||||
client = Client()
|
||||
svc = InvoiceContextService(client, InMemoryCache(), ttl_seconds=60) # type: ignore[arg-type]
|
||||
first = await svc.get(session_id="s1", msisdn="119", use_cache=False)
|
||||
second = await svc.get(session_id="s2", msisdn="119", use_cache=False)
|
||||
assert [x["code"] for x in first.business_events or []] == ["CVN.002", "CVN.006"]
|
||||
assert [x["code"] for x in second.business_events or []] == ["CVN.002", "CVN.006"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_invoice_context_billing_analysis_falha_emite_cvn007_sem_perder_complete_invoices():
|
||||
class Failing(Client):
|
||||
def billing_analysis(self, msisdn, **kwargs):
|
||||
self.billing_calls += 1
|
||||
raise RuntimeError("HTTP 550")
|
||||
|
||||
client = Failing()
|
||||
svc = InvoiceContextService(client, InMemoryCache(), ttl_seconds=60) # type: ignore[arg-type]
|
||||
ctx = await svc.get(session_id="s1", msisdn="119", message_id="msg-fail", use_cache=False)
|
||||
assert ctx.complete_invoices is not None
|
||||
assert ctx.billing_analysis is None
|
||||
assert "billing_analysis" in str(ctx.error)
|
||||
assert [x["code"] for x in ctx.business_events or []] == ["CVN.002", "CVN.007"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_invoice_context_cache_incompleto_busca_detail_quando_solicitado_depois():
|
||||
client = Client()
|
||||
svc = InvoiceContextService(client, InMemoryCache(), ttl_seconds=60) # type: ignore[arg-type]
|
||||
await svc.get(session_id="s1", msisdn="119", include_detail=False)
|
||||
detailed = await svc.get(session_id="s1", msisdn="119", include_detail=True)
|
||||
assert detailed.invoice_detail is not None
|
||||
assert client.pdf_calls == 1
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_invoice_context_expoe_timings_e_erros_por_subconsulta():
|
||||
class Partial(Client):
|
||||
def billing_analysis(self, msisdn, **kwargs):
|
||||
self.billing_calls += 1
|
||||
raise RuntimeError("indisponivel")
|
||||
|
||||
client = Partial()
|
||||
svc = InvoiceContextService(client, InMemoryCache(), ttl_seconds=60) # type: ignore[arg-type]
|
||||
ctx = await svc.get(session_id="s1", msisdn="119", include_detail=True, use_cache=False)
|
||||
assert ctx.metadata["cache_hit"] is False
|
||||
assert ctx.metadata["fetch_elapsed_ms"] >= 0
|
||||
assert {"complete_invoices", "billing_analysis", "invoice_detail"} <= set(ctx.metadata["task_timings"])
|
||||
assert "billing_analysis" in ctx.errors
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_invoice_context_cache_hit_expoe_cache_age_sem_republicar_eventos():
|
||||
client = Client()
|
||||
svc = InvoiceContextService(client, InMemoryCache(), ttl_seconds=60) # type: ignore[arg-type]
|
||||
await svc.get(session_id="s1", msisdn="119")
|
||||
cached = await svc.get(session_id="s1", msisdn="119")
|
||||
assert cached.cache_hit is True
|
||||
assert cached.metadata["cache_hit"] is True
|
||||
assert cached.metadata["cache_age_ms"] >= 0
|
||||
assert cached.business_events == []
|
||||
Reference in New Issue
Block a user