from contextlib import nullcontext from pathlib import Path import app.communication_service as service class _Result: def __init__(self, *, scalar_value=None, rows=()): self.scalar_value = scalar_value self.rows = list(rows) def scalar(self): return self.scalar_value def mappings(self): return self def all(self): return self.rows class _Connection: def __init__(self, *, possible, rows=()): self.possible = possible self.rows = rows self.statements = [] def execute(self, statement, params=None): self.statements.append((str(statement), params)) if len(self.statements) == 1: return _Result(scalar_value=self.possible) return _Result(rows=self.rows) class _Engine: def __init__(self, connection): self.connection = connection def begin(self): return nullcontext(self.connection) def test_no_historical_candidates_returns_after_one_statement(monkeypatch): connection = _Connection(possible=False) monkeypatch.setattr(service, "engine", _Engine(connection)) assert service._list_message_backed_chatwoot_items_for_opportunity( "dc3c020e-33eb-4067-b7da-ab684533a425" ) == [] assert len(connection.statements) == 1 assert "SELECT EXISTS" in connection.statements[0][0] def test_positive_fallback_uses_two_statements_and_preserves_rows(monkeypatch): expected = [{"id": "message-1", "source_system": "chatwoot", "created_at": "2026-08-14"}] connection = _Connection(possible=True, rows=expected) monkeypatch.setattr(service, "engine", _Engine(connection)) result = service._list_message_backed_chatwoot_items_for_opportunity( "dc3c020e-33eb-4067-b7da-ab684533a425", limit=12 ) assert result == expected assert len(connection.statements) == 2 assert connection.statements[1][1]["limit"] == 12 def test_relationship_paths_are_unioned_without_or_join_conditions(monkeypatch): connection = _Connection(possible=True) monkeypatch.setattr(service, "engine", _Engine(connection)) service._list_message_backed_chatwoot_items_for_opportunity( "dc3c020e-33eb-4067-b7da-ab684533a425" ) sql = connection.statements[1][0] assert "re.id = m.raw_event_id OR re.message_id = m.id" not in sql assert "ar.message_id = m.id OR ar.raw_event_id = re.id" not in sql assert "t.message_id = m.id OR t.raw_event_id = re.id" not in sql assert sql.count(" UNION\n") >= 10 assert "SELECT DISTINCT ON (m.id)" in sql assert "ORDER BY m.id, COALESCE(t.created_at, m.created_at) DESC" in sql assert "ORDER BY created_at DESC" in sql def test_only_missing_relationship_indexes_are_added(): migration = Path("migrations/008_historical_communication_lookup_indexes.sql").read_text() expected = { "messages(raw_event_id)", "raw_events(message_id)", "action_runs(message_id)", "action_runs(raw_event_id)", "tasks(message_id)", "tasks(raw_event_id)", "tasks(action_run_id)", } assert all(columns in migration for columns in expected) assert "messages(conversation_id)" not in migration assert "raw_events(conversation_id)" not in migration assert "tasks(opportunity_id)" not in migration