diff --git a/database/models/core.py b/database/models/core.py index b91830578..3dc1168bf 100644 --- a/database/models/core.py +++ b/database/models/core.py @@ -297,7 +297,9 @@ class Pull(CodecovBaseModel): behind_by_commit = Column(types.Text) author = relationship(Owner) - repository = relationship(Repository, backref=backref("pulls", cascade="delete")) + repository = relationship( + Repository, backref=backref("pulls", cascade="delete", lazy="dynamic") + ) __table_args__ = (Index("pulls_repoid_pullid", "repoid", "pullid", unique=True),) diff --git a/services/notification/notifiers/comment/__init__.py b/services/notification/notifiers/comment/__init__.py index 2cd2ab7a3..3ab50e8aa 100644 --- a/services/notification/notifiers/comment/__init__.py +++ b/services/notification/notifiers/comment/__init__.py @@ -8,6 +8,7 @@ ) from database.enums import Notification +from database.models import Pull from helpers.metrics import metrics from services.comparison.types import Comparison from services.license import requires_license @@ -351,6 +352,13 @@ def is_enabled(self) -> bool: self.notifier_yaml_settings, dict ) + async def is_first_pull(self, comparison: Comparison) -> bool: + pull = comparison.pull + first_pull = ( + pull.repository.pulls.with_entities(Pull.id_).order_by(Pull.id_).first() + ) + return first_pull.id_ == pull.id_ + async def build_message(self, comparison: Comparison) -> List[str]: if self.should_use_upgrade_decoration(): return self._create_upgrade_message(comparison) @@ -358,11 +366,22 @@ async def build_message(self, comparison: Comparison) -> List[str]: return self._create_empty_upload_message() if self.should_use_upload_limit_decoration(): return self._create_reached_upload_limit_message(comparison) + if await self.is_first_pull(comparison): + return self._create_welcome_message() pull_dict = comparison.enriched_pull.provider_pull return await self.create_message( comparison, pull_dict, self.notifier_yaml_settings ) + def _create_welcome_message(self): + return [ + "## Welcome to [Codecov](https://codecov.io) :tada:", + "", + "Once merged to your default branch, Codecov will compare your coverage reports and display the results in this comment.", + "", + "Thanks for integrating Codecov - We've got you covered :open_umbrella:", + ] + def _create_empty_upload_message(self): if self.is_passing_empty_upload(): return [ diff --git a/services/notification/notifiers/tests/integration/test_comment.py b/services/notification/notifiers/tests/integration/test_comment.py index 6707ed716..19b0530df 100644 --- a/services/notification/notifiers/tests/integration/test_comment.py +++ b/services/notification/notifiers/tests/integration/test_comment.py @@ -10,6 +10,14 @@ from services.notification.notifiers.comment import CommentNotifier +@pytest.fixture +def is_not_first_pull(mocker): + mocker.patch( + "services.notification.notifiers.comment.CommentNotifier.is_first_pull", + return_value=False, + ) + + @pytest.fixture def codecove2e_comparison(dbsession, request, sample_report, small_report): repository = RepositoryFactory.create( @@ -319,6 +327,7 @@ def sample_comparison_for_limited_upload( ) +@pytest.mark.usefixtures("is_not_first_pull") class TestCommentNotifierIntegration(object): @pytest.mark.asyncio async def test_notify(self, sample_comparison, codecov_vcr, mock_configuration): diff --git a/services/notification/notifiers/tests/unit/test_comment.py b/services/notification/notifiers/tests/unit/test_comment.py index b219e5d7c..aef0e4f63 100644 --- a/services/notification/notifiers/tests/unit/test_comment.py +++ b/services/notification/notifiers/tests/unit/test_comment.py @@ -36,6 +36,14 @@ from services.yaml.reader import get_components_from_yaml +@pytest.fixture +def is_not_first_pull(mocker): + mocker.patch( + "services.notification.notifiers.comment.CommentNotifier.is_first_pull", + return_value=False, + ) + + @pytest.fixture def sample_comparison_bunch_empty_flags(request, dbsession, mocker): """ @@ -500,6 +508,7 @@ async def test__possibly_write_gh_app_login_announcement_enterprise( assert mock_write.call_count == 0 +@pytest.mark.usefixtures("is_not_first_pull") class TestCommentNotifier(object): @pytest.mark.asyncio async def test_is_enabled_settings_individual_settings_false(self, dbsession): @@ -3864,6 +3873,7 @@ async def test_footer_section_writer_with_project_cov_hidden(self, mocker): ] +@pytest.mark.usefixtures("is_not_first_pull") class TestCommentNotifierInNewLayout(object): @pytest.mark.asyncio async def test_create_message_files_section_with_critical_files_new_layout( @@ -4715,3 +4725,29 @@ async def test_write_message_component_section_no_base( "| [py_files](urlurl/components?src=pr&el=component) | `50.00% <0.00%> (?)` | |", ] assert message == expected + + +class TestCommentNotifierWelcome: + @pytest.mark.asyncio + async def test_build_message( + self, dbsession, mock_configuration, mock_repo_provider, sample_comparison + ): + mock_configuration.params["setup"]["codecov_dashboard_url"] = "test.example.br" + notifier = CommentNotifier( + repository=sample_comparison.head.commit.repository, + title="title", + notifier_yaml_settings={"layout": "reach, diff, flags, files, footer"}, + notifier_site_settings=True, + current_yaml={}, + ) + result = await notifier.build_message(sample_comparison) + expected_result = [ + "## Welcome to [Codecov](https://codecov.io) :tada:", + "", + "Once merged to your default branch, Codecov will compare your coverage reports and display the results in this comment.", + "", + "Thanks for integrating Codecov - We've got you covered :open_umbrella:", + ] + for exp, res in zip(expected_result, result): + assert exp == res + assert result == expected_result diff --git a/tasks/tests/integration/test_notify_task.py b/tasks/tests/integration/test_notify_task.py index 62eeee806..c5c083a10 100644 --- a/tasks/tests/integration/test_notify_task.py +++ b/tasks/tests/integration/test_notify_task.py @@ -4,7 +4,7 @@ import pytest from database.models import Pull -from database.tests.factories import CommitFactory, RepositoryFactory +from database.tests.factories import CommitFactory, PullFactory, RepositoryFactory from services.archive import ArchiveService from services.notification.notifiers.base import NotificationResult from tasks.notify import NotifyTask @@ -807,6 +807,8 @@ async def test_simple_call_status_and_notifiers( repository=repository, author=repository.owner, ) + # create another pull so that we don't trigger the 1st time comment message + dbsession.add(PullFactory.create(repository=repository, pullid=8)) commit = CommitFactory.create( message="", pullid=9,