perf: eliminate N+1 in opportunities next actions
This commit is contained in:
107
tests/test_opportunity_next_action_bulk.py
Normal file
107
tests/test_opportunity_next_action_bulk.py
Normal file
@@ -0,0 +1,107 @@
|
||||
from contextlib import nullcontext
|
||||
|
||||
import pytest
|
||||
|
||||
import app.document_reconciliation_service as document_service
|
||||
import app.operation_service as operation_service
|
||||
import app.opportunity_next_action_service as service
|
||||
from app.domain.opportunity_flow import build_opportunity_evidence
|
||||
|
||||
|
||||
class _Result:
|
||||
def __init__(self, rows):
|
||||
self._rows = rows
|
||||
|
||||
def mappings(self):
|
||||
return self
|
||||
|
||||
def all(self):
|
||||
return self._rows
|
||||
|
||||
|
||||
class _Connection:
|
||||
def __init__(self, opportunities, *, tasks=None, operation_links=None):
|
||||
self.opportunities = opportunities
|
||||
self.tasks = tasks or []
|
||||
self.operation_links = operation_links or []
|
||||
self.query_count = 0
|
||||
|
||||
def execute(self, statement, params=None):
|
||||
self.query_count += 1
|
||||
sql = str(statement)
|
||||
if "FROM opportunities WHERE" in sql:
|
||||
wanted = set((params or {}).get("opportunity_ids", []))
|
||||
return _Result([row for oid, row in self.opportunities.items() if oid in wanted])
|
||||
if "FROM tasks WHERE" in sql:
|
||||
return _Result(self.tasks)
|
||||
if "FROM operation_links WHERE" in sql:
|
||||
return _Result(self.operation_links)
|
||||
return _Result([])
|
||||
|
||||
|
||||
class _Engine:
|
||||
def __init__(self, connection):
|
||||
self.connection = connection
|
||||
|
||||
def begin(self):
|
||||
return nullcontext(self.connection)
|
||||
|
||||
|
||||
def _opportunity(oid):
|
||||
return {
|
||||
"id": oid,
|
||||
"stage": "NEW_LEAD",
|
||||
"status": "open",
|
||||
"title": "Teste",
|
||||
"fiscal_customer_id": None,
|
||||
"customer_id": None,
|
||||
"metadata": {},
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("tasks,operation_links", [
|
||||
([], []),
|
||||
([{"id": "task-1", "opportunity_id": "11111111-1111-1111-1111-111111111111",
|
||||
"action_code": "CONTACT_CUSTOMER", "action": "Contactar cliente", "note": "",
|
||||
"priority": "alta", "route": "/tasks/task-1", "status": "pending",
|
||||
"due_at": None, "created_at": None, "metadata": {}, "rn": 1}], []),
|
||||
([], [{"id": "link-1", "opportunity_id": "11111111-1111-1111-1111-111111111111",
|
||||
"system": "clientflow", "external_type": "payment", "external_id": None,
|
||||
"external_name": "Pagamento", "external_url": None, "status": "confirmed",
|
||||
"payload": {}, "last_synced_at": None, "created_at": None, "updated_at": None}]),
|
||||
])
|
||||
def test_bulk_decision_is_equivalent_to_individual(monkeypatch, tasks, operation_links):
|
||||
oid = "11111111-1111-1111-1111-111111111111"
|
||||
row = _opportunity(oid)
|
||||
connection = _Connection({oid: row}, tasks=tasks, operation_links=operation_links)
|
||||
monkeypatch.setattr(service, "engine", _Engine(connection))
|
||||
monkeypatch.setattr(operation_service, "ensure_operation_schema", lambda: None)
|
||||
monkeypatch.setattr(document_service, "document_reconciliation_v2_available", lambda conn=None: False)
|
||||
|
||||
snapshots = service._bulk_operation_snapshots([oid], {oid: operation_links}, {oid: []})
|
||||
evidence = build_opportunity_evidence(
|
||||
row, linked_documents=[], tasks=tasks, operation_snapshot=snapshots[oid],
|
||||
linked_customer=None, fiscal_data_complete=False,
|
||||
has_reconciliation_candidate=False, company_profile="blif",
|
||||
)
|
||||
monkeypatch.setattr(service, "_build_db_evidence", lambda opportunity_id: evidence)
|
||||
|
||||
assert service.get_opportunity_next_actions([oid])[oid] == service.get_opportunity_next_action(oid)
|
||||
|
||||
|
||||
def test_bulk_query_count_is_constant_for_batch_size(monkeypatch):
|
||||
monkeypatch.setattr(operation_service, "ensure_operation_schema", lambda: None)
|
||||
monkeypatch.setattr(document_service, "document_reconciliation_v2_available", lambda conn=None: False)
|
||||
|
||||
def measured(size):
|
||||
opportunities = {
|
||||
f"00000000-0000-0000-0000-{number:012d}":
|
||||
_opportunity(f"00000000-0000-0000-0000-{number:012d}")
|
||||
for number in range(1, size + 1)
|
||||
}
|
||||
connection = _Connection(opportunities)
|
||||
monkeypatch.setattr(service, "engine", _Engine(connection))
|
||||
service.get_opportunity_next_actions(opportunities)
|
||||
return connection.query_count
|
||||
|
||||
assert measured(1) == measured(50) == 6
|
||||
Reference in New Issue
Block a user