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