##// END OF EJS Templates
Test cleanup....
Jean-Philippe Lang -
r8356:395fe0d777f2
parent child
Show More
@@ -1,117 +1,113
1 1 require File.expand_path('../../../test_helper', __FILE__)
2 2
3 3 class ApiTest::DisabledRestApiTest < ActionController::IntegrationTest
4 4 fixtures :projects, :trackers, :issue_statuses, :issues,
5 5 :enumerations, :users, :issue_categories,
6 6 :projects_trackers,
7 7 :roles,
8 8 :member_roles,
9 9 :members,
10 10 :enabled_modules,
11 11 :workflows
12 12
13 13 def setup
14 14 Setting.rest_api_enabled = '0'
15 15 Setting.login_required = '1'
16 16 end
17 17
18 18 def teardown
19 19 Setting.rest_api_enabled = '1'
20 20 Setting.login_required = '0'
21 21 end
22 22
23 23 # Using the NewsController because it's a simple API.
24 24 context "get /news with the API disabled" do
25 25
26 26 context "in :xml format" do
27 27 context "with a valid api token" do
28 28 setup do
29 29 @user = User.generate_with_protected!
30 30 @token = Token.generate!(:user => @user, :action => 'api')
31 31 get "/news.xml?key=#{@token.value}"
32 32 end
33 33
34 34 should_respond_with :unauthorized
35 35 should_respond_with_content_type :xml
36 36 should "not login as the user" do
37 37 assert_equal User.anonymous, User.current
38 38 end
39 39 end
40 40
41 41 context "with a valid HTTP authentication" do
42 42 setup do
43 43 @user = User.generate_with_protected!(:password => 'my_password', :password_confirmation => 'my_password')
44 @authorization = ActionController::HttpAuthentication::Basic.encode_credentials(@user.login, 'my_password')
45 get "/news.xml", nil, :authorization => @authorization
44 get "/news.xml", nil, :authorization => credentials(@user.login, 'my_password')
46 45 end
47 46
48 47 should_respond_with :unauthorized
49 48 should_respond_with_content_type :xml
50 49 should "not login as the user" do
51 50 assert_equal User.anonymous, User.current
52 51 end
53 52 end
54 53
55 54 context "with a valid HTTP authentication using the API token" do
56 55 setup do
57 56 @user = User.generate_with_protected!
58 57 @token = Token.generate!(:user => @user, :action => 'api')
59 @authorization = ActionController::HttpAuthentication::Basic.encode_credentials(@token.value, 'X')
60 get "/news.xml", nil, :authorization => @authorization
58 get "/news.xml", nil, :authorization => credentials(@token.value, 'X')
61 59 end
62 60
63 61 should_respond_with :unauthorized
64 62 should_respond_with_content_type :xml
65 63 should "not login as the user" do
66 64 assert_equal User.anonymous, User.current
67 65 end
68 66 end
69 67 end
70 68
71 69 context "in :json format" do
72 70 context "with a valid api token" do
73 71 setup do
74 72 @user = User.generate_with_protected!
75 73 @token = Token.generate!(:user => @user, :action => 'api')
76 74 get "/news.json?key=#{@token.value}"
77 75 end
78 76
79 77 should_respond_with :unauthorized
80 78 should_respond_with_content_type :json
81 79 should "not login as the user" do
82 80 assert_equal User.anonymous, User.current
83 81 end
84 82 end
85 83
86 84 context "with a valid HTTP authentication" do
87 85 setup do
88 86 @user = User.generate_with_protected!(:password => 'my_password', :password_confirmation => 'my_password')
89 @authorization = ActionController::HttpAuthentication::Basic.encode_credentials(@user.login, 'my_password')
90 get "/news.json", nil, :authorization => @authorization
87 get "/news.json", nil, :authorization => credentials(@user.login, 'my_password')
91 88 end
92 89
93 90 should_respond_with :unauthorized
94 91 should_respond_with_content_type :json
95 92 should "not login as the user" do
96 93 assert_equal User.anonymous, User.current
97 94 end
98 95 end
99 96
100 97 context "with a valid HTTP authentication using the API token" do
101 98 setup do
102 99 @user = User.generate_with_protected!
103 100 @token = Token.generate!(:user => @user, :action => 'api')
104 @authorization = ActionController::HttpAuthentication::Basic.encode_credentials(@token.value, 'DoesNotMatter')
105 get "/news.json", nil, :authorization => @authorization
101 get "/news.json", nil, :authorization => credentials(@token.value, 'DoesNotMatter')
106 102 end
107 103
108 104 should_respond_with :unauthorized
109 105 should_respond_with_content_type :json
110 106 should "not login as the user" do
111 107 assert_equal User.anonymous, User.current
112 108 end
113 109 end
114 110
115 111 end
116 112 end
117 113 end
@@ -1,458 +1,454
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 ENV["RAILS_ENV"] = "test"
19 19 require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
20 20 require 'test_help'
21 21 require Rails.root.join('test', 'mocks', 'open_id_authentication_mock.rb').to_s
22 22
23 23 require File.expand_path(File.dirname(__FILE__) + '/object_daddy_helpers')
24 24 include ObjectDaddyHelpers
25 25
26 26 class ActiveSupport::TestCase
27 27 # Transactional fixtures accelerate your tests by wrapping each test method
28 28 # in a transaction that's rolled back on completion. This ensures that the
29 29 # test database remains unchanged so your fixtures don't have to be reloaded
30 30 # between every test method. Fewer database queries means faster tests.
31 31 #
32 32 # Read Mike Clark's excellent walkthrough at
33 33 # http://clarkware.com/cgi/blosxom/2005/10/24#Rails10FastTesting
34 34 #
35 35 # Every Active Record database supports transactions except MyISAM tables
36 36 # in MySQL. Turn off transactional fixtures in this case; however, if you
37 37 # don't care one way or the other, switching from MyISAM to InnoDB tables
38 38 # is recommended.
39 39 self.use_transactional_fixtures = true
40 40
41 41 # Instantiated fixtures are slow, but give you @david where otherwise you
42 42 # would need people(:david). If you don't want to migrate your existing
43 43 # test cases which use the @david style and don't mind the speed hit (each
44 44 # instantiated fixtures translates to a database query per test method),
45 45 # then set this back to true.
46 46 self.use_instantiated_fixtures = false
47 47
48 48 # Add more helper methods to be used by all tests here...
49 49
50 50 def log_user(login, password)
51 51 User.anonymous
52 52 get "/login"
53 53 assert_equal nil, session[:user_id]
54 54 assert_response :success
55 55 assert_template "account/login"
56 56 post "/login", :username => login, :password => password
57 57 assert_equal login, User.find(session[:user_id]).login
58 58 end
59 59
60 60 def uploaded_test_file(name, mime)
61 61 ActionController::TestUploadedFile.new(
62 62 ActiveSupport::TestCase.fixture_path + "/files/#{name}", mime, true)
63 63 end
64 64
65 65 def credentials(user, password=nil)
66 66 ActionController::HttpAuthentication::Basic.encode_credentials(user, password || user)
67 67 end
68 68
69 69 # Mock out a file
70 70 def self.mock_file
71 71 file = 'a_file.png'
72 72 file.stubs(:size).returns(32)
73 73 file.stubs(:original_filename).returns('a_file.png')
74 74 file.stubs(:content_type).returns('image/png')
75 75 file.stubs(:read).returns(false)
76 76 file
77 77 end
78 78
79 79 def mock_file
80 80 self.class.mock_file
81 81 end
82 82
83 83 def mock_file_with_options(options={})
84 84 file = ''
85 85 file.stubs(:size).returns(32)
86 86 original_filename = options[:original_filename] || nil
87 87 file.stubs(:original_filename).returns(original_filename)
88 88 content_type = options[:content_type] || nil
89 89 file.stubs(:content_type).returns(content_type)
90 90 file.stubs(:read).returns(false)
91 91 file
92 92 end
93 93
94 94 # Use a temporary directory for attachment related tests
95 95 def set_tmp_attachments_directory
96 96 Dir.mkdir "#{Rails.root}/tmp/test" unless File.directory?("#{Rails.root}/tmp/test")
97 97 unless File.directory?("#{Rails.root}/tmp/test/attachments")
98 98 Dir.mkdir "#{Rails.root}/tmp/test/attachments"
99 99 end
100 100 Attachment.storage_path = "#{Rails.root}/tmp/test/attachments"
101 101 end
102 102
103 103 def set_fixtures_attachments_directory
104 104 Attachment.storage_path = "#{Rails.root}/test/fixtures/files"
105 105 end
106 106
107 107 def with_settings(options, &block)
108 108 saved_settings = options.keys.inject({}) {|h, k| h[k] = Setting[k].is_a?(Symbol) ? Setting[k] : Setting[k].dup; h}
109 109 options.each {|k, v| Setting[k] = v}
110 110 yield
111 111 ensure
112 112 saved_settings.each {|k, v| Setting[k] = v} if saved_settings
113 113 end
114 114
115 115 def change_user_password(login, new_password)
116 116 user = User.first(:conditions => {:login => login})
117 117 user.password, user.password_confirmation = new_password, new_password
118 118 user.save!
119 119 end
120 120
121 121 def self.ldap_configured?
122 122 @test_ldap = Net::LDAP.new(:host => '127.0.0.1', :port => 389)
123 123 return @test_ldap.bind
124 124 rescue Exception => e
125 125 # LDAP is not listening
126 126 return nil
127 127 end
128 128
129 129 # Returns the path to the test +vendor+ repository
130 130 def self.repository_path(vendor)
131 131 Rails.root.join("tmp/test/#{vendor.downcase}_repository").to_s
132 132 end
133 133
134 134 # Returns the url of the subversion test repository
135 135 def self.subversion_repository_url
136 136 path = repository_path('subversion')
137 137 path = '/' + path unless path.starts_with?('/')
138 138 "file://#{path}"
139 139 end
140 140
141 141 # Returns true if the +vendor+ test repository is configured
142 142 def self.repository_configured?(vendor)
143 143 File.directory?(repository_path(vendor))
144 144 end
145 145
146 146 def assert_error_tag(options={})
147 147 assert_tag({:attributes => { :id => 'errorExplanation' }}.merge(options))
148 148 end
149 149
150 150 def assert_include(expected, s)
151 151 assert s.include?(expected), "\"#{expected}\" not found in \"#{s}\""
152 152 end
153 153
154 154 # Shoulda macros
155 155 def self.should_render_404
156 156 should_respond_with :not_found
157 157 should_render_template 'common/error'
158 158 end
159 159
160 160 def self.should_have_before_filter(expected_method, options = {})
161 161 should_have_filter('before', expected_method, options)
162 162 end
163 163
164 164 def self.should_have_after_filter(expected_method, options = {})
165 165 should_have_filter('after', expected_method, options)
166 166 end
167 167
168 168 def self.should_have_filter(filter_type, expected_method, options)
169 169 description = "have #{filter_type}_filter :#{expected_method}"
170 170 description << " with #{options.inspect}" unless options.empty?
171 171
172 172 should description do
173 173 klass = "action_controller/filters/#{filter_type}_filter".classify.constantize
174 174 expected = klass.new(:filter, expected_method.to_sym, options)
175 175 assert_equal 1, @controller.class.filter_chain.select { |filter|
176 176 filter.method == expected.method && filter.kind == expected.kind &&
177 177 filter.options == expected.options && filter.class == expected.class
178 178 }.size
179 179 end
180 180 end
181 181
182 182 def self.should_show_the_old_and_new_values_for(prop_key, model, &block)
183 183 context "" do
184 184 setup do
185 185 if block_given?
186 186 instance_eval &block
187 187 else
188 188 @old_value = model.generate!
189 189 @new_value = model.generate!
190 190 end
191 191 end
192 192
193 193 should "use the new value's name" do
194 194 @detail = JournalDetail.generate!(:property => 'attr',
195 195 :old_value => @old_value.id,
196 196 :value => @new_value.id,
197 197 :prop_key => prop_key)
198 198
199 199 assert_match @new_value.name, show_detail(@detail, true)
200 200 end
201 201
202 202 should "use the old value's name" do
203 203 @detail = JournalDetail.generate!(:property => 'attr',
204 204 :old_value => @old_value.id,
205 205 :value => @new_value.id,
206 206 :prop_key => prop_key)
207 207
208 208 assert_match @old_value.name, show_detail(@detail, true)
209 209 end
210 210 end
211 211 end
212 212
213 213 # Test that a request allows the three types of API authentication
214 214 #
215 215 # * HTTP Basic with username and password
216 216 # * HTTP Basic with an api key for the username
217 217 # * Key based with the key=X parameter
218 218 #
219 219 # @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete)
220 220 # @param [String] url the request url
221 221 # @param [optional, Hash] parameters additional request parameters
222 222 # @param [optional, Hash] options additional options
223 223 # @option options [Symbol] :success_code Successful response code (:success)
224 224 # @option options [Symbol] :failure_code Failure response code (:unauthorized)
225 225 def self.should_allow_api_authentication(http_method, url, parameters={}, options={})
226 226 should_allow_http_basic_auth_with_username_and_password(http_method, url, parameters, options)
227 227 should_allow_http_basic_auth_with_key(http_method, url, parameters, options)
228 228 should_allow_key_based_auth(http_method, url, parameters, options)
229 229 end
230 230
231 231 # Test that a request allows the username and password for HTTP BASIC
232 232 #
233 233 # @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete)
234 234 # @param [String] url the request url
235 235 # @param [optional, Hash] parameters additional request parameters
236 236 # @param [optional, Hash] options additional options
237 237 # @option options [Symbol] :success_code Successful response code (:success)
238 238 # @option options [Symbol] :failure_code Failure response code (:unauthorized)
239 239 def self.should_allow_http_basic_auth_with_username_and_password(http_method, url, parameters={}, options={})
240 240 success_code = options[:success_code] || :success
241 241 failure_code = options[:failure_code] || :unauthorized
242 242
243 243 context "should allow http basic auth using a username and password for #{http_method} #{url}" do
244 244 context "with a valid HTTP authentication" do
245 245 setup do
246 246 @user = User.generate_with_protected!(:password => 'my_password', :password_confirmation => 'my_password', :admin => true) # Admin so they can access the project
247 @authorization = ActionController::HttpAuthentication::Basic.encode_credentials(@user.login, 'my_password')
248 send(http_method, url, parameters, {:authorization => @authorization})
247 send(http_method, url, parameters, {:authorization => credentials(@user.login, 'my_password')})
249 248 end
250 249
251 250 should_respond_with success_code
252 251 should_respond_with_content_type_based_on_url(url)
253 252 should "login as the user" do
254 253 assert_equal @user, User.current
255 254 end
256 255 end
257 256
258 257 context "with an invalid HTTP authentication" do
259 258 setup do
260 259 @user = User.generate_with_protected!
261 @authorization = ActionController::HttpAuthentication::Basic.encode_credentials(@user.login, 'wrong_password')
262 send(http_method, url, parameters, {:authorization => @authorization})
260 send(http_method, url, parameters, {:authorization => credentials(@user.login, 'wrong_password')})
263 261 end
264 262
265 263 should_respond_with failure_code
266 264 should_respond_with_content_type_based_on_url(url)
267 265 should "not login as the user" do
268 266 assert_equal User.anonymous, User.current
269 267 end
270 268 end
271 269
272 270 context "without credentials" do
273 271 setup do
274 272 send(http_method, url, parameters, {:authorization => ''})
275 273 end
276 274
277 275 should_respond_with failure_code
278 276 should_respond_with_content_type_based_on_url(url)
279 277 should "include_www_authenticate_header" do
280 278 assert @controller.response.headers.has_key?('WWW-Authenticate')
281 279 end
282 280 end
283 281 end
284 282
285 283 end
286 284
287 285 # Test that a request allows the API key with HTTP BASIC
288 286 #
289 287 # @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete)
290 288 # @param [String] url the request url
291 289 # @param [optional, Hash] parameters additional request parameters
292 290 # @param [optional, Hash] options additional options
293 291 # @option options [Symbol] :success_code Successful response code (:success)
294 292 # @option options [Symbol] :failure_code Failure response code (:unauthorized)
295 293 def self.should_allow_http_basic_auth_with_key(http_method, url, parameters={}, options={})
296 294 success_code = options[:success_code] || :success
297 295 failure_code = options[:failure_code] || :unauthorized
298 296
299 297 context "should allow http basic auth with a key for #{http_method} #{url}" do
300 298 context "with a valid HTTP authentication using the API token" do
301 299 setup do
302 300 @user = User.generate_with_protected!(:admin => true)
303 301 @token = Token.generate!(:user => @user, :action => 'api')
304 @authorization = ActionController::HttpAuthentication::Basic.encode_credentials(@token.value, 'X')
305 send(http_method, url, parameters, {:authorization => @authorization})
302 send(http_method, url, parameters, {:authorization => credentials(@token.value, 'X')})
306 303 end
307 304
308 305 should_respond_with success_code
309 306 should_respond_with_content_type_based_on_url(url)
310 307 should_be_a_valid_response_string_based_on_url(url)
311 308 should "login as the user" do
312 309 assert_equal @user, User.current
313 310 end
314 311 end
315 312
316 313 context "with an invalid HTTP authentication" do
317 314 setup do
318 315 @user = User.generate_with_protected!
319 316 @token = Token.generate!(:user => @user, :action => 'feeds')
320 @authorization = ActionController::HttpAuthentication::Basic.encode_credentials(@token.value, 'X')
321 send(http_method, url, parameters, {:authorization => @authorization})
317 send(http_method, url, parameters, {:authorization => credentials(@token.value, 'X')})
322 318 end
323 319
324 320 should_respond_with failure_code
325 321 should_respond_with_content_type_based_on_url(url)
326 322 should "not login as the user" do
327 323 assert_equal User.anonymous, User.current
328 324 end
329 325 end
330 326 end
331 327 end
332 328
333 329 # Test that a request allows full key authentication
334 330 #
335 331 # @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete)
336 332 # @param [String] url the request url, without the key=ZXY parameter
337 333 # @param [optional, Hash] parameters additional request parameters
338 334 # @param [optional, Hash] options additional options
339 335 # @option options [Symbol] :success_code Successful response code (:success)
340 336 # @option options [Symbol] :failure_code Failure response code (:unauthorized)
341 337 def self.should_allow_key_based_auth(http_method, url, parameters={}, options={})
342 338 success_code = options[:success_code] || :success
343 339 failure_code = options[:failure_code] || :unauthorized
344 340
345 341 context "should allow key based auth using key=X for #{http_method} #{url}" do
346 342 context "with a valid api token" do
347 343 setup do
348 344 @user = User.generate_with_protected!(:admin => true)
349 345 @token = Token.generate!(:user => @user, :action => 'api')
350 346 # Simple url parse to add on ?key= or &key=
351 347 request_url = if url.match(/\?/)
352 348 url + "&key=#{@token.value}"
353 349 else
354 350 url + "?key=#{@token.value}"
355 351 end
356 352 send(http_method, request_url, parameters)
357 353 end
358 354
359 355 should_respond_with success_code
360 356 should_respond_with_content_type_based_on_url(url)
361 357 should_be_a_valid_response_string_based_on_url(url)
362 358 should "login as the user" do
363 359 assert_equal @user, User.current
364 360 end
365 361 end
366 362
367 363 context "with an invalid api token" do
368 364 setup do
369 365 @user = User.generate_with_protected!
370 366 @token = Token.generate!(:user => @user, :action => 'feeds')
371 367 # Simple url parse to add on ?key= or &key=
372 368 request_url = if url.match(/\?/)
373 369 url + "&key=#{@token.value}"
374 370 else
375 371 url + "?key=#{@token.value}"
376 372 end
377 373 send(http_method, request_url, parameters)
378 374 end
379 375
380 376 should_respond_with failure_code
381 377 should_respond_with_content_type_based_on_url(url)
382 378 should "not login as the user" do
383 379 assert_equal User.anonymous, User.current
384 380 end
385 381 end
386 382 end
387 383
388 384 context "should allow key based auth using X-Redmine-API-Key header for #{http_method} #{url}" do
389 385 setup do
390 386 @user = User.generate_with_protected!(:admin => true)
391 387 @token = Token.generate!(:user => @user, :action => 'api')
392 388 send(http_method, url, parameters, {'X-Redmine-API-Key' => @token.value.to_s})
393 389 end
394 390
395 391 should_respond_with success_code
396 392 should_respond_with_content_type_based_on_url(url)
397 393 should_be_a_valid_response_string_based_on_url(url)
398 394 should "login as the user" do
399 395 assert_equal @user, User.current
400 396 end
401 397 end
402 398 end
403 399
404 400 # Uses should_respond_with_content_type based on what's in the url:
405 401 #
406 402 # '/project/issues.xml' => should_respond_with_content_type :xml
407 403 # '/project/issues.json' => should_respond_with_content_type :json
408 404 #
409 405 # @param [String] url Request
410 406 def self.should_respond_with_content_type_based_on_url(url)
411 407 case
412 408 when url.match(/xml/i)
413 409 should_respond_with_content_type :xml
414 410 when url.match(/json/i)
415 411 should_respond_with_content_type :json
416 412 else
417 413 raise "Unknown content type for should_respond_with_content_type_based_on_url: #{url}"
418 414 end
419 415
420 416 end
421 417
422 418 # Uses the url to assert which format the response should be in
423 419 #
424 420 # '/project/issues.xml' => should_be_a_valid_xml_string
425 421 # '/project/issues.json' => should_be_a_valid_json_string
426 422 #
427 423 # @param [String] url Request
428 424 def self.should_be_a_valid_response_string_based_on_url(url)
429 425 case
430 426 when url.match(/xml/i)
431 427 should_be_a_valid_xml_string
432 428 when url.match(/json/i)
433 429 should_be_a_valid_json_string
434 430 else
435 431 raise "Unknown content type for should_be_a_valid_response_based_on_url: #{url}"
436 432 end
437 433
438 434 end
439 435
440 436 # Checks that the response is a valid JSON string
441 437 def self.should_be_a_valid_json_string
442 438 should "be a valid JSON string (or empty)" do
443 439 assert(response.body.blank? || ActiveSupport::JSON.decode(response.body))
444 440 end
445 441 end
446 442
447 443 # Checks that the response is a valid XML string
448 444 def self.should_be_a_valid_xml_string
449 445 should "be a valid XML string" do
450 446 assert REXML::Document.new(response.body)
451 447 end
452 448 end
453 449
454 450 end
455 451
456 452 # Simple module to "namespace" all of the API tests
457 453 module ApiTest
458 454 end
General Comments 0
You need to be logged in to leave comments. Login now