Now that I’ve built up a few shoulda helpers for testing the different authentication APIs in Redmine, I can now start testing the actual bug reported.
Creating a standard test macro for all APIs
The first thing I did was to create a standard test macro for all of the different API authentication types. Otherwise I would have to type out all three shoulda macros for each controller and action. I came up with a simple should_allow_api_authentication
macro.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# Test that a request allows the three types of API authentication # # * HTTP Basic with username and password # * HTTP Basic with an api key for the username # * Key based with the key=X parameter # # @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete) # @param [String] url the request url # @param [optional, Hash] parameters additional request parameters def self.should_allow_api_authentication(http_method, url, parameters={}) should_allow_http_basic_auth_with_username_and_password(http_method, url, parameters) should_allow_http_basic_auth_with_key(http_method, url, parameters) should_allow_key_based_auth(http_method, url, parameters) end |
Update Issues #index
Next I wanted to use this method to make sure IssuesController#index
is correctly allowing all authentication types. After reviewing the existing code, I found a subtle bug in the test.
1 2 3 4 5 6 7 8 |
context "/index.xml" do setup do get '/issues.xml' end should_respond_with :success should_respond_with_content_type 'application/xml' end |
All that the test is doing here is checking that the issues.xml
page loaded and returned an XML content type. Never mind the fact that Redmine will always return a page on that action, showing the list of issues you can see (or the ones the Anonymous user can see). So instead of “testing” the global issues list which always returns, I changed it to test the issues list for a private project which will require authentication.
1 2 3 |
context "/index.xml" do should_allow_api_authentication(:get, "/projects/private-child/issues.xml") end |
The other nice thing about using my new should_allow_api_authentication
is that it creates 23 tests.
Yes… a one line macro creates 23 separate tests that test all of the major cases and many of the minor edge cases. That’s a bit better than the 2 cases the old test had.
Update Issues #show
I also wanted to update IssuesController#show
to make sure it’s supporting the authentication (remember, the bug is about some actions not supporting all authentication types). Once again, I swapped in my should_allow_api_authentication
macro and was able to throw away a lot of custom code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
context "/issues/1.xml" do setup do get '/issues/1.xml' end should_respond_with :success should_respond_with_content_type 'application/xml' end context "/issues/1.json" do setup do get '/issues/1.json' end should_respond_with :success should_respond_with_content_type 'application/json' should 'return a valid JSON string' do assert ActiveSupport::JSON.decode(response.body) end end |
New Code:
1 2 3 4 5 6 7 |
context "/issues/6.xml" do should_allow_api_authentication(:get, "/issues/6.xml") end context "/issues/6.json" do should_allow_api_authentication(:get, "/issues/6.json") end |
Once again, Issue 1 is public so this wasn’t going through any authentication at all. Issue 6 (new code) is on a private project so this test exercises all of the authentication methods.
Other actions
I was going to continue onto the other Issue actions but I noticed that I’ll need to make my macros more generic first. For example, right now the macros are making sure the request was successful (HTTP 200) but that won’t work when creating a new issue because it returns the created response (HTTP 201). I’m going to tackle issue creation tomorrow.
The full commit of these changes is online. If you check the test_helper.rb
I also added a few tests to make sure that the response is a valid XML or JSON string.