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