def test_completed_is_true_if_pr_is_closed_and_was_approved_before_merging_even_if_changes_had_been_requested( self, ): pull_request = build(builder.pull_request().closed(True).merged( True).merged_at("2020-01-13T14:59:59Z").reviews([ (builder.review().submitted_at("2020-01-13T14:59:57Z").state( ReviewState.CHANGES_REQUESTED)), (builder.review().submitted_at("2020-01-13T14:59:58Z").state( ReviewState.APPROVED)), ])) task_fields = src.asana.helpers.extract_task_fields_from_pull_request( pull_request) self.assertEqual(True, task_fields["completed"])
def test_pull_request_review_mentions(self): pull_request = build(builder.pull_request().reviews([ builder.review("").comments([ builder.comment("@one @two @three"), builder.comment("@four") ]), builder.review("@a @b @c").comments( [builder.comment(""), builder.comment("@five")]), ])) self.assertEqual( sorted(github_logic._pull_request_review_mentions(pull_request)), ["a", "b", "c", "five", "four", "one", "three", "two"], )
def test_is_pull_request_ready_for_automerge_after_approval_approved_and_requested_changes( self, ): author_1 = builder.user().login("author_1") author_2 = builder.user().login("author_2") pull_request = build(builder.pull_request().commit( builder.commit().status(Commit.BUILD_SUCCESSFUL)).reviews([ builder.review().submitted_at("2020-01-11T14:59:58Z").state( ReviewState.CHANGES_REQUESTED).author(author_1), builder.review().submitted_at("2020-01-12T14:59:58Z").state( ReviewState.APPROVED).author(author_2), ]).mergeable(MergeableState.MERGEABLE).merged(False).label( builder.label().name(github_logic.AutomergeLabel. AFTER_TESTS_AND_APPROVAL.value))) self.assertFalse( github_logic._is_pull_request_ready_for_automerge(pull_request))
def test_includes_asana_review_comment_author(self): github_review = build(builder.review().author( builder.user("github_test_user_login")).state(ReviewState.DEFAULT)) asana_review_comment = src.asana.helpers.asana_comment_from_github_review( github_review) self.assertContainsStrings(asana_review_comment, ["TEST_USER_ASANA_DOMAIN_USER_ID"])
def test_includes_link_to_review(self): url = "https://github.com/Asana/SGTM/pull/31#issuecomment-626850667" github_review = build(builder.review().state( ReviewState.DEFAULT).url(url)) asana_review_comment = src.asana.helpers.asana_comment_from_github_review( github_review) self.assertContainsStrings(asana_review_comment, [f'<A href="{url}">'])
def test_handles_at_sign_in_review_gracefully(self): github_review = build(builder.review().author( builder.user("github_unknown_user_login")).state( ReviewState.DEFAULT).body("*****@*****.**")) asana_review_comment = src.asana.helpers.asana_comment_from_github_review( github_review) self.assertContainsStrings(asana_review_comment, ["*****@*****.**"])
def test_transforms_github_at_mentions_to_asana_at_mentions(self): github_review = build(builder.review().author( builder.user("github_unknown_user_login")).state( ReviewState.DEFAULT).body("@github_test_user_login")) asana_review_comment = src.asana.helpers.asana_comment_from_github_review( github_review) self.assertContainsStrings(asana_review_comment, ["TEST_USER_ASANA_DOMAIN_USER_ID"])
def test_individual_that_is_at_mentioned_in_review_comments_is_a_follower( self): pull_request = build(builder.pull_request().reviews( [builder.review().body("@github_test_user_login")])) task_fields = src.asana.helpers.extract_task_fields_from_pull_request( pull_request) self.assertIn("TEST_USER_ASANA_DOMAIN_USER_ID", task_fields["followers"])
def test_is_pull_request_ready_for_automerge_no_automerge_label(self): pull_request = build(builder.pull_request().commit( builder.commit().status(Commit.BUILD_SUCCESSFUL)).review( builder.review().submitted_at("2020-01-13T14:59:58Z").state( ReviewState.APPROVED)).mergeable( MergeableState.MERGEABLE).merged(False).label( builder.label().name("random label"))) self.assertFalse( github_logic._is_pull_request_ready_for_automerge(pull_request))
def test_handles_non_asana_review_comment_author_that_has_no_name_gracefully( self): github_review = build(builder.review().author( builder.user("github_unknown_user_login")).state( ReviewState.DEFAULT)) asana_review_comment_comment = src.asana.helpers.asana_comment_from_github_review( github_review) self.assertContainsStrings(asana_review_comment_comment, ["github_unknown_user_login"])
def test_is_pull_request_ready_for_automerge_build_pending(self): pull_request = build(builder.pull_request().commit( builder.commit().status(Commit.BUILD_PENDING)).review( builder.review().submitted_at("2020-01-13T14:59:58Z").state( ReviewState.APPROVED)).mergeable( MergeableState.MERGEABLE).merged(False).label( builder.label().name(github_logic.AutomergeLabel. AFTER_TESTS.value))) self.assertFalse( github_logic._is_pull_request_ready_for_automerge(pull_request))
def test_reviewer_is_a_follower(self): pull_request = build(builder.pull_request().reviews([ builder.review().submitted_at("2020-02-13T14:59:57Z").state( ReviewState.CHANGES_REQUESTED).body("LGTM!").author( builder.user("github_test_user_login")) ])) task_fields = src.asana.helpers.extract_task_fields_from_pull_request( pull_request) self.assertIn("TEST_USER_ASANA_DOMAIN_USER_ID", task_fields["followers"])
def test_includes_review_text(self): github_review = build(builder.review().author( builder.user("github_unknown_user_login")).state( ReviewState.DEFAULT).body("GITHUB_REVIEW_TEXT").comment( builder.comment().body("GITHUB_REVIEW_COMMENT_TEXT"))) asana_review_comment = src.asana.helpers.asana_comment_from_github_review( github_review) self.assertContainsStrings( asana_review_comment, ["GITHUB_REVIEW_TEXT", "GITHUB_REVIEW_COMMENT_TEXT"])
def test_completed_handles_gracefully_if_pr_is_closed_and_pr_was_approved_before_merging_with_merged_glitch( self, ): pull_request = build(builder.pull_request().closed(True).merged( False).merged_at("2020-01-13T14:59:58Z").reviews([ builder.review().submitted_at("2020-01-13T14:59:59Z").state( ReviewState.APPROVED) ])) task_fields = src.asana.helpers.extract_task_fields_from_pull_request( pull_request) self.assertEqual(True, task_fields["completed"])
def test_is_pull_request_ready_for_automerge_after_tests(self): pull_request = build(builder.pull_request().commit( builder.commit().status(Commit.BUILD_SUCCESSFUL)).review( builder.review().submitted_at("2020-01-13T14:59:58Z").state( ReviewState.CHANGES_REQUESTED)).mergeable( MergeableState.MERGEABLE).merged(False).label( builder.label().name(github_logic.AutomergeLabel. AFTER_TESTS.value))) self.assertTrue( github_logic._is_pull_request_ready_for_automerge(pull_request))
def test_is_pull_request_ready_for_automerge_immediately_conflicting(self): pull_request = build(builder.pull_request().commit( builder.commit().status(Commit.BUILD_FAILED)).review( builder.review().submitted_at("2020-01-13T14:59:58Z").state( ReviewState.CHANGES_REQUESTED)).mergeable( MergeableState.CONFLICTING).merged(False).label( builder.label().name(github_logic.AutomergeLabel. IMMEDIATELY.value))) self.assertFalse( github_logic._is_pull_request_ready_for_automerge(pull_request))
def test_is_pull_request_ready_for_automerge_after_tests_mergeable_is_false( self): pull_request = build(builder.pull_request().commit( builder.commit().status(Commit.BUILD_SUCCESSFUL)).review( builder.review().submitted_at("2020-01-13T14:59:58Z").state( ReviewState.APPROVED)).mergeable( MergeableState.CONFLICTING).merged(False).label( builder.label().name(github_logic.AutomergeLabel. AFTER_TESTS.value))) self.assertFalse( github_logic._is_pull_request_ready_for_automerge(pull_request))
def test_is_pull_request_ready_for_automerge_autofail_if_merged(self): pull_request = build(builder.pull_request().commit( builder.commit().status(Commit.BUILD_SUCCESSFUL)).review( builder.review().submitted_at("2020-01-13T14:59:58Z").state( ReviewState.APPROVED)).mergeable( MergeableState.MERGEABLE).merged(True).closed( False).label(builder.label().name( github_logic.AutomergeLabel. AFTER_TESTS_AND_APPROVAL.value))) self.assertFalse( github_logic._is_pull_request_ready_for_automerge(pull_request))
def test_pull_request_approved_after_merging_review_that_had_approval( self): merged_at = datetime.now() reviewed_at = merged_at + timedelta(days=1) commented_at = merged_at + timedelta(days=2) pull_request = build(builder.pull_request().merged_at( datetime.now()).reviews([ builder.review("This looks OK").submitted_at(reviewed_at) ]).comments([builder.comment("SGTM!").published_at(commented_at)])) self.assertTrue( github_logic.pull_request_approved_after_merging(pull_request))
def test_pull_request_approved_before_merging_review_requested_changes_before_merge( self, ): merged_at = datetime.now() submitted_at = merged_at - timedelta(hours=1) pull_request = build(builder.pull_request().reviews([ builder.review().submitted_at(submitted_at).state( ReviewState.CHANGES_REQUESTED) ]).merged_at(merged_at)) self.assertFalse( github_logic.pull_request_approved_before_merging(pull_request))
def test_pull_request_approved_before_merging_review_approved_before_merge( self): merged_at = datetime.now() submitted_at = merged_at - timedelta(hours=1) pull_request = build(builder.pull_request().reviews([ builder.review().submitted_at(submitted_at).state( ReviewState.APPROVED) ]).merged_at(merged_at)) self.assertTrue( github_logic.pull_request_approved_before_merging(pull_request))
def test_handles_non_asana_review_comment_author_gracefully(self): github_review = build(builder.review().author( builder.user("github_unknown_user_login", "GITHUB_UNKNOWN_USER_NAME")).state( ReviewState.DEFAULT)) asana_review_comment = src.asana.helpers.asana_comment_from_github_review( github_review) self.assertContainsStrings( asana_review_comment, ["github_unknown_user_login", "GITHUB_UNKNOWN_USER_NAME"], )
def test_is_pull_request_ready_for_automerge_after_approval(self): pull_request = build( builder.pull_request().commit( builder.commit().status(Commit.BUILD_PENDING) ) # build hasn't finished, but PR is approved .review(builder.review().submitted_at( "2020-01-13T14:59:58Z").state(ReviewState.APPROVED)).mergeable( MergeableState.MERGEABLE).merged(False).label( builder.label().name( github_logic.AutomergeLabel.AFTER_APPROVAL.value))) self.assertTrue( github_logic._is_pull_request_ready_for_automerge(pull_request))
def test_does_not_add_warning_comment_if_pr_is_approved( self, add_pr_comment_mock, edit_pr_title_mock): pull_request = build(builder.pull_request().title( self.SAMPLE_PR_TITLE).label(builder.label().name( github_logic.AutomergeLabel.AFTER_TESTS_AND_APPROVAL.value )).review( builder.review().submitted_at("2020-01-13T14:59:58Z").state( ReviewState.APPROVED))) github_logic.maybe_add_automerge_warning_comment(pull_request) add_pr_comment_mock.assert_not_called()
def test_completed_is_true_if_pr_was_merged_and_changes_requested_but_still_said_lgtm_in_review_after_merge( self, ): # this is a bit of a nuanced case. Here the reviewer has requested changes, but still said LGTM!, so we # interpret this as the reviewer trusting the author to make the changes requested without having to come # back to the reviewer and review that the changes were made pull_request = build(builder.pull_request().closed(True).merged( True).merged_at("2020-01-13T14:59:59Z").reviews([ builder.review().submitted_at("2020-02-13T14:59:57Z").state( ReviewState.CHANGES_REQUESTED).body("LGTM!") ])) task_fields = src.asana.helpers.extract_task_fields_from_pull_request( pull_request) self.assertEqual(True, task_fields["completed"])
def test_completed_is_false_if_pr_was_merged_and_changes_requested_and_commented_lgtm_on_the_pr_before_merge( self, ): pull_request = build(builder.pull_request().closed(True).merged( True).merged_at("2020-01-13T14:59:59Z").reviews([ builder.review().submitted_at("2020-01-13T14:59:57Z").state( ReviewState.CHANGES_REQUESTED) ]).comments([ builder.comment().published_at("2020-01-13T14:59:58Z").body( "LGTM!") ])) task_fields = src.asana.helpers.extract_task_fields_from_pull_request( pull_request) self.assertEqual(False, task_fields["completed"])
def test_considers_double_quotes_safe_in_review_text(self): placeholder = "💣" github_placeholder_review = build(builder.review().author( builder.user("github_unknown_user_login")).state( ReviewState.DEFAULT).body(placeholder).comment( builder.comment().body(placeholder))) asana_placeholder_review = src.asana.helpers.asana_comment_from_github_review( github_placeholder_review) safe_characters = ['"', "'"] for safe_character in safe_characters: github_review = build(builder.review().author( builder.user("github_unknown_user_login")).state( ReviewState.DEFAULT).body(safe_character).comment( builder.comment().body(safe_character))) asana_review_comment = src.asana.helpers.asana_comment_from_github_review( github_review) expected = asana_placeholder_review.replace( placeholder, safe_character) self.assertEqual( asana_review_comment, expected, f"Did not expected the {safe_character} character to be escaped", )
def test_converts_urls_to_links(self): github_review = build(builder.review().author( builder.user("github_unknown_user_login")).state( ReviewState.DEFAULT).body("https://www.asana.com").comment( builder.comment().body("http://www.foo.com"))) asana_review_comment = src.asana.helpers.asana_comment_from_github_review( github_review) self.assertContainsStrings( asana_review_comment, [ '<a href="{}">{}</a>'.format(url, url) for url in ["https://www.asana.com", "http://www.foo.com"] ], )
def test_does_not_inject_unsafe_html(self): placeholder = "💣" github_placeholder_review = build(builder.review().author( builder.user("github_unknown_user_login")).state( ReviewState.DEFAULT).body(placeholder).comment( builder.comment().body(placeholder))) asana_placeholder_review = src.asana.helpers.asana_comment_from_github_review( github_placeholder_review) unsafe_characters = ["&", "<", ">"] for unsafe_character in unsafe_characters: github_review = build(builder.review().author( builder.user("github_unknown_user_login")).state( ReviewState.DEFAULT).body(unsafe_character).comment( builder.comment().body(unsafe_character))) asana_review_comment_comment = src.asana.helpers.asana_comment_from_github_review( github_review) unexpected = asana_placeholder_review.replace( placeholder, unsafe_character) self.assertNotEqual( asana_review_comment_comment, unexpected, f"Expected the {unsafe_character} character to be escaped", )
def test_does_not_rely_on_github_to_return_reviews_sorted_by_submitted_at_timestamp( self, ): # this would be a plausible state during a race condition, as two simultaneously submitted reviews could be # returned by github in the order they were inserted in a database, yet have slightly out-of-order timestamps pull_request = build(builder.pull_request().closed(True).merged( True).merged_at("2020-01-13T14:59:59Z").reviews([ (builder.review().submitted_at("2020-01-13T14:59:58Z").state( ReviewState.CHANGES_REQUESTED)), (builder.review().submitted_at("2020-01-13T14:59:57Z").state( ReviewState.APPROVED)), ])) task_fields = src.asana.helpers.extract_task_fields_from_pull_request( pull_request) self.assertEqual(False, task_fields["completed"]) pull_request = build(builder.pull_request().closed(True).merged( True).merged_at("2020-01-13T14:59:59Z").reviews([ (builder.review().submitted_at("2020-01-13T14:59:58Z").state( ReviewState.APPROVED)), (builder.review().submitted_at("2020-01-13T14:59:57Z").state( ReviewState.CHANGES_REQUESTED)), ])) task_fields = src.asana.helpers.extract_task_fields_from_pull_request( pull_request) self.assertEqual(True, task_fields["completed"])