Repository navigation
Validate bpo issue number - #159
Conversation
Codecov Report
@@ Coverage Diff @@
## master #159 +/- ##
=====================================
Coverage 100% 100%
=====================================
Files 20 20
Lines 1577 1650 +73
Branches 87 88 +1
=====================================
+ Hits 1577 1650 +73
Continue to review full report at Codecov.
|
|
Just an FYI I'm not comfortable accepting this with the use of |
|
Sorry @brettcannon, hadn't thought of that. I updated my PR to use |
| target_url=url) | ||
|
|
||
|
|
||
| def create_failure_status(issue_not_found_on_bpo, found_issue=None): |
There was a problem hiding this comment.
There's no need to overload creating a failure case for the missing issue number in the PR versus the issue not existing. Just make them separate functions.
| url = f"https://bugs.python.org/issue{issue_number}" | ||
| async with aiohttp.ClientSession() as session: | ||
| async with session.head(url) as res: | ||
| return False if res.status == 404 else True |
There was a problem hiding this comment.
| return False if res.status == 404 else True | |
| return res.status != 404 |
There was a problem hiding this comment.
Haha, that's kind of embarrassing...
|
|
||
| @pytest.mark.asyncio | ||
| @pytest.mark.parametrize("action", ["opened", "synchronize", "reopened"]) | ||
| async def test_set_status_failure_via_issue_non_found_on_bpo(action): |
There was a problem hiding this comment.
| async def test_set_status_failure_via_issue_non_found_on_bpo(action): | |
| async def test_set_status_failure_via_issue_not_found_on_bpo(action): |
4091315 to
f5fb352
Compare
|
Thanks for the review @brettcannon, I have updated my PR. |
|
@brettcannon Thanks again for the review. I have now pushed one more commit which adds the necessary docstrings, so the PR might be ready to be merged. |
|
I don't quite have time to review again, but I did notice your docstrings weren't complete sentences (i.e. missing periods). We follow PEP 8 as a style so please update the docstrings (I know this can seem very nit-picky, but it helps readers know you actually meant for the sentence to end and didn't accidentally leave something out). |
0d9c31f to
0bd9b32
Compare
|
I added the missing periods, @brettcannon. Please let me know in case there is any way I can help out in making the PR easier to review. |
|
@lysnikolaou unless you can magically find me more time, it's just a matter of work easing up a bit (I'm hoping I will get to this either this week or next). |
brettcannon
left a comment
There was a problem hiding this comment.
Some minor structural things to help keep the code a little easier to maintain.
| issue_number = issue_number_found.group("issue") | ||
| new_body = BODY.format(body=body, issue_number=issue_number) | ||
| body_data = {"body": new_body, "maintainer_can_modify": True} | ||
| await gh.patch(event.data["pull_request"]["url"], data=body_data) |
There was a problem hiding this comment.
The patching of the first message seems to occur regardless of whether the issue # was validated. Do you think it would be better to not create a URL if we can check that it won't return a 404?
There was a problem hiding this comment.
Yeah, that sounds like a good improvement.
| status = create_success_status(issue_number_found) | ||
| issue_number_on_bpo = await _validate_issue_number(issue_number) | ||
| if issue_number_on_bpo: | ||
| status = create_success_status(issue_number_found) |
There was a problem hiding this comment.
Any reason to not pass in issue_number since you already have that pulled out?
There was a problem hiding this comment.
When I created the two functions create_failure_status_issue_not_on_bpo and create_failure_status_no_issue, I wanted them to be consistent with how create_success_status was already doing things. That's why I passed issue_number_found as the parameter and not issue_number. I agree that it would be better to pass issue_number though, since that is already there.
|
|
||
| def create_failure_status_issue_not_on_bpo(issue_number_found): | ||
| """Create a failure status for when an issue does not exist on the bug tracker.""" | ||
| issue_number = issue_number_found.group("issue") |
There was a problem hiding this comment.
What is the benefit of not passing in the issue number directly instead of the re.Match object?
|
@brettcannon we should maybe also consider that validating issues in the tests has really slowed down |
2. Refactor, so that the first message does not get patched even if the issue is not on bpo
|
@lysnikolaou Stubbing/mocking/patching out the b.p.o calls for all tests but the one(s) that are specifically testing this functionality would be good. When you're ready for me to re-review, let me know (I think you can click on my review status to ask for a new review now in GitHub). |
|
@brettcannon Sorry for my delayed response, I had no time at all in the few last days. I am not sure, how I could do that, since there is no direct calling of the functions from the tests. Should we maybe add another field to the |
|
@lysnikolaou you can use https://docs.pytest.org/en/latest/monkeypatch.html. And don't worry about the test mocking out functions that are not publicly exposed as we're fine with glass box testing. |
|
@brettcannon I have patched out the bpo calls from all the tests that would make the call and do not test this specifically. I am not sure, if that's the best way to do it, so your review is needed once more. |
brettcannon
left a comment
There was a problem hiding this comment.
I think a tweak to your mocking approach might be needed as I think you're avoiding the costly function a bit too aggressively 😉
| issue_number = issue_number_found.group("issue") | ||
| try: | ||
| issue_number_on_bpo = await _validate_issue_number(issue_number) | ||
| except NameError: |
There was a problem hiding this comment.
What is triggering a NameError? That shouldn't be occurring and so that makes this try block suspicious. If it's because of how aiohttp functions then it should be contained within _validate_issue_number() so it effectively can't fail unless there's a critical exception that should bubble up to get logged.
| body = event.data["pull_request"]["body"] or "" | ||
| if not body or CLOSING_TAG not in body: | ||
| new_body = BODY.format(body=body, issue_number=issue_number) | ||
| body_data = {"body": new_body, "maintainer_can_modify": True} |
There was a problem hiding this comment.
Is "maintainer_can_modify" required for this to work, or are you just sneaking that in here? If it's the latter then I would pull it out as it's easy to lose track of. Better to open an issue and do a separate check for it as this isn't related to issue numbers from bugs.python.org which is what this module is aimed at.
| @pytest.mark.asyncio | ||
| @pytest.mark.parametrize("action", ["opened", "synchronize", "reopened"]) | ||
| async def test_set_status_success(action, monkeypatch): | ||
| monkeypatch.delattr('bedevere.bpo._validate_issue_number') |
There was a problem hiding this comment.
The problem with the deletion of the function is now you don't know if the function was called with the appropriate issue number. I would set up a mock and verify that it was called with the expected issue number. That way you can also return false as expected to make sure everything is calling that function the way you expect it to.
| async def _validate_issue_number(issue_number): | ||
| """Make sure the issue number exists on bugs.python.org.""" | ||
| url = f"https://bugs.python.org/issue{issue_number}" | ||
| async with aiohttp.ClientSession() as session: |
There was a problem hiding this comment.
Instead of creating a new session for aiohttp, you could pass it in through the __main__ method where dispatch occurs to try and maximize connection pooling. All routes should be acceptings *args, **kwargs so passing in a session keyword argument should work fine.
|
Hey @brettcannon, sorry for the delay again. Life gets in the way. For the last few days, I've been trying to implement all the proposed changes and I have an implementation that uses |
|
It isn't like I'm very fast on my reviews so no need to apologize on taking your time. 😄 As for mocking, I have no specific guidance beyond you can use the hand-coded mocks as necessary, but otherwise I have no opinion on what 3rd-party packages to use. |
|
@brettcannon I used Moreover, sending a kwarg in |
|
@lysnikolaou I think the mocking problem is that you are using a fixture to blindly assign the mock, which means you never actually put the object you mocked out back. I would drop your custom fixture and instead use http://doc.pytest.org/en/latest/monkeypatch.html and set a mock as needed in each test. That way As for your |
0e021e6 to
4718ee6
Compare
4718ee6 to
2ebfa34
Compare
|
@brettcannon Both monkeypatching and the passing of session from |
brettcannon
left a comment
There was a problem hiding this comment.
This is looking great and I think it's really close to being ready to merge. Just a few minor things to tweak.
| await gh.patch(event.data["pull_request"]["url"], data=body_data) | ||
| status = create_success_status(issue_number_found) | ||
| issue_number = issue_number_found.group("issue") | ||
| session = kwargs.get('session', '') |
There was a problem hiding this comment.
Instead of doing this I would add session as keyword argument to this function with a default value of None.
| def create_failure_status_issue_not_on_bpo(issue_number): | ||
| """Create a failure status for when an issue does not exist on the bug tracker.""" | ||
| description = f"Issue #{issue_number} not found on bugs.python.org" | ||
| url = "https://devguide.python.org/pullrequest/#submitting" |
There was a problem hiding this comment.
What do you think of changing this to be the issue URL to show that it's a 404? If they specified an issue number they know that's what they should be doing so I don't think linking back to the docs is going to be the most helpful option for us.
There was a problem hiding this comment.
I totally agree. I will change the url to link to the (non-existing) issue.
| } | ||
| event = sansio.Event(data, event="pull_request", delivery_id="12345") | ||
| gh = FakeGH() | ||
| session = aiohttp.ClientSession() |
There was a problem hiding this comment.
Any reason not to use the context manager to make sure it closes?
| assert body_data["body"].count("[bpo-123](https://bugs.python.org/issue123)") == 2 | ||
| assert body_data["body"].count("[something about bpo-123](https://bugs.python.org/issue123)") == 1 | ||
| assert "123456" in gh.patch_url | ||
| if action == 'opened': |
There was a problem hiding this comment.
If this is necessary then it might be better to have a helper function for the tests which does everything that's the same and then break this into two separate tests that do the appropriate mock check that's unique to each.
2. Change failure url to point to bpo
dd7d736 to
772f385
Compare
brettcannon
left a comment
There was a problem hiding this comment.
One last minor change to make the tests slightly easier to read.
| assert "1234" in status["description"] | ||
| assert status["context"] == "bedevere/issue-number" | ||
| assert "git-sha" in gh.url | ||
| bpo._validate_issue_number.assert_awaited_with("1234", None) |
There was a problem hiding this comment.
| bpo._validate_issue_number.assert_awaited_with("1234", None) | |
| bpo._validate_issue_number.assert_awaited_with("1234", session=None) |
| gh = FakeGH() | ||
| await bpo.router.dispatch(event, gh) | ||
| assert gh.data is not None | ||
| bpo._validate_issue_number.assert_awaited_with("1234", None) |
There was a problem hiding this comment.
| bpo._validate_issue_number.assert_awaited_with("1234", None) | |
| bpo._validate_issue_number.assert_awaited_with("1234", session=None) |
| await bpo.router.dispatch(event, gh) | ||
| assert gh.patch_data is not None | ||
| assert gh.patch_data["body"] == "\n\n<!-- issue-number: bpo-32636 -->\nhttps://bugs.python.org/issue32636\n<!-- /issue-number -->\n" | ||
| bpo._validate_issue_number.assert_awaited_with("32636", None) |
There was a problem hiding this comment.
| bpo._validate_issue_number.assert_awaited_with("32636", None) | |
| bpo._validate_issue_number.assert_awaited_with("32636", session=None) |
| assert "1234" in status["description"] | ||
| assert status["context"] == "bedevere/issue-number" | ||
| assert "git-sha" in gh.url | ||
| bpo._validate_issue_number.assert_awaited_with("1234", None) |
There was a problem hiding this comment.
| bpo._validate_issue_number.assert_awaited_with("1234", None) | |
| bpo._validate_issue_number.assert_awaited_with("1234", session=None) |
| status = gh.patch_data | ||
| assert "https://bugs.python.org/issue1234" in status["body"] | ||
| assert "1347" in gh.patch_url | ||
| bpo._validate_issue_number.assert_awaited_with("1234", None) |
There was a problem hiding this comment.
| bpo._validate_issue_number.assert_awaited_with("1234", None) | |
| bpo._validate_issue_number.assert_awaited_with("1234", session=None) |
| await bpo.router.dispatch(event, gh) | ||
| assert gh.patch_data is None | ||
| assert gh.patch_url is None | ||
| bpo._validate_issue_number.assert_awaited_with("1234", None) |
There was a problem hiding this comment.
| bpo._validate_issue_number.assert_awaited_with("1234", None) | |
| bpo._validate_issue_number.assert_awaited_with("1234", session=None) |
| async def test_set_pull_request_body_success(action): | ||
| async def test_set_pull_request_body_success_opened(monkeypatch): | ||
| await set_pull_request_body_success_helper("opened", monkeypatch) | ||
| bpo._validate_issue_number.assert_awaited_with("12345", None) |
There was a problem hiding this comment.
| bpo._validate_issue_number.assert_awaited_with("12345", None) | |
| bpo._validate_issue_number.assert_awaited_with("12345", session=None) |
| await bpo.router.dispatch(event, gh) | ||
| if gh.patch_data: | ||
| assert "[bpo-123](https://bugs.python.org/issue123)" not in gh.patch_data | ||
| bpo._validate_issue_number.assert_awaited_with("12345", None) |
There was a problem hiding this comment.
| bpo._validate_issue_number.assert_awaited_with("12345", None) | |
| bpo._validate_issue_number.assert_awaited_with("12345", session=None) |
| "opened", | ||
| monkeypatch | ||
| ) | ||
| bpo._validate_issue_number.assert_awaited_with("12345", None) |
There was a problem hiding this comment.
| bpo._validate_issue_number.assert_awaited_with("12345", None) | |
| bpo._validate_issue_number.assert_awaited_with("12345", session=None) |
brettcannon
left a comment
There was a problem hiding this comment.
I fixed my own complaints. 😁
|
@brettcannon Thanks for all the reviews and having such great patience. |
Because #72 was closed I thought I could move the work of Lev forward. Taking some of his work and following all the suggestions in the initial PR, I have formulated a new one.
I used the
requestslibrary, but did not updated therequirements.txtfile, because I'm not sure that the correct way to go.urllibmight be a better alternative, because it's in the stdlib.Relevant issue: #15
Co-authored-by: Lev Lazinskiy
Update: I updated
requirements.txtso that the tests can pass on Travis. In case we use some other library, it can always be updated again.