Skip to content

Conversation

@BenMusch
Copy link
Contributor

  • Have you followed the guidelines in our Contributing document?
  • Have you checked to ensure there aren't other open Pull Requests for the same change?
  • Have you added an explanation of what your changes do and why you'd like us to include them?
  • Have you written new tests for your changes? Here's an example.
  • Have you successfully run brew tests with your changes locally?

This PR cleans up some of the code in the GitHub module. The goals are to reduce duplication around things like building URLs, and to prevent external code from having to know about the "key:value" format of github qualifiers

@BenMusch
Copy link
Contributor Author

BenMusch commented Aug 13, 2017

Two questions:

  1. I'd love to add more tests around things like searching issues, but it seems like the method for testing network interaction is to actually make the request. Issues are a bit unstable so I can't test an issue with confidence that it won't be closed, deleted, edited, etc. Is there a strong opinion of how to mock out network interactions for that situation?

  2. Is there a tl;dr on how to set up the tests locally? brew tests runs them all, but I'd like to be able to run bundle exec rspec ... but I keep getting errors when I do that (e.g. could not locate file utils/github

@reitermarkus
Copy link
Member

Nice work here!

Is there a strong opinion of how to mock out network interactions for that situation?

I think mocking the JSON response would be enough.

@reitermarkus
Copy link
Member

but I'd like to be able to run bundle exec rspec ...

If you want to run single tests, you can specify e. g. --only=utils/github, or add :focus to the describe or it block, which will run it before all other tests.

Copy link
Member

@MikeMcQuaid MikeMcQuaid left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work here, code is much cleaner 👏

valid_dirnames = ["Formula", "HomebrewFormula", "Casks", "."].freeze
matches = GitHub.search_code("user:Homebrew", "user:caskroom", "filename:#{query}", "extension:rb")
[*matches].map do |match|
matches = GitHub.search_code(user: ["Homebrew", "caskroom"], filename: query, extension: "rb")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps should be users if it's taking an array?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That'd be kinda difficult to pull off, since #query_string would have to know how to singularize a word. I could just make it remove an s at the end of the word, or hard-code the users case, but that would make the method too specific in my opinion

[*matches].map do |match|
matches = GitHub.search_code(user: "caskroom", path: "Casks",
filename: query, extension: "rb")
Array(matches).map do |match|
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it worth instead ensuring search_code always returns an array?


describe "::query_string" do
it "builds a query with the given hash parameters formatted as key:value" do
query = subject.query_string(user: "Homebrew", repo: "Brew")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lowercase brew here

end

def url_to(*subroutes)
URI.parse(File.join(API_URL, *subroutes))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feels a little weird using File.join in this way. I'd just use String#join

Copy link
Member

@reitermarkus reitermarkus Aug 14, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

URI::join

Edit: Only works with one argument. Maybe URL.join(API_URL, subroutes.join("/"))?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed that this is kinda weird -- I did it so that url_to '/repos', '/Homebrew' would work, but I'm also fine if the weird-ness outweighs that benefit

@MikeMcQuaid
Copy link
Member

I'd love to add more tests around things like searching issues, but it seems like the method for testing network interaction is to actually make the request. Issues are a bit unstable so I can't test an issue with confidence that it won't be closed, deleted, edited, etc. Is there a strong opinion of how to mock out network interactions for that situation?

In general it would be good to have at least one integration tests here that actually tests hitting the GitHub API. Use an old Homebrew/brew issue and we can lock it if necessary. I wouldn't worry about it changing/breaking: if it does, we can fix it then.

Other tests beyond the one integration test: yeh, as @reitermarkus said I think just stubbing the JSON response is a good call.

Is there a tl;dr on how to set up the tests locally? brew tests runs them all, but I'd like to be able to run bundle exec rspec ... but I keep getting errors when I do that (e.g. could not locate file utils/github

Let us know where you expected this information to live. I'd love to make that more obviously documented.

@BenMusch
Copy link
Contributor Author

BenMusch commented Aug 14, 2017

Let us know where you expected this information to live. I'd love to make that more obviously documented.

I was expecting something to be in a file like docs/Developing.md or something similar

Edit: Also, that document could contain info about how to run brew from the source repo, took me a minute or two to realize that was why the tests were running without my changes

end
end

def issues_matching(query, qualifiers = {})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

qualifiers = {} -> **qualifiers, while we're at it.

prs = open_or_closed_prs
else
return
return []
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the return, and conditionally assign it to prs instead.

search("code", **params)
end

def uri_escape(query)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can also remove the check for URI.respond_to?(:encode_www_form_component) here, since we're always using Ruby >= 2.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe just replace uri_escape with URI.encode_www_form_component, since we're only using it once.

params_list = main_params

qualifiers.each do |key, value|
if value.is_a? Array
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of these separate checks, just use [*value], and loop through all values, even if it is only one.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

flat_map here, and append to params_list instead.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inline this again, since it is only used once.

Copy link
Member

@reitermarkus reitermarkus Aug 14, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move prs = out of the if.

prs = if 
  
else
  
end

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inline this, since it is only used once.

@BenMusch
Copy link
Contributor Author

This PR is all set from my perspective, can I get one last review?

describe "::search_issues", :needs_network do
it "queries GitHub issues with the passed parameters" do
results = subject.search_issues("brew search", repo: "Homebrew/brew", author: "avetamine", is: "closed")
expect(results.count).to eq(1)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will break if that user creates a new issue which will eventually be closed. Not sure if the line below should use #first or #last then.

results = subject.search_issues("brew search", repo: "Homebrew/brew", author: "avetamine", is: "closed")
expect(results.count).to eq(1)
expect(results.first["title"]).to eq("brew search : 422 Unprocessable Entity")
expect(results.count).to be > 1
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

expect(results).not_to be_empty

end

def query_string(*main_params, **qualifiers)
params_list = main_params
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe just call this params.

Copy link
Member

@reitermarkus reitermarkus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One last comment, other than that 👍!

@reitermarkus reitermarkus dismissed MikeMcQuaid’s stale review August 18, 2017 14:23

Everything done, except pluralising named arguments (which is unnecessary).

@reitermarkus reitermarkus merged commit 3b92f69 into Homebrew:master Aug 18, 2017
@reitermarkus
Copy link
Member

Thanks for the great cleanup, @BenMusch!

@BenMusch BenMusch deleted the github-refactor branch August 18, 2017 14:29
@BenMusch
Copy link
Contributor Author

No problem, thanks for the thorough reviews!

@Homebrew Homebrew locked and limited conversation to collaborators May 4, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants