async def test_is_core_dev(): teams = [{"name": "not Python core"}] gh = FakeGH(getiter={"https://api.github.com/orgs/python/teams": teams}) with pytest.raises(ValueError): await util.is_core_dev(gh, "brett") teams = [{"name": "Python core", "id": 42}] getitem = {"https://api.github.com/teams/42/memberships/brett": True} gh = FakeGH(getiter={"https://api.github.com/orgs/python/teams": teams}, getitem=getitem) assert await util.is_core_dev(gh, "brett") assert gh.getiter_url == "https://api.github.com/orgs/python/teams" teams = [{"name": "Python core", "id": 42}] getitem = { "https://api.github.com/teams/42/memberships/andrea": gidgethub.BadRequest(status_code=http.HTTPStatus(404)) } gh = FakeGH(getiter={"https://api.github.com/orgs/python/teams": teams}, getitem=getitem) assert not await util.is_core_dev(gh, "andrea") teams = [{"name": "Python core", "id": 42}] getitem = { "https://api.github.com/teams/42/memberships/andrea": gidgethub.BadRequest(status_code=http.HTTPStatus(400)) } gh = FakeGH(getiter={"https://api.github.com/orgs/python/teams": teams}, getitem=getitem) with pytest.raises(gidgethub.BadRequest): await util.is_core_dev(gh, "andrea")
async def test_is_core_dev(): teams = [{"name": "not Python core"}] gh = FakeGH(getiter={"/orgs/python/teams": teams}) with pytest.raises(ValueError): await util.is_core_dev(gh, "mariatta") teams = [{"name": "python core", "id": 42}] getitem = {"/teams/42/memberships/mariatta": True} gh = FakeGH(getiter={"/orgs/python/teams": teams}, getitem=getitem) assert await util.is_core_dev(gh, "mariatta") assert gh.getiter_url == "/orgs/python/teams" teams = [{"name": "python core", "id": 42}] getitem = { "/teams/42/memberships/miss-islington": gidgethub.BadRequest(status_code=http.HTTPStatus(404)) } gh = FakeGH(getiter={"/orgs/python/teams": teams}, getitem=getitem) assert not await util.is_core_dev(gh, "miss-islington") teams = [{"name": "python core", "id": 42}] getitem = { "/teams/42/memberships/miss-islington": gidgethub.BadRequest(status_code=http.HTTPStatus(400)) } gh = FakeGH(getiter={"/orgs/python/teams": teams}, getitem=getitem) with pytest.raises(gidgethub.BadRequest): await util.is_core_dev(gh, "miss-islington")
async def test_comment_on_pr_failure(): issue_number = 100 message = "Thanks for the PR!" gh = FakeGH(post=gidgethub.BadRequest(status_code=http.HTTPStatus(400))) with pytest.raises(gidgethub.BadRequest): await util.comment_on_pr(gh, issue_number, message)
async def test_opened_pr(): # New PR from a core dev. username = "******" issue_url = "https://api.github.com/issue/42" data = { "action": "opened", "pull_request": { "user": { "login": username, }, "issue_url": issue_url, }, } event = sansio.Event(data, event="pull_request", delivery_id="12345") teams = [{"name": "python core", "id": 6}] items = { f"https://api.github.com/teams/6/memberships/{username}": "OK", issue_url: { "labels": [], "labels_url": "https://api.github.com/labels" }, } gh = FakeGH(getiter={"https://api.github.com/orgs/python/teams": teams}, getitem=items) await awaiting.router.dispatch(event, gh) assert len(gh.post_) == 1 post_ = gh.post_[0] assert post_[0] == "https://api.github.com/labels" assert post_[1] == [awaiting.Blocker.core_review.value] # New PR from a non-core dev. username = "******" issue_url = "https://api.github.com/issue/42" data = { "action": "opened", "pull_request": { "user": { "login": username, }, "issue_url": issue_url, }, } event = sansio.Event(data, event="pull_request", delivery_id="12345") teams = [{"name": "python core", "id": 6}] items = { f"https://api.github.com/teams/6/memberships/{username}": gidgethub.BadRequest(status_code=http.HTTPStatus(404)), issue_url: { "labels": [], "labels_url": "https://api.github.com/labels" }, } gh = FakeGH(getiter={"https://api.github.com/orgs/python/teams": teams}, getitem=items) await awaiting.router.dispatch(event, gh) assert len(gh.post_) == 1 post_ = gh.post_[0] assert post_[0] == "https://api.github.com/labels" assert post_[1] == [awaiting.Blocker.review.value]
async def test_change_requested_for_non_core_dev(): data = { "action": "submitted", "review": { "user": { "login": "******", }, "state": "changes_requested".upper(), }, "pull_request": { "url": "https://api.github.com/pr/42", "issue_url": "https://api.github.com/issue/42", "comments_url": "https://api.github.com/comment/42", "user": { "login": "******" }, }, } event = sansio.Event(data, event="pull_request_review", delivery_id="12345") teams = [{"name": "python core", "id": 6}] items = { f"https://api.github.com/teams/6/memberships/gvanrossum": True, "https://api.github.com/teams/6/memberships/miss-islington": gidgethub.BadRequest(status_code=http.HTTPStatus(404)), "https://api.github.com/issue/42": { "labels": [], "labels_url": "https://api.github.com/labels/42", }, } iterators = { "https://api.github.com/orgs/python/teams": teams, "https://api.github.com/pr/42/reviews": [{ "user": { "login": "******" }, "state": "changes_requested" }], } gh = FakeGH(getiter=iterators, getitem=items) await awaiting.router.dispatch(event, gh) assert len(gh.post_) == 2 labeling = gh.post_[0] assert labeling[0] == "https://api.github.com/labels/42" assert labeling[1] == [awaiting.Blocker.changes.value] message = gh.post_[1] assert message[0] == "https://api.github.com/comment/42" change_requested_message = awaiting.CHANGES_REQUESTED_MESSAGE.replace( "{easter_egg}", "").strip() assert change_requested_message in message[1]["body"]
async def test_new_comment(): # Comment not from PR author. data = { "action": "created", "issue": {"user": {"login": "******"}}, "comment": { "user": {"login": "******"}, "body": awaiting.BORING_TRIGGER_PHRASE, }, } event = sansio.Event(data, event="issue_comment", delivery_id="12345") gh = FakeGH() await awaiting.router.dispatch(event, gh) assert not len(gh.post_) # Comment from PR author but missing trigger phrase. data = { "action": "created", "issue": {"user": {"login": "******"}}, "comment": { "user": {"login": "******"}, "body": "I DID expect the Spanish Inquisition", }, } event = sansio.Event(data, event="issue_comment", delivery_id="12345") gh = FakeGH() await awaiting.router.dispatch(event, gh) assert not len(gh.post_) # Everything is right with the world. data = { "action": "created", "issue": { "user": {"login": "******"}, "labels": [], "labels_url": "https://api.github.com/labels/42", "url": "https://api.github.com/issue/42", "pull_request": {"url": "https://api.github.com/pr/42"}, "comments_url": "https://api.github.com/comments/42", }, "comment": { "user": {"login": "******"}, "body": awaiting.BORING_TRIGGER_PHRASE, }, } event = sansio.Event(data, event="issue_comment", delivery_id="12345") items = { "https://api.github.com/teams/6/memberships/brettcannon": True, "https://api.github.com/teams/6/memberships/gvanrossum": True, "https://api.github.com/teams/6/memberships/not-core-dev": gidgethub.BadRequest(status_code=http.HTTPStatus(404)), } iterators = { "https://api.github.com/orgs/python/teams": [{"name": "python core", "id": 6}], "https://api.github.com/pr/42/reviews": [ {"user": {"login": "******"}, "state": "approved"}, {"user": {"login": "******"}, "state": "changes_requested"}, {"user": {"login": "******"}, "state": "approved"}, ], } gh = FakeGH(getitem=items, getiter=iterators) await awaiting.router.dispatch(event, gh) assert len(gh.post_) == 2 labeling, comment = gh.post_ assert labeling[0] == "https://api.github.com/labels/42" assert labeling[1] == [awaiting.Blocker.change_review.value] assert comment[0] == "https://api.github.com/comments/42" comment_body = comment[1]["body"] assert "@brettcannon" in comment_body assert "@gvanrossum" in comment_body assert "not-core-dev" not in comment_body # All is right with the Monty Python world. data = { "action": "created", "issue": { "user": {"login": "******"}, "labels": [], "labels_url": "https://api.github.com/labels/42", "url": "https://api.github.com/issue/42", "pull_request": {"url": "https://api.github.com/pr/42"}, "comments_url": "https://api.github.com/comments/42", }, "comment": { "user": {"login": "******"}, "body": awaiting.FUN_TRIGGER_PHRASE, }, } event = sansio.Event(data, event="issue_comment", delivery_id="12345") gh = FakeGH(getitem=items, getiter=iterators) await awaiting.router.dispatch(event, gh) assert len(gh.post_) == 2 labeling, comment = gh.post_ assert labeling[0] == "https://api.github.com/labels/42" assert labeling[1] == [awaiting.Blocker.change_review.value] assert comment[0] == "https://api.github.com/comments/42" comment_body = comment[1]["body"] assert "@brettcannon" in comment_body assert "@gvanrossum" in comment_body assert "not-core-dev" not in comment_body
async def test_new_review(): # First non-comment review from a non-core dev. username = "******" data = { "action": "submitted", "review": { "state": "approved", "user": { "login": username, }, }, "pull_request": { "url": "https://api.github.com/pr/42", "issue_url": "https://api.github.com/issue/42", }, } event = sansio.Event(data, event="pull_request_review", delivery_id="12345") teams = [ {"name": "python core", "id": 6} ] items = { f"https://api.github.com/teams/6/memberships/{username}": gidgethub.BadRequest(status_code=http.HTTPStatus(404)), "https://api.github.com/teams/6/memberships/brettcannon": True, "https://api.github.com/issue/42": { "labels": [], "labels_url": "https://api.github.com/labels/42", } } iterators = { "https://api.github.com/orgs/python/teams": teams, "https://api.github.com/pr/42/reviews": [{"user": {"login": "******"}, "state": "commented"}], } gh = FakeGH(getiter=iterators, getitem=items) await awaiting.router.dispatch(event, gh) assert len(gh.post_) == 1 post_ = gh.post_[0] assert post_[0] == "https://api.github.com/labels/42" assert post_[1] == [awaiting.Blocker.core_review.value] # First and second review from a non-core dev. items = { f"https://api.github.com/teams/6/memberships/{username}": gidgethub.BadRequest(status_code=http.HTTPStatus(404)), "https://api.github.com/teams/6/memberships/brettcannon": True, "https://api.github.com/issue/42": { "labels": [], "labels_url": "https://api.github.com/labels/42", } } iterators = { "https://api.github.com/orgs/python/teams": teams, "https://api.github.com/pr/42/reviews": [{"user": {"login": "******"}, "state": "approved"}], } gh = FakeGH(getiter=iterators, getitem=items) await awaiting.router.dispatch(event, gh) assert not gh.post_ # First comment review from a non-core dev. data = { "action": "submitted", "review": { "state": "comment", "user": { "login": username, }, }, "pull_request": { "url": "https://api.github.com/pr/42", "issue_url": "https://api.github.com/issue/42", }, } event = sansio.Event(data, event="pull_request_review", delivery_id="12345") items = { f"https://api.github.com/teams/6/memberships/{username}": gidgethub.BadRequest(status_code=http.HTTPStatus(404)), "https://api.github.com/teams/6/memberships/brettcannon": True, "https://api.github.com/issue/42": { "labels": [], "labels_url": "https://api.github.com/labels/42", } } iterators = { "https://api.github.com/orgs/python/teams": teams, "https://api.github.com/pr/42/reviews": [{"user": {"login": "******"}, "state": "approved"}], } gh = FakeGH(getiter=iterators, getitem=items) await awaiting.router.dispatch(event, gh) assert not gh.post_ # Core dev submits an approving review. username = "******" data = { "action": "submitted", "review": { "user": { "login": username, }, "state": "APPROVED", }, "pull_request": { "url": "https://api.github.com/pr/42", "issue_url": "https://api.github.com/issue/42", }, } event = sansio.Event(data, event="pull_request_review", delivery_id="12345") teams = [ {"name": "python core", "id": 6} ] items = { f"https://api.github.com/teams/6/memberships/{username}": True, "https://api.github.com/issue/42": { "labels": [{"name": awaiting.Blocker.changes.value}], "labels_url": "https://api.github.com/labels/42", } } iterators = { "https://api.github.com/orgs/python/teams": teams, "https://api.github.com/pr/42/reviews": [], } gh = FakeGH(getiter=iterators, getitem=items) await awaiting.router.dispatch(event, gh) assert len(gh.post_) == 1 post_ = gh.post_[0] assert post_[0] == "https://api.github.com/labels/42" assert post_[1] == [awaiting.Blocker.merge.value] # Core dev requests changes. data = { "action": "submitted", "review": { "user": { "login": username, }, "state": "changes_requested".upper(), }, "pull_request": { "url": "https://api.github.com/pr/42", "issue_url": "https://api.github.com/issue/42", "comments_url": "https://api.github.com/comment/42", "user": { "login": "******" } }, } event = sansio.Event(data, event="pull_request_review", delivery_id="12345") items = { f"https://api.github.com/teams/6/memberships/{username}": True, f"https://api.github.com/teams/6/memberships/miss-islington": gidgethub.BadRequest(status_code=http.HTTPStatus(404)), "https://api.github.com/issue/42": { "labels": [], "labels_url": "https://api.github.com/labels/42", } } gh = FakeGH(getiter=iterators, getitem=items) await awaiting.router.dispatch(event, gh) assert len(gh.post_) == 2 labeling = gh.post_[0] assert labeling[0] == "https://api.github.com/labels/42" assert labeling[1] == [awaiting.Blocker.changes.value] message = gh.post_[1] assert message[0] == "https://api.github.com/comment/42" assert awaiting.BORING_TRIGGER_PHRASE in message[1]["body"] # Comment reviews do nothing. data = { "action": "submitted", "review": { "user": { "login": username, }, "state": "commented".upper(), }, "pull_request": { "url": "https://api.github.com/pr/42", "issue_url": "https://api.github.com/issue/42", "comments_url": "https://api.github.com/comment/42", }, } event = sansio.Event(data, event="pull_request_review", delivery_id="12345") gh = FakeGH(getiter=iterators, getitem=items) await awaiting.router.dispatch(event, gh) assert not len(gh.post_) # Skip commenting if "awaiting changes" is already set. data = { "action": "submitted", "review": { "user": { "login": username, }, "state": "changes_requested".upper(), }, "pull_request": { "url": "https://api.github.com/pr/42", "issue_url": "https://api.github.com/issue/42", "comments_url": "https://api.github.com/comment/42", }, } event = sansio.Event(data, event="pull_request_review", delivery_id="12345") items = { f"https://api.github.com/teams/6/memberships/{username}": True, "https://api.github.com/issue/42": { "labels": [{"name": awaiting.Blocker.changes.value}], "labels_url": "https://api.github.com/labels/42", } } gh = FakeGH(getiter=iterators, getitem=items) await awaiting.router.dispatch(event, gh) assert not len(gh.post_)
async def test_non_core_dev_does_not_downgrade(): core_dev = "brettcannon" non_core_dev = "andreamcinnes" teams = [{"name": "python core", "id": 6}] items = { f"https://api.github.com/teams/6/memberships/{non_core_dev}": gidgethub.BadRequest(status_code=http.HTTPStatus(404)), f"https://api.github.com/teams/6/memberships/{core_dev}": True, "https://api.github.com/issue/42": { "labels": [], "labels_url": "https://api.github.com/labels/42", }, } # Approval from a core dev changes the state to "Awaiting merge". data = { "action": "submitted", "review": { "state": "approved", "user": { "login": core_dev, }, }, "pull_request": { "url": "https://api.github.com/pr/42", "issue_url": "https://api.github.com/issue/42", "state": "open", }, } event = sansio.Event(data, event="pull_request_review", delivery_id="12345") iterators = { "https://api.github.com/orgs/python/teams": teams, "https://api.github.com/pr/42/reviews": [{ "user": { "login": core_dev }, "state": "approved" }], } gh = FakeGH(getiter=iterators, getitem=items) await awaiting.router.dispatch(event, gh) assert len(gh.post_) == 1 post_ = gh.post_[0] assert post_[0] == "https://api.github.com/labels/42" assert post_[1] == [awaiting.Blocker.merge.value] # Non-comment review from a non-core dev doesn't "downgrade" the PR's state. data = { "action": "submitted", "review": { "state": "approved", "user": { "login": non_core_dev, }, }, "pull_request": { "url": "https://api.github.com/pr/42", "issue_url": "https://api.github.com/issue/42", "state": "open", }, } event = sansio.Event(data, event="pull_request_review", delivery_id="12345") iterators = { "https://api.github.com/orgs/python/teams": teams, "https://api.github.com/pr/42/reviews": [ { "user": { "login": core_dev }, "state": "approved" }, { "user": { "login": non_core_dev }, "state": "approved" }, ], } gh = FakeGH(getiter=iterators, getitem=items) await awaiting.router.dispatch(event, gh) assert not gh.post_
async def delete(self, url, url_vars={}): raise gidgethub.BadRequest(http.HTTPStatus.BAD_REQUEST, "oops")
async def delete(self, url, url_vars={}): raise gidgethub.BadRequest(http.HTTPStatus.BAD_REQUEST, "Label does not exist")
async def test_ci_passed_approved_by_non_core_dev_review_pr_is_not_merged(): sha = "f2393593c99dd2d3ab8bfab6fcc5ddee540518a9" data = { "sha": sha, "commit": { "committer": { "login": "******", }, } } event = sansio.Event(data, event='status', delivery_id='1') getitem = { f'/repos/python/cpython/commits/{sha}/status': { "state": "success", "statuses": [{ "state": "success", "description": "Issue report skipped", "context": "bedevere/issue-number", }, { "state": "success", "description": "The Travis CI build passed", "target_url": "https://travis-ci.org/python/cpython/builds/340259685?utm_source=github_status&utm_medium=notification", "context": "continuous-integration/travis-ci/pr", }] }, "/repos/python/cpython/pulls/5544": { "user": { "login": "******" }, "merged_by": { "login": "******" } }, "/teams/42/memberships/Mariatta": gidgethub.BadRequest(status_code=http.HTTPStatus(404)) } getiter = { '/repos/miss-islington/cpython/git/refs/heads/': [{ "ref": f"refs/heads/backport-{sha[0:7]}-3.6", "object": { "sha": sha, } }, { "ref": "refs/heads/backport-63ae044-3.6", "object": { "sha": "67a2b0b7713e40dea7762b7d7764ae18fe967561", "type": "commit", "url": "https://api.github.com/repos/miss-islington/cpython/git/commits/67a2b0b7713e40dea7762b7d7764ae18fe967561" } }], f'/repos/python/cpython/pulls?state=open&head=miss-islington:backport-{sha[0:7]}-3.6': [ { "number": 5547, "title": "[3.6] bpo-32720: Fixed the replacement field grammar documentation. (GH-5544)", "body": "\n\n`arg_name` and `element_index` are defined as `digit`+ instead of `integer`.\n(cherry picked from commit 7a561afd2c79f63a6008843b83733911d07f0119)\n\nCo-authored-by: Mariatta <*****@*****.**>" }, ], '/repos/python/cpython/pulls/5547/reviews': [{ "user": { "login": "******" }, "state": "APPROVED" }], '/repos/python/cpython/pulls/5547/commits': [{ "sha": "f2393593c99dd2d3ab8bfab6fcc5ddee540518a9", "commit": { "message": "bpo-32720: Fixed the replacement field grammar documentation. (GH-5544)\n\n`arg_name` and `element_index` are defined as `digit`+ instead of `integer`.\n(cherry picked from commit 7a561afd2c79f63a6008843b83733911d07f0119)\n\nCo-authored-by: Mariatta <*****@*****.**>" } }], "/orgs/python/teams": [{ "name": "Python core", "id": 42 }], } gh = FakeGH(getitem=getitem, getiter=getiter) await status_change.router.dispatch(event, gh) assert not hasattr(gh, 'put_data') # is not merged