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