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