##// END OF EJS Templates
define constant of escaped "Can't" for tests...
Toshi MARUYAMA -
r12518:b825198e0fd2
parent child
Show More
@@ -1,484 +1,486
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2014 Jean-Philippe Lang
2 # Copyright (C) 2006-2014 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 require 'awesome_nested_set/version'
27 require 'awesome_nested_set/version'
28
28
29 class ActiveSupport::TestCase
29 class ActiveSupport::TestCase
30 include ActionDispatch::TestProcess
30 include ActionDispatch::TestProcess
31
31
32 self.use_transactional_fixtures = true
32 self.use_transactional_fixtures = true
33 self.use_instantiated_fixtures = false
33 self.use_instantiated_fixtures = false
34
34
35 ESCAPED_CANT = 'can't'
35 ESCAPED_CANT = 'can't'
36 ESCAPED_UCANT = 'Can't'
36 # Rails 4.0.2
37 # Rails 4.0.2
37 #ESCAPED_CANT = 'can't'
38 #ESCAPED_CANT = 'can't'
39 #ESCAPED_UCANT = 'Can't'
38
40
39 def log_user(login, password)
41 def log_user(login, password)
40 User.anonymous
42 User.anonymous
41 get "/login"
43 get "/login"
42 assert_equal nil, session[:user_id]
44 assert_equal nil, session[:user_id]
43 assert_response :success
45 assert_response :success
44 assert_template "account/login"
46 assert_template "account/login"
45 post "/login", :username => login, :password => password
47 post "/login", :username => login, :password => password
46 assert_equal login, User.find(session[:user_id]).login
48 assert_equal login, User.find(session[:user_id]).login
47 end
49 end
48
50
49 def uploaded_test_file(name, mime)
51 def uploaded_test_file(name, mime)
50 fixture_file_upload("files/#{name}", mime, true)
52 fixture_file_upload("files/#{name}", mime, true)
51 end
53 end
52
54
53 def credentials(user, password=nil)
55 def credentials(user, password=nil)
54 {'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Basic.encode_credentials(user, password || user)}
56 {'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Basic.encode_credentials(user, password || user)}
55 end
57 end
56
58
57 # Mock out a file
59 # Mock out a file
58 def self.mock_file
60 def self.mock_file
59 file = 'a_file.png'
61 file = 'a_file.png'
60 file.stubs(:size).returns(32)
62 file.stubs(:size).returns(32)
61 file.stubs(:original_filename).returns('a_file.png')
63 file.stubs(:original_filename).returns('a_file.png')
62 file.stubs(:content_type).returns('image/png')
64 file.stubs(:content_type).returns('image/png')
63 file.stubs(:read).returns(false)
65 file.stubs(:read).returns(false)
64 file
66 file
65 end
67 end
66
68
67 def mock_file
69 def mock_file
68 self.class.mock_file
70 self.class.mock_file
69 end
71 end
70
72
71 def mock_file_with_options(options={})
73 def mock_file_with_options(options={})
72 file = ''
74 file = ''
73 file.stubs(:size).returns(32)
75 file.stubs(:size).returns(32)
74 original_filename = options[:original_filename] || nil
76 original_filename = options[:original_filename] || nil
75 file.stubs(:original_filename).returns(original_filename)
77 file.stubs(:original_filename).returns(original_filename)
76 content_type = options[:content_type] || nil
78 content_type = options[:content_type] || nil
77 file.stubs(:content_type).returns(content_type)
79 file.stubs(:content_type).returns(content_type)
78 file.stubs(:read).returns(false)
80 file.stubs(:read).returns(false)
79 file
81 file
80 end
82 end
81
83
82 # Use a temporary directory for attachment related tests
84 # Use a temporary directory for attachment related tests
83 def set_tmp_attachments_directory
85 def set_tmp_attachments_directory
84 Dir.mkdir "#{Rails.root}/tmp/test" unless File.directory?("#{Rails.root}/tmp/test")
86 Dir.mkdir "#{Rails.root}/tmp/test" unless File.directory?("#{Rails.root}/tmp/test")
85 unless File.directory?("#{Rails.root}/tmp/test/attachments")
87 unless File.directory?("#{Rails.root}/tmp/test/attachments")
86 Dir.mkdir "#{Rails.root}/tmp/test/attachments"
88 Dir.mkdir "#{Rails.root}/tmp/test/attachments"
87 end
89 end
88 Attachment.storage_path = "#{Rails.root}/tmp/test/attachments"
90 Attachment.storage_path = "#{Rails.root}/tmp/test/attachments"
89 end
91 end
90
92
91 def set_fixtures_attachments_directory
93 def set_fixtures_attachments_directory
92 Attachment.storage_path = "#{Rails.root}/test/fixtures/files"
94 Attachment.storage_path = "#{Rails.root}/test/fixtures/files"
93 end
95 end
94
96
95 def with_settings(options, &block)
97 def with_settings(options, &block)
96 saved_settings = options.keys.inject({}) do |h, k|
98 saved_settings = options.keys.inject({}) do |h, k|
97 h[k] = case Setting[k]
99 h[k] = case Setting[k]
98 when Symbol, false, true, nil
100 when Symbol, false, true, nil
99 Setting[k]
101 Setting[k]
100 else
102 else
101 Setting[k].dup
103 Setting[k].dup
102 end
104 end
103 h
105 h
104 end
106 end
105 options.each {|k, v| Setting[k] = v}
107 options.each {|k, v| Setting[k] = v}
106 yield
108 yield
107 ensure
109 ensure
108 saved_settings.each {|k, v| Setting[k] = v} if saved_settings
110 saved_settings.each {|k, v| Setting[k] = v} if saved_settings
109 end
111 end
110
112
111 # Yields the block with user as the current user
113 # Yields the block with user as the current user
112 def with_current_user(user, &block)
114 def with_current_user(user, &block)
113 saved_user = User.current
115 saved_user = User.current
114 User.current = user
116 User.current = user
115 yield
117 yield
116 ensure
118 ensure
117 User.current = saved_user
119 User.current = saved_user
118 end
120 end
119
121
120 def change_user_password(login, new_password)
122 def change_user_password(login, new_password)
121 user = User.where(:login => login).first
123 user = User.where(:login => login).first
122 user.password, user.password_confirmation = new_password, new_password
124 user.password, user.password_confirmation = new_password, new_password
123 user.save!
125 user.save!
124 end
126 end
125
127
126 def self.ldap_configured?
128 def self.ldap_configured?
127 @test_ldap = Net::LDAP.new(:host => '127.0.0.1', :port => 389)
129 @test_ldap = Net::LDAP.new(:host => '127.0.0.1', :port => 389)
128 return @test_ldap.bind
130 return @test_ldap.bind
129 rescue Exception => e
131 rescue Exception => e
130 # LDAP is not listening
132 # LDAP is not listening
131 return nil
133 return nil
132 end
134 end
133
135
134 def self.convert_installed?
136 def self.convert_installed?
135 Redmine::Thumbnail.convert_available?
137 Redmine::Thumbnail.convert_available?
136 end
138 end
137
139
138 # Returns the path to the test +vendor+ repository
140 # Returns the path to the test +vendor+ repository
139 def self.repository_path(vendor)
141 def self.repository_path(vendor)
140 Rails.root.join("tmp/test/#{vendor.downcase}_repository").to_s
142 Rails.root.join("tmp/test/#{vendor.downcase}_repository").to_s
141 end
143 end
142
144
143 # Returns the url of the subversion test repository
145 # Returns the url of the subversion test repository
144 def self.subversion_repository_url
146 def self.subversion_repository_url
145 path = repository_path('subversion')
147 path = repository_path('subversion')
146 path = '/' + path unless path.starts_with?('/')
148 path = '/' + path unless path.starts_with?('/')
147 "file://#{path}"
149 "file://#{path}"
148 end
150 end
149
151
150 # Returns true if the +vendor+ test repository is configured
152 # Returns true if the +vendor+ test repository is configured
151 def self.repository_configured?(vendor)
153 def self.repository_configured?(vendor)
152 File.directory?(repository_path(vendor))
154 File.directory?(repository_path(vendor))
153 end
155 end
154
156
155 def repository_path_hash(arr)
157 def repository_path_hash(arr)
156 hs = {}
158 hs = {}
157 hs[:path] = arr.join("/")
159 hs[:path] = arr.join("/")
158 hs[:param] = arr.join("/")
160 hs[:param] = arr.join("/")
159 hs
161 hs
160 end
162 end
161
163
162 def assert_save(object)
164 def assert_save(object)
163 saved = object.save
165 saved = object.save
164 message = "#{object.class} could not be saved"
166 message = "#{object.class} could not be saved"
165 errors = object.errors.full_messages.map {|m| "- #{m}"}
167 errors = object.errors.full_messages.map {|m| "- #{m}"}
166 message << ":\n#{errors.join("\n")}" if errors.any?
168 message << ":\n#{errors.join("\n")}" if errors.any?
167 assert_equal true, saved, message
169 assert_equal true, saved, message
168 end
170 end
169
171
170 def assert_error_tag(options={})
172 def assert_error_tag(options={})
171 assert_tag({:attributes => { :id => 'errorExplanation' }}.merge(options))
173 assert_tag({:attributes => { :id => 'errorExplanation' }}.merge(options))
172 end
174 end
173
175
174 def assert_include(expected, s, message=nil)
176 def assert_include(expected, s, message=nil)
175 assert s.include?(expected), (message || "\"#{expected}\" not found in \"#{s}\"")
177 assert s.include?(expected), (message || "\"#{expected}\" not found in \"#{s}\"")
176 end
178 end
177
179
178 def assert_not_include(expected, s, message=nil)
180 def assert_not_include(expected, s, message=nil)
179 assert !s.include?(expected), (message || "\"#{expected}\" found in \"#{s}\"")
181 assert !s.include?(expected), (message || "\"#{expected}\" found in \"#{s}\"")
180 end
182 end
181
183
182 def assert_select_in(text, *args, &block)
184 def assert_select_in(text, *args, &block)
183 d = HTML::Document.new(CGI::unescapeHTML(String.new(text))).root
185 d = HTML::Document.new(CGI::unescapeHTML(String.new(text))).root
184 assert_select(d, *args, &block)
186 assert_select(d, *args, &block)
185 end
187 end
186
188
187 def assert_mail_body_match(expected, mail, message=nil)
189 def assert_mail_body_match(expected, mail, message=nil)
188 if expected.is_a?(String)
190 if expected.is_a?(String)
189 assert_include expected, mail_body(mail), message
191 assert_include expected, mail_body(mail), message
190 else
192 else
191 assert_match expected, mail_body(mail), message
193 assert_match expected, mail_body(mail), message
192 end
194 end
193 end
195 end
194
196
195 def assert_mail_body_no_match(expected, mail, message=nil)
197 def assert_mail_body_no_match(expected, mail, message=nil)
196 if expected.is_a?(String)
198 if expected.is_a?(String)
197 assert_not_include expected, mail_body(mail), message
199 assert_not_include expected, mail_body(mail), message
198 else
200 else
199 assert_no_match expected, mail_body(mail), message
201 assert_no_match expected, mail_body(mail), message
200 end
202 end
201 end
203 end
202
204
203 def mail_body(mail)
205 def mail_body(mail)
204 mail.parts.first.body.encoded
206 mail.parts.first.body.encoded
205 end
207 end
206
208
207 # awesome_nested_set new node lft and rgt value changed this refactor revision.
209 # awesome_nested_set new node lft and rgt value changed this refactor revision.
208 # https://github.com/collectiveidea/awesome_nested_set/commit/199fca9bb938e40200cd90714dc69247ef017c61
210 # https://github.com/collectiveidea/awesome_nested_set/commit/199fca9bb938e40200cd90714dc69247ef017c61
209 # The reason of behavior change is that "self.class.base_class.unscoped" was added to this line.
211 # The reason of behavior change is that "self.class.base_class.unscoped" was added to this line.
210 # https://github.com/collectiveidea/awesome_nested_set/commit/199fca9bb9#diff-f61b59a5e6319024e211b0ffdd0e4ef1R273
212 # https://github.com/collectiveidea/awesome_nested_set/commit/199fca9bb9#diff-f61b59a5e6319024e211b0ffdd0e4ef1R273
211 # It seems correct behavior because of this line comment.
213 # It seems correct behavior because of this line comment.
212 # https://github.com/collectiveidea/awesome_nested_set/blame/199fca9bb9/lib/awesome_nested_set/model.rb#L278
214 # https://github.com/collectiveidea/awesome_nested_set/blame/199fca9bb9/lib/awesome_nested_set/model.rb#L278
213 def new_issue_lft
215 def new_issue_lft
214 ::AwesomeNestedSet::VERSION > "2.1.6" ? Issue.maximum(:rgt) + 1 : 1
216 ::AwesomeNestedSet::VERSION > "2.1.6" ? Issue.maximum(:rgt) + 1 : 1
215 end
217 end
216 end
218 end
217
219
218 module Redmine
220 module Redmine
219 module ApiTest
221 module ApiTest
220 # Base class for API tests
222 # Base class for API tests
221 class Base < ActionDispatch::IntegrationTest
223 class Base < ActionDispatch::IntegrationTest
222 # Test that a request allows the three types of API authentication
224 # Test that a request allows the three types of API authentication
223 #
225 #
224 # * HTTP Basic with username and password
226 # * HTTP Basic with username and password
225 # * HTTP Basic with an api key for the username
227 # * HTTP Basic with an api key for the username
226 # * Key based with the key=X parameter
228 # * Key based with the key=X parameter
227 #
229 #
228 # @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete)
230 # @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete)
229 # @param [String] url the request url
231 # @param [String] url the request url
230 # @param [optional, Hash] parameters additional request parameters
232 # @param [optional, Hash] parameters additional request parameters
231 # @param [optional, Hash] options additional options
233 # @param [optional, Hash] options additional options
232 # @option options [Symbol] :success_code Successful response code (:success)
234 # @option options [Symbol] :success_code Successful response code (:success)
233 # @option options [Symbol] :failure_code Failure response code (:unauthorized)
235 # @option options [Symbol] :failure_code Failure response code (:unauthorized)
234 def self.should_allow_api_authentication(http_method, url, parameters={}, options={})
236 def self.should_allow_api_authentication(http_method, url, parameters={}, options={})
235 should_allow_http_basic_auth_with_username_and_password(http_method, url, parameters, options)
237 should_allow_http_basic_auth_with_username_and_password(http_method, url, parameters, options)
236 should_allow_http_basic_auth_with_key(http_method, url, parameters, options)
238 should_allow_http_basic_auth_with_key(http_method, url, parameters, options)
237 should_allow_key_based_auth(http_method, url, parameters, options)
239 should_allow_key_based_auth(http_method, url, parameters, options)
238 end
240 end
239
241
240 # Test that a request allows the username and password for HTTP BASIC
242 # Test that a request allows the username and password for HTTP BASIC
241 #
243 #
242 # @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete)
244 # @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete)
243 # @param [String] url the request url
245 # @param [String] url the request url
244 # @param [optional, Hash] parameters additional request parameters
246 # @param [optional, Hash] parameters additional request parameters
245 # @param [optional, Hash] options additional options
247 # @param [optional, Hash] options additional options
246 # @option options [Symbol] :success_code Successful response code (:success)
248 # @option options [Symbol] :success_code Successful response code (:success)
247 # @option options [Symbol] :failure_code Failure response code (:unauthorized)
249 # @option options [Symbol] :failure_code Failure response code (:unauthorized)
248 def self.should_allow_http_basic_auth_with_username_and_password(http_method, url, parameters={}, options={})
250 def self.should_allow_http_basic_auth_with_username_and_password(http_method, url, parameters={}, options={})
249 success_code = options[:success_code] || :success
251 success_code = options[:success_code] || :success
250 failure_code = options[:failure_code] || :unauthorized
252 failure_code = options[:failure_code] || :unauthorized
251
253
252 context "should allow http basic auth using a username and password for #{http_method} #{url}" do
254 context "should allow http basic auth using a username and password for #{http_method} #{url}" do
253 context "with a valid HTTP authentication" do
255 context "with a valid HTTP authentication" do
254 setup do
256 setup do
255 @user = User.generate! do |user|
257 @user = User.generate! do |user|
256 user.admin = true
258 user.admin = true
257 user.password = 'my_password'
259 user.password = 'my_password'
258 end
260 end
259 send(http_method, url, parameters, credentials(@user.login, 'my_password'))
261 send(http_method, url, parameters, credentials(@user.login, 'my_password'))
260 end
262 end
261
263
262 should_respond_with success_code
264 should_respond_with success_code
263 should_respond_with_content_type_based_on_url(url)
265 should_respond_with_content_type_based_on_url(url)
264 should "login as the user" do
266 should "login as the user" do
265 assert_equal @user, User.current
267 assert_equal @user, User.current
266 end
268 end
267 end
269 end
268
270
269 context "with an invalid HTTP authentication" do
271 context "with an invalid HTTP authentication" do
270 setup do
272 setup do
271 @user = User.generate!
273 @user = User.generate!
272 send(http_method, url, parameters, credentials(@user.login, 'wrong_password'))
274 send(http_method, url, parameters, credentials(@user.login, 'wrong_password'))
273 end
275 end
274
276
275 should_respond_with failure_code
277 should_respond_with failure_code
276 should_respond_with_content_type_based_on_url(url)
278 should_respond_with_content_type_based_on_url(url)
277 should "not login as the user" do
279 should "not login as the user" do
278 assert_equal User.anonymous, User.current
280 assert_equal User.anonymous, User.current
279 end
281 end
280 end
282 end
281
283
282 context "without credentials" do
284 context "without credentials" do
283 setup do
285 setup do
284 send(http_method, url, parameters)
286 send(http_method, url, parameters)
285 end
287 end
286
288
287 should_respond_with failure_code
289 should_respond_with failure_code
288 should_respond_with_content_type_based_on_url(url)
290 should_respond_with_content_type_based_on_url(url)
289 should "include_www_authenticate_header" do
291 should "include_www_authenticate_header" do
290 assert @controller.response.headers.has_key?('WWW-Authenticate')
292 assert @controller.response.headers.has_key?('WWW-Authenticate')
291 end
293 end
292 end
294 end
293 end
295 end
294 end
296 end
295
297
296 # Test that a request allows the API key with HTTP BASIC
298 # Test that a request allows the API key with HTTP BASIC
297 #
299 #
298 # @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete)
300 # @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete)
299 # @param [String] url the request url
301 # @param [String] url the request url
300 # @param [optional, Hash] parameters additional request parameters
302 # @param [optional, Hash] parameters additional request parameters
301 # @param [optional, Hash] options additional options
303 # @param [optional, Hash] options additional options
302 # @option options [Symbol] :success_code Successful response code (:success)
304 # @option options [Symbol] :success_code Successful response code (:success)
303 # @option options [Symbol] :failure_code Failure response code (:unauthorized)
305 # @option options [Symbol] :failure_code Failure response code (:unauthorized)
304 def self.should_allow_http_basic_auth_with_key(http_method, url, parameters={}, options={})
306 def self.should_allow_http_basic_auth_with_key(http_method, url, parameters={}, options={})
305 success_code = options[:success_code] || :success
307 success_code = options[:success_code] || :success
306 failure_code = options[:failure_code] || :unauthorized
308 failure_code = options[:failure_code] || :unauthorized
307
309
308 context "should allow http basic auth with a key for #{http_method} #{url}" do
310 context "should allow http basic auth with a key for #{http_method} #{url}" do
309 context "with a valid HTTP authentication using the API token" do
311 context "with a valid HTTP authentication using the API token" do
310 setup do
312 setup do
311 @user = User.generate! do |user|
313 @user = User.generate! do |user|
312 user.admin = true
314 user.admin = true
313 end
315 end
314 @token = Token.create!(:user => @user, :action => 'api')
316 @token = Token.create!(:user => @user, :action => 'api')
315 send(http_method, url, parameters, credentials(@token.value, 'X'))
317 send(http_method, url, parameters, credentials(@token.value, 'X'))
316 end
318 end
317 should_respond_with success_code
319 should_respond_with success_code
318 should_respond_with_content_type_based_on_url(url)
320 should_respond_with_content_type_based_on_url(url)
319 should_be_a_valid_response_string_based_on_url(url)
321 should_be_a_valid_response_string_based_on_url(url)
320 should "login as the user" do
322 should "login as the user" do
321 assert_equal @user, User.current
323 assert_equal @user, User.current
322 end
324 end
323 end
325 end
324
326
325 context "with an invalid HTTP authentication" do
327 context "with an invalid HTTP authentication" do
326 setup do
328 setup do
327 @user = User.generate!
329 @user = User.generate!
328 @token = Token.create!(:user => @user, :action => 'feeds')
330 @token = Token.create!(:user => @user, :action => 'feeds')
329 send(http_method, url, parameters, credentials(@token.value, 'X'))
331 send(http_method, url, parameters, credentials(@token.value, 'X'))
330 end
332 end
331 should_respond_with failure_code
333 should_respond_with failure_code
332 should_respond_with_content_type_based_on_url(url)
334 should_respond_with_content_type_based_on_url(url)
333 should "not login as the user" do
335 should "not login as the user" do
334 assert_equal User.anonymous, User.current
336 assert_equal User.anonymous, User.current
335 end
337 end
336 end
338 end
337 end
339 end
338 end
340 end
339
341
340 # Test that a request allows full key authentication
342 # Test that a request allows full key authentication
341 #
343 #
342 # @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete)
344 # @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete)
343 # @param [String] url the request url, without the key=ZXY parameter
345 # @param [String] url the request url, without the key=ZXY parameter
344 # @param [optional, Hash] parameters additional request parameters
346 # @param [optional, Hash] parameters additional request parameters
345 # @param [optional, Hash] options additional options
347 # @param [optional, Hash] options additional options
346 # @option options [Symbol] :success_code Successful response code (:success)
348 # @option options [Symbol] :success_code Successful response code (:success)
347 # @option options [Symbol] :failure_code Failure response code (:unauthorized)
349 # @option options [Symbol] :failure_code Failure response code (:unauthorized)
348 def self.should_allow_key_based_auth(http_method, url, parameters={}, options={})
350 def self.should_allow_key_based_auth(http_method, url, parameters={}, options={})
349 success_code = options[:success_code] || :success
351 success_code = options[:success_code] || :success
350 failure_code = options[:failure_code] || :unauthorized
352 failure_code = options[:failure_code] || :unauthorized
351
353
352 context "should allow key based auth using key=X for #{http_method} #{url}" do
354 context "should allow key based auth using key=X for #{http_method} #{url}" do
353 context "with a valid api token" do
355 context "with a valid api token" do
354 setup do
356 setup do
355 @user = User.generate! do |user|
357 @user = User.generate! do |user|
356 user.admin = true
358 user.admin = true
357 end
359 end
358 @token = Token.create!(:user => @user, :action => 'api')
360 @token = Token.create!(:user => @user, :action => 'api')
359 # Simple url parse to add on ?key= or &key=
361 # Simple url parse to add on ?key= or &key=
360 request_url = if url.match(/\?/)
362 request_url = if url.match(/\?/)
361 url + "&key=#{@token.value}"
363 url + "&key=#{@token.value}"
362 else
364 else
363 url + "?key=#{@token.value}"
365 url + "?key=#{@token.value}"
364 end
366 end
365 send(http_method, request_url, parameters)
367 send(http_method, request_url, parameters)
366 end
368 end
367 should_respond_with success_code
369 should_respond_with success_code
368 should_respond_with_content_type_based_on_url(url)
370 should_respond_with_content_type_based_on_url(url)
369 should_be_a_valid_response_string_based_on_url(url)
371 should_be_a_valid_response_string_based_on_url(url)
370 should "login as the user" do
372 should "login as the user" do
371 assert_equal @user, User.current
373 assert_equal @user, User.current
372 end
374 end
373 end
375 end
374
376
375 context "with an invalid api token" do
377 context "with an invalid api token" do
376 setup do
378 setup do
377 @user = User.generate! do |user|
379 @user = User.generate! do |user|
378 user.admin = true
380 user.admin = true
379 end
381 end
380 @token = Token.create!(:user => @user, :action => 'feeds')
382 @token = Token.create!(:user => @user, :action => 'feeds')
381 # Simple url parse to add on ?key= or &key=
383 # Simple url parse to add on ?key= or &key=
382 request_url = if url.match(/\?/)
384 request_url = if url.match(/\?/)
383 url + "&key=#{@token.value}"
385 url + "&key=#{@token.value}"
384 else
386 else
385 url + "?key=#{@token.value}"
387 url + "?key=#{@token.value}"
386 end
388 end
387 send(http_method, request_url, parameters)
389 send(http_method, request_url, parameters)
388 end
390 end
389 should_respond_with failure_code
391 should_respond_with failure_code
390 should_respond_with_content_type_based_on_url(url)
392 should_respond_with_content_type_based_on_url(url)
391 should "not login as the user" do
393 should "not login as the user" do
392 assert_equal User.anonymous, User.current
394 assert_equal User.anonymous, User.current
393 end
395 end
394 end
396 end
395 end
397 end
396
398
397 context "should allow key based auth using X-Redmine-API-Key header for #{http_method} #{url}" do
399 context "should allow key based auth using X-Redmine-API-Key header for #{http_method} #{url}" do
398 setup do
400 setup do
399 @user = User.generate! do |user|
401 @user = User.generate! do |user|
400 user.admin = true
402 user.admin = true
401 end
403 end
402 @token = Token.create!(:user => @user, :action => 'api')
404 @token = Token.create!(:user => @user, :action => 'api')
403 send(http_method, url, parameters, {'X-Redmine-API-Key' => @token.value.to_s})
405 send(http_method, url, parameters, {'X-Redmine-API-Key' => @token.value.to_s})
404 end
406 end
405 should_respond_with success_code
407 should_respond_with success_code
406 should_respond_with_content_type_based_on_url(url)
408 should_respond_with_content_type_based_on_url(url)
407 should_be_a_valid_response_string_based_on_url(url)
409 should_be_a_valid_response_string_based_on_url(url)
408 should "login as the user" do
410 should "login as the user" do
409 assert_equal @user, User.current
411 assert_equal @user, User.current
410 end
412 end
411 end
413 end
412 end
414 end
413
415
414 # Uses should_respond_with_content_type based on what's in the url:
416 # Uses should_respond_with_content_type based on what's in the url:
415 #
417 #
416 # '/project/issues.xml' => should_respond_with_content_type :xml
418 # '/project/issues.xml' => should_respond_with_content_type :xml
417 # '/project/issues.json' => should_respond_with_content_type :json
419 # '/project/issues.json' => should_respond_with_content_type :json
418 #
420 #
419 # @param [String] url Request
421 # @param [String] url Request
420 def self.should_respond_with_content_type_based_on_url(url)
422 def self.should_respond_with_content_type_based_on_url(url)
421 case
423 case
422 when url.match(/xml/i)
424 when url.match(/xml/i)
423 should "respond with XML" do
425 should "respond with XML" do
424 assert_equal 'application/xml', @response.content_type
426 assert_equal 'application/xml', @response.content_type
425 end
427 end
426 when url.match(/json/i)
428 when url.match(/json/i)
427 should "respond with JSON" do
429 should "respond with JSON" do
428 assert_equal 'application/json', @response.content_type
430 assert_equal 'application/json', @response.content_type
429 end
431 end
430 else
432 else
431 raise "Unknown content type for should_respond_with_content_type_based_on_url: #{url}"
433 raise "Unknown content type for should_respond_with_content_type_based_on_url: #{url}"
432 end
434 end
433 end
435 end
434
436
435 # Uses the url to assert which format the response should be in
437 # Uses the url to assert which format the response should be in
436 #
438 #
437 # '/project/issues.xml' => should_be_a_valid_xml_string
439 # '/project/issues.xml' => should_be_a_valid_xml_string
438 # '/project/issues.json' => should_be_a_valid_json_string
440 # '/project/issues.json' => should_be_a_valid_json_string
439 #
441 #
440 # @param [String] url Request
442 # @param [String] url Request
441 def self.should_be_a_valid_response_string_based_on_url(url)
443 def self.should_be_a_valid_response_string_based_on_url(url)
442 case
444 case
443 when url.match(/xml/i)
445 when url.match(/xml/i)
444 should_be_a_valid_xml_string
446 should_be_a_valid_xml_string
445 when url.match(/json/i)
447 when url.match(/json/i)
446 should_be_a_valid_json_string
448 should_be_a_valid_json_string
447 else
449 else
448 raise "Unknown content type for should_be_a_valid_response_based_on_url: #{url}"
450 raise "Unknown content type for should_be_a_valid_response_based_on_url: #{url}"
449 end
451 end
450 end
452 end
451
453
452 # Checks that the response is a valid JSON string
454 # Checks that the response is a valid JSON string
453 def self.should_be_a_valid_json_string
455 def self.should_be_a_valid_json_string
454 should "be a valid JSON string (or empty)" do
456 should "be a valid JSON string (or empty)" do
455 assert(response.body.blank? || ActiveSupport::JSON.decode(response.body))
457 assert(response.body.blank? || ActiveSupport::JSON.decode(response.body))
456 end
458 end
457 end
459 end
458
460
459 # Checks that the response is a valid XML string
461 # Checks that the response is a valid XML string
460 def self.should_be_a_valid_xml_string
462 def self.should_be_a_valid_xml_string
461 should "be a valid XML string" do
463 should "be a valid XML string" do
462 assert REXML::Document.new(response.body)
464 assert REXML::Document.new(response.body)
463 end
465 end
464 end
466 end
465
467
466 def self.should_respond_with(status)
468 def self.should_respond_with(status)
467 should "respond with #{status}" do
469 should "respond with #{status}" do
468 assert_response status
470 assert_response status
469 end
471 end
470 end
472 end
471 end
473 end
472 end
474 end
473 end
475 end
474
476
475 # URL helpers do not work with config.threadsafe!
477 # URL helpers do not work with config.threadsafe!
476 # https://github.com/rspec/rspec-rails/issues/476#issuecomment-4705454
478 # https://github.com/rspec/rspec-rails/issues/476#issuecomment-4705454
477 ActionView::TestCase::TestController.instance_eval do
479 ActionView::TestCase::TestController.instance_eval do
478 helper Rails.application.routes.url_helpers
480 helper Rails.application.routes.url_helpers
479 end
481 end
480 ActionView::TestCase::TestController.class_eval do
482 ActionView::TestCase::TestController.class_eval do
481 def _routes
483 def _routes
482 Rails.application.routes
484 Rails.application.routes
483 end
485 end
484 end
486 end
General Comments 0
You need to be logged in to leave comments. Login now