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