##// END OF EJS Templates
Additional tests for IssuesHelper#show_detail....
Jean-Philippe Lang -
r9332:4598c64ebd6d
parent child
Show More
@@ -1,485 +1,454
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
2 # Copyright (C) 2006-2011 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 ENV["RAILS_ENV"] = "test"
18 ENV["RAILS_ENV"] = "test"
19 require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
19 require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
20 require 'test_help'
20 require 'test_help'
21 require Rails.root.join('test', 'mocks', 'open_id_authentication_mock.rb').to_s
21 require Rails.root.join('test', 'mocks', 'open_id_authentication_mock.rb').to_s
22
22
23 require File.expand_path(File.dirname(__FILE__) + '/object_daddy_helpers')
23 require File.expand_path(File.dirname(__FILE__) + '/object_daddy_helpers')
24 include ObjectDaddyHelpers
24 include ObjectDaddyHelpers
25
25
26 class ActiveSupport::TestCase
26 class ActiveSupport::TestCase
27 # Transactional fixtures accelerate your tests by wrapping each test method
27 # Transactional fixtures accelerate your tests by wrapping each test method
28 # in a transaction that's rolled back on completion. This ensures that the
28 # in a transaction that's rolled back on completion. This ensures that the
29 # test database remains unchanged so your fixtures don't have to be reloaded
29 # test database remains unchanged so your fixtures don't have to be reloaded
30 # between every test method. Fewer database queries means faster tests.
30 # between every test method. Fewer database queries means faster tests.
31 #
31 #
32 # Read Mike Clark's excellent walkthrough at
32 # Read Mike Clark's excellent walkthrough at
33 # http://clarkware.com/cgi/blosxom/2005/10/24#Rails10FastTesting
33 # http://clarkware.com/cgi/blosxom/2005/10/24#Rails10FastTesting
34 #
34 #
35 # Every Active Record database supports transactions except MyISAM tables
35 # Every Active Record database supports transactions except MyISAM tables
36 # in MySQL. Turn off transactional fixtures in this case; however, if you
36 # in MySQL. Turn off transactional fixtures in this case; however, if you
37 # don't care one way or the other, switching from MyISAM to InnoDB tables
37 # don't care one way or the other, switching from MyISAM to InnoDB tables
38 # is recommended.
38 # is recommended.
39 self.use_transactional_fixtures = true
39 self.use_transactional_fixtures = true
40
40
41 # Instantiated fixtures are slow, but give you @david where otherwise you
41 # Instantiated fixtures are slow, but give you @david where otherwise you
42 # would need people(:david). If you don't want to migrate your existing
42 # would need people(:david). If you don't want to migrate your existing
43 # test cases which use the @david style and don't mind the speed hit (each
43 # test cases which use the @david style and don't mind the speed hit (each
44 # instantiated fixtures translates to a database query per test method),
44 # instantiated fixtures translates to a database query per test method),
45 # then set this back to true.
45 # then set this back to true.
46 self.use_instantiated_fixtures = false
46 self.use_instantiated_fixtures = false
47
47
48 # Add more helper methods to be used by all tests here...
48 # Add more helper methods to be used by all tests here...
49
49
50 def log_user(login, password)
50 def log_user(login, password)
51 User.anonymous
51 User.anonymous
52 get "/login"
52 get "/login"
53 assert_equal nil, session[:user_id]
53 assert_equal nil, session[:user_id]
54 assert_response :success
54 assert_response :success
55 assert_template "account/login"
55 assert_template "account/login"
56 post "/login", :username => login, :password => password
56 post "/login", :username => login, :password => password
57 assert_equal login, User.find(session[:user_id]).login
57 assert_equal login, User.find(session[:user_id]).login
58 end
58 end
59
59
60 def uploaded_test_file(name, mime)
60 def uploaded_test_file(name, mime)
61 ActionController::TestUploadedFile.new(
61 ActionController::TestUploadedFile.new(
62 ActiveSupport::TestCase.fixture_path + "/files/#{name}", mime, true)
62 ActiveSupport::TestCase.fixture_path + "/files/#{name}", mime, true)
63 end
63 end
64
64
65 def credentials(user, password=nil)
65 def credentials(user, password=nil)
66 {:authorization => ActionController::HttpAuthentication::Basic.encode_credentials(user, password || user)}
66 {:authorization => ActionController::HttpAuthentication::Basic.encode_credentials(user, password || user)}
67 end
67 end
68
68
69 # Mock out a file
69 # Mock out a file
70 def self.mock_file
70 def self.mock_file
71 file = 'a_file.png'
71 file = 'a_file.png'
72 file.stubs(:size).returns(32)
72 file.stubs(:size).returns(32)
73 file.stubs(:original_filename).returns('a_file.png')
73 file.stubs(:original_filename).returns('a_file.png')
74 file.stubs(:content_type).returns('image/png')
74 file.stubs(:content_type).returns('image/png')
75 file.stubs(:read).returns(false)
75 file.stubs(:read).returns(false)
76 file
76 file
77 end
77 end
78
78
79 def mock_file
79 def mock_file
80 self.class.mock_file
80 self.class.mock_file
81 end
81 end
82
82
83 def mock_file_with_options(options={})
83 def mock_file_with_options(options={})
84 file = ''
84 file = ''
85 file.stubs(:size).returns(32)
85 file.stubs(:size).returns(32)
86 original_filename = options[:original_filename] || nil
86 original_filename = options[:original_filename] || nil
87 file.stubs(:original_filename).returns(original_filename)
87 file.stubs(:original_filename).returns(original_filename)
88 content_type = options[:content_type] || nil
88 content_type = options[:content_type] || nil
89 file.stubs(:content_type).returns(content_type)
89 file.stubs(:content_type).returns(content_type)
90 file.stubs(:read).returns(false)
90 file.stubs(:read).returns(false)
91 file
91 file
92 end
92 end
93
93
94 # Use a temporary directory for attachment related tests
94 # Use a temporary directory for attachment related tests
95 def set_tmp_attachments_directory
95 def set_tmp_attachments_directory
96 Dir.mkdir "#{Rails.root}/tmp/test" unless File.directory?("#{Rails.root}/tmp/test")
96 Dir.mkdir "#{Rails.root}/tmp/test" unless File.directory?("#{Rails.root}/tmp/test")
97 unless File.directory?("#{Rails.root}/tmp/test/attachments")
97 unless File.directory?("#{Rails.root}/tmp/test/attachments")
98 Dir.mkdir "#{Rails.root}/tmp/test/attachments"
98 Dir.mkdir "#{Rails.root}/tmp/test/attachments"
99 end
99 end
100 Attachment.storage_path = "#{Rails.root}/tmp/test/attachments"
100 Attachment.storage_path = "#{Rails.root}/tmp/test/attachments"
101 end
101 end
102
102
103 def set_fixtures_attachments_directory
103 def set_fixtures_attachments_directory
104 Attachment.storage_path = "#{Rails.root}/test/fixtures/files"
104 Attachment.storage_path = "#{Rails.root}/test/fixtures/files"
105 end
105 end
106
106
107 def with_settings(options, &block)
107 def with_settings(options, &block)
108 saved_settings = options.keys.inject({}) {|h, k| h[k] = Setting[k].is_a?(Symbol) ? Setting[k] : Setting[k].dup; h}
108 saved_settings = options.keys.inject({}) {|h, k| h[k] = Setting[k].is_a?(Symbol) ? Setting[k] : Setting[k].dup; h}
109 options.each {|k, v| Setting[k] = v}
109 options.each {|k, v| Setting[k] = v}
110 yield
110 yield
111 ensure
111 ensure
112 saved_settings.each {|k, v| Setting[k] = v} if saved_settings
112 saved_settings.each {|k, v| Setting[k] = v} if saved_settings
113 end
113 end
114
114
115 def change_user_password(login, new_password)
115 def change_user_password(login, new_password)
116 user = User.first(:conditions => {:login => login})
116 user = User.first(:conditions => {:login => login})
117 user.password, user.password_confirmation = new_password, new_password
117 user.password, user.password_confirmation = new_password, new_password
118 user.save!
118 user.save!
119 end
119 end
120
120
121 def self.ldap_configured?
121 def self.ldap_configured?
122 @test_ldap = Net::LDAP.new(:host => '127.0.0.1', :port => 389)
122 @test_ldap = Net::LDAP.new(:host => '127.0.0.1', :port => 389)
123 return @test_ldap.bind
123 return @test_ldap.bind
124 rescue Exception => e
124 rescue Exception => e
125 # LDAP is not listening
125 # LDAP is not listening
126 return nil
126 return nil
127 end
127 end
128
128
129 # Returns the path to the test +vendor+ repository
129 # Returns the path to the test +vendor+ repository
130 def self.repository_path(vendor)
130 def self.repository_path(vendor)
131 Rails.root.join("tmp/test/#{vendor.downcase}_repository").to_s
131 Rails.root.join("tmp/test/#{vendor.downcase}_repository").to_s
132 end
132 end
133
133
134 # Returns the url of the subversion test repository
134 # Returns the url of the subversion test repository
135 def self.subversion_repository_url
135 def self.subversion_repository_url
136 path = repository_path('subversion')
136 path = repository_path('subversion')
137 path = '/' + path unless path.starts_with?('/')
137 path = '/' + path unless path.starts_with?('/')
138 "file://#{path}"
138 "file://#{path}"
139 end
139 end
140
140
141 # Returns true if the +vendor+ test repository is configured
141 # Returns true if the +vendor+ test repository is configured
142 def self.repository_configured?(vendor)
142 def self.repository_configured?(vendor)
143 File.directory?(repository_path(vendor))
143 File.directory?(repository_path(vendor))
144 end
144 end
145
145
146 def repository_path_hash(arr)
146 def repository_path_hash(arr)
147 hs = {}
147 hs = {}
148 hs[:path] = arr.join("/")
148 hs[:path] = arr.join("/")
149 hs[:param] = arr
149 hs[:param] = arr
150 hs
150 hs
151 end
151 end
152
152
153 def assert_error_tag(options={})
153 def assert_error_tag(options={})
154 assert_tag({:attributes => { :id => 'errorExplanation' }}.merge(options))
154 assert_tag({:attributes => { :id => 'errorExplanation' }}.merge(options))
155 end
155 end
156
156
157 def assert_include(expected, s)
157 def assert_include(expected, s)
158 assert s.include?(expected), "\"#{expected}\" not found in \"#{s}\""
158 assert s.include?(expected), "\"#{expected}\" not found in \"#{s}\""
159 end
159 end
160
160
161 def assert_not_include(expected, s)
161 def assert_not_include(expected, s)
162 assert !s.include?(expected), "\"#{expected}\" found in \"#{s}\""
162 assert !s.include?(expected), "\"#{expected}\" found in \"#{s}\""
163 end
163 end
164
164
165 def assert_mail_body_match(expected, mail)
165 def assert_mail_body_match(expected, mail)
166 if expected.is_a?(String)
166 if expected.is_a?(String)
167 assert_include expected, mail_body(mail)
167 assert_include expected, mail_body(mail)
168 else
168 else
169 assert_match expected, mail_body(mail)
169 assert_match expected, mail_body(mail)
170 end
170 end
171 end
171 end
172
172
173 def assert_mail_body_no_match(expected, mail)
173 def assert_mail_body_no_match(expected, mail)
174 if expected.is_a?(String)
174 if expected.is_a?(String)
175 assert_not_include expected, mail_body(mail)
175 assert_not_include expected, mail_body(mail)
176 else
176 else
177 assert_no_match expected, mail_body(mail)
177 assert_no_match expected, mail_body(mail)
178 end
178 end
179 end
179 end
180
180
181 def mail_body(mail)
181 def mail_body(mail)
182 mail.body
182 mail.body
183 end
183 end
184
184
185 # Shoulda macros
185 # Shoulda macros
186 def self.should_render_404
186 def self.should_render_404
187 should_respond_with :not_found
187 should_respond_with :not_found
188 should_render_template 'common/error'
188 should_render_template 'common/error'
189 end
189 end
190
190
191 def self.should_have_before_filter(expected_method, options = {})
191 def self.should_have_before_filter(expected_method, options = {})
192 should_have_filter('before', expected_method, options)
192 should_have_filter('before', expected_method, options)
193 end
193 end
194
194
195 def self.should_have_after_filter(expected_method, options = {})
195 def self.should_have_after_filter(expected_method, options = {})
196 should_have_filter('after', expected_method, options)
196 should_have_filter('after', expected_method, options)
197 end
197 end
198
198
199 def self.should_have_filter(filter_type, expected_method, options)
199 def self.should_have_filter(filter_type, expected_method, options)
200 description = "have #{filter_type}_filter :#{expected_method}"
200 description = "have #{filter_type}_filter :#{expected_method}"
201 description << " with #{options.inspect}" unless options.empty?
201 description << " with #{options.inspect}" unless options.empty?
202
202
203 should description do
203 should description do
204 klass = "action_controller/filters/#{filter_type}_filter".classify.constantize
204 klass = "action_controller/filters/#{filter_type}_filter".classify.constantize
205 expected = klass.new(:filter, expected_method.to_sym, options)
205 expected = klass.new(:filter, expected_method.to_sym, options)
206 assert_equal 1, @controller.class.filter_chain.select { |filter|
206 assert_equal 1, @controller.class.filter_chain.select { |filter|
207 filter.method == expected.method && filter.kind == expected.kind &&
207 filter.method == expected.method && filter.kind == expected.kind &&
208 filter.options == expected.options && filter.class == expected.class
208 filter.options == expected.options && filter.class == expected.class
209 }.size
209 }.size
210 end
210 end
211 end
211 end
212
212
213 def self.should_show_the_old_and_new_values_for(prop_key, model, &block)
214 context "" do
215 setup do
216 if block_given?
217 instance_eval &block
218 else
219 @old_value = model.generate!
220 @new_value = model.generate!
221 end
222 end
223
224 should "use the new value's name" do
225 @detail = JournalDetail.generate!(:property => 'attr',
226 :old_value => @old_value.id,
227 :value => @new_value.id,
228 :prop_key => prop_key)
229
230 assert_match @new_value.name, show_detail(@detail, true)
231 end
232
233 should "use the old value's name" do
234 @detail = JournalDetail.generate!(:property => 'attr',
235 :old_value => @old_value.id,
236 :value => @new_value.id,
237 :prop_key => prop_key)
238
239 assert_match @old_value.name, show_detail(@detail, true)
240 end
241 end
242 end
243
244 # Test that a request allows the three types of API authentication
213 # Test that a request allows the three types of API authentication
245 #
214 #
246 # * HTTP Basic with username and password
215 # * HTTP Basic with username and password
247 # * HTTP Basic with an api key for the username
216 # * HTTP Basic with an api key for the username
248 # * Key based with the key=X parameter
217 # * Key based with the key=X parameter
249 #
218 #
250 # @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete)
219 # @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete)
251 # @param [String] url the request url
220 # @param [String] url the request url
252 # @param [optional, Hash] parameters additional request parameters
221 # @param [optional, Hash] parameters additional request parameters
253 # @param [optional, Hash] options additional options
222 # @param [optional, Hash] options additional options
254 # @option options [Symbol] :success_code Successful response code (:success)
223 # @option options [Symbol] :success_code Successful response code (:success)
255 # @option options [Symbol] :failure_code Failure response code (:unauthorized)
224 # @option options [Symbol] :failure_code Failure response code (:unauthorized)
256 def self.should_allow_api_authentication(http_method, url, parameters={}, options={})
225 def self.should_allow_api_authentication(http_method, url, parameters={}, options={})
257 should_allow_http_basic_auth_with_username_and_password(http_method, url, parameters, options)
226 should_allow_http_basic_auth_with_username_and_password(http_method, url, parameters, options)
258 should_allow_http_basic_auth_with_key(http_method, url, parameters, options)
227 should_allow_http_basic_auth_with_key(http_method, url, parameters, options)
259 should_allow_key_based_auth(http_method, url, parameters, options)
228 should_allow_key_based_auth(http_method, url, parameters, options)
260 end
229 end
261
230
262 # Test that a request allows the username and password for HTTP BASIC
231 # Test that a request allows the username and password for HTTP BASIC
263 #
232 #
264 # @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete)
233 # @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete)
265 # @param [String] url the request url
234 # @param [String] url the request url
266 # @param [optional, Hash] parameters additional request parameters
235 # @param [optional, Hash] parameters additional request parameters
267 # @param [optional, Hash] options additional options
236 # @param [optional, Hash] options additional options
268 # @option options [Symbol] :success_code Successful response code (:success)
237 # @option options [Symbol] :success_code Successful response code (:success)
269 # @option options [Symbol] :failure_code Failure response code (:unauthorized)
238 # @option options [Symbol] :failure_code Failure response code (:unauthorized)
270 def self.should_allow_http_basic_auth_with_username_and_password(http_method, url, parameters={}, options={})
239 def self.should_allow_http_basic_auth_with_username_and_password(http_method, url, parameters={}, options={})
271 success_code = options[:success_code] || :success
240 success_code = options[:success_code] || :success
272 failure_code = options[:failure_code] || :unauthorized
241 failure_code = options[:failure_code] || :unauthorized
273
242
274 context "should allow http basic auth using a username and password for #{http_method} #{url}" do
243 context "should allow http basic auth using a username and password for #{http_method} #{url}" do
275 context "with a valid HTTP authentication" do
244 context "with a valid HTTP authentication" do
276 setup do
245 setup do
277 @user = User.generate_with_protected!(:password => 'my_password', :password_confirmation => 'my_password', :admin => true) # Admin so they can access the project
246 @user = User.generate_with_protected!(:password => 'my_password', :password_confirmation => 'my_password', :admin => true) # Admin so they can access the project
278 send(http_method, url, parameters, credentials(@user.login, 'my_password'))
247 send(http_method, url, parameters, credentials(@user.login, 'my_password'))
279 end
248 end
280
249
281 should_respond_with success_code
250 should_respond_with success_code
282 should_respond_with_content_type_based_on_url(url)
251 should_respond_with_content_type_based_on_url(url)
283 should "login as the user" do
252 should "login as the user" do
284 assert_equal @user, User.current
253 assert_equal @user, User.current
285 end
254 end
286 end
255 end
287
256
288 context "with an invalid HTTP authentication" do
257 context "with an invalid HTTP authentication" do
289 setup do
258 setup do
290 @user = User.generate_with_protected!
259 @user = User.generate_with_protected!
291 send(http_method, url, parameters, credentials(@user.login, 'wrong_password'))
260 send(http_method, url, parameters, credentials(@user.login, 'wrong_password'))
292 end
261 end
293
262
294 should_respond_with failure_code
263 should_respond_with failure_code
295 should_respond_with_content_type_based_on_url(url)
264 should_respond_with_content_type_based_on_url(url)
296 should "not login as the user" do
265 should "not login as the user" do
297 assert_equal User.anonymous, User.current
266 assert_equal User.anonymous, User.current
298 end
267 end
299 end
268 end
300
269
301 context "without credentials" do
270 context "without credentials" do
302 setup do
271 setup do
303 send(http_method, url, parameters)
272 send(http_method, url, parameters)
304 end
273 end
305
274
306 should_respond_with failure_code
275 should_respond_with failure_code
307 should_respond_with_content_type_based_on_url(url)
276 should_respond_with_content_type_based_on_url(url)
308 should "include_www_authenticate_header" do
277 should "include_www_authenticate_header" do
309 assert @controller.response.headers.has_key?('WWW-Authenticate')
278 assert @controller.response.headers.has_key?('WWW-Authenticate')
310 end
279 end
311 end
280 end
312 end
281 end
313
282
314 end
283 end
315
284
316 # Test that a request allows the API key with HTTP BASIC
285 # Test that a request allows the API key with HTTP BASIC
317 #
286 #
318 # @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete)
287 # @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete)
319 # @param [String] url the request url
288 # @param [String] url the request url
320 # @param [optional, Hash] parameters additional request parameters
289 # @param [optional, Hash] parameters additional request parameters
321 # @param [optional, Hash] options additional options
290 # @param [optional, Hash] options additional options
322 # @option options [Symbol] :success_code Successful response code (:success)
291 # @option options [Symbol] :success_code Successful response code (:success)
323 # @option options [Symbol] :failure_code Failure response code (:unauthorized)
292 # @option options [Symbol] :failure_code Failure response code (:unauthorized)
324 def self.should_allow_http_basic_auth_with_key(http_method, url, parameters={}, options={})
293 def self.should_allow_http_basic_auth_with_key(http_method, url, parameters={}, options={})
325 success_code = options[:success_code] || :success
294 success_code = options[:success_code] || :success
326 failure_code = options[:failure_code] || :unauthorized
295 failure_code = options[:failure_code] || :unauthorized
327
296
328 context "should allow http basic auth with a key for #{http_method} #{url}" do
297 context "should allow http basic auth with a key for #{http_method} #{url}" do
329 context "with a valid HTTP authentication using the API token" do
298 context "with a valid HTTP authentication using the API token" do
330 setup do
299 setup do
331 @user = User.generate_with_protected!(:admin => true)
300 @user = User.generate_with_protected!(:admin => true)
332 @token = Token.generate!(:user => @user, :action => 'api')
301 @token = Token.generate!(:user => @user, :action => 'api')
333 send(http_method, url, parameters, credentials(@token.value, 'X'))
302 send(http_method, url, parameters, credentials(@token.value, 'X'))
334 end
303 end
335
304
336 should_respond_with success_code
305 should_respond_with success_code
337 should_respond_with_content_type_based_on_url(url)
306 should_respond_with_content_type_based_on_url(url)
338 should_be_a_valid_response_string_based_on_url(url)
307 should_be_a_valid_response_string_based_on_url(url)
339 should "login as the user" do
308 should "login as the user" do
340 assert_equal @user, User.current
309 assert_equal @user, User.current
341 end
310 end
342 end
311 end
343
312
344 context "with an invalid HTTP authentication" do
313 context "with an invalid HTTP authentication" do
345 setup do
314 setup do
346 @user = User.generate_with_protected!
315 @user = User.generate_with_protected!
347 @token = Token.generate!(:user => @user, :action => 'feeds')
316 @token = Token.generate!(:user => @user, :action => 'feeds')
348 send(http_method, url, parameters, credentials(@token.value, 'X'))
317 send(http_method, url, parameters, credentials(@token.value, 'X'))
349 end
318 end
350
319
351 should_respond_with failure_code
320 should_respond_with failure_code
352 should_respond_with_content_type_based_on_url(url)
321 should_respond_with_content_type_based_on_url(url)
353 should "not login as the user" do
322 should "not login as the user" do
354 assert_equal User.anonymous, User.current
323 assert_equal User.anonymous, User.current
355 end
324 end
356 end
325 end
357 end
326 end
358 end
327 end
359
328
360 # Test that a request allows full key authentication
329 # Test that a request allows full key authentication
361 #
330 #
362 # @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete)
331 # @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete)
363 # @param [String] url the request url, without the key=ZXY parameter
332 # @param [String] url the request url, without the key=ZXY parameter
364 # @param [optional, Hash] parameters additional request parameters
333 # @param [optional, Hash] parameters additional request parameters
365 # @param [optional, Hash] options additional options
334 # @param [optional, Hash] options additional options
366 # @option options [Symbol] :success_code Successful response code (:success)
335 # @option options [Symbol] :success_code Successful response code (:success)
367 # @option options [Symbol] :failure_code Failure response code (:unauthorized)
336 # @option options [Symbol] :failure_code Failure response code (:unauthorized)
368 def self.should_allow_key_based_auth(http_method, url, parameters={}, options={})
337 def self.should_allow_key_based_auth(http_method, url, parameters={}, options={})
369 success_code = options[:success_code] || :success
338 success_code = options[:success_code] || :success
370 failure_code = options[:failure_code] || :unauthorized
339 failure_code = options[:failure_code] || :unauthorized
371
340
372 context "should allow key based auth using key=X for #{http_method} #{url}" do
341 context "should allow key based auth using key=X for #{http_method} #{url}" do
373 context "with a valid api token" do
342 context "with a valid api token" do
374 setup do
343 setup do
375 @user = User.generate_with_protected!(:admin => true)
344 @user = User.generate_with_protected!(:admin => true)
376 @token = Token.generate!(:user => @user, :action => 'api')
345 @token = Token.generate!(:user => @user, :action => 'api')
377 # Simple url parse to add on ?key= or &key=
346 # Simple url parse to add on ?key= or &key=
378 request_url = if url.match(/\?/)
347 request_url = if url.match(/\?/)
379 url + "&key=#{@token.value}"
348 url + "&key=#{@token.value}"
380 else
349 else
381 url + "?key=#{@token.value}"
350 url + "?key=#{@token.value}"
382 end
351 end
383 send(http_method, request_url, parameters)
352 send(http_method, request_url, parameters)
384 end
353 end
385
354
386 should_respond_with success_code
355 should_respond_with success_code
387 should_respond_with_content_type_based_on_url(url)
356 should_respond_with_content_type_based_on_url(url)
388 should_be_a_valid_response_string_based_on_url(url)
357 should_be_a_valid_response_string_based_on_url(url)
389 should "login as the user" do
358 should "login as the user" do
390 assert_equal @user, User.current
359 assert_equal @user, User.current
391 end
360 end
392 end
361 end
393
362
394 context "with an invalid api token" do
363 context "with an invalid api token" do
395 setup do
364 setup do
396 @user = User.generate_with_protected!
365 @user = User.generate_with_protected!
397 @token = Token.generate!(:user => @user, :action => 'feeds')
366 @token = Token.generate!(:user => @user, :action => 'feeds')
398 # Simple url parse to add on ?key= or &key=
367 # Simple url parse to add on ?key= or &key=
399 request_url = if url.match(/\?/)
368 request_url = if url.match(/\?/)
400 url + "&key=#{@token.value}"
369 url + "&key=#{@token.value}"
401 else
370 else
402 url + "?key=#{@token.value}"
371 url + "?key=#{@token.value}"
403 end
372 end
404 send(http_method, request_url, parameters)
373 send(http_method, request_url, parameters)
405 end
374 end
406
375
407 should_respond_with failure_code
376 should_respond_with failure_code
408 should_respond_with_content_type_based_on_url(url)
377 should_respond_with_content_type_based_on_url(url)
409 should "not login as the user" do
378 should "not login as the user" do
410 assert_equal User.anonymous, User.current
379 assert_equal User.anonymous, User.current
411 end
380 end
412 end
381 end
413 end
382 end
414
383
415 context "should allow key based auth using X-Redmine-API-Key header for #{http_method} #{url}" do
384 context "should allow key based auth using X-Redmine-API-Key header for #{http_method} #{url}" do
416 setup do
385 setup do
417 @user = User.generate_with_protected!(:admin => true)
386 @user = User.generate_with_protected!(:admin => true)
418 @token = Token.generate!(:user => @user, :action => 'api')
387 @token = Token.generate!(:user => @user, :action => 'api')
419 send(http_method, url, parameters, {'X-Redmine-API-Key' => @token.value.to_s})
388 send(http_method, url, parameters, {'X-Redmine-API-Key' => @token.value.to_s})
420 end
389 end
421
390
422 should_respond_with success_code
391 should_respond_with success_code
423 should_respond_with_content_type_based_on_url(url)
392 should_respond_with_content_type_based_on_url(url)
424 should_be_a_valid_response_string_based_on_url(url)
393 should_be_a_valid_response_string_based_on_url(url)
425 should "login as the user" do
394 should "login as the user" do
426 assert_equal @user, User.current
395 assert_equal @user, User.current
427 end
396 end
428 end
397 end
429 end
398 end
430
399
431 # Uses should_respond_with_content_type based on what's in the url:
400 # Uses should_respond_with_content_type based on what's in the url:
432 #
401 #
433 # '/project/issues.xml' => should_respond_with_content_type :xml
402 # '/project/issues.xml' => should_respond_with_content_type :xml
434 # '/project/issues.json' => should_respond_with_content_type :json
403 # '/project/issues.json' => should_respond_with_content_type :json
435 #
404 #
436 # @param [String] url Request
405 # @param [String] url Request
437 def self.should_respond_with_content_type_based_on_url(url)
406 def self.should_respond_with_content_type_based_on_url(url)
438 case
407 case
439 when url.match(/xml/i)
408 when url.match(/xml/i)
440 should_respond_with_content_type :xml
409 should_respond_with_content_type :xml
441 when url.match(/json/i)
410 when url.match(/json/i)
442 should_respond_with_content_type :json
411 should_respond_with_content_type :json
443 else
412 else
444 raise "Unknown content type for should_respond_with_content_type_based_on_url: #{url}"
413 raise "Unknown content type for should_respond_with_content_type_based_on_url: #{url}"
445 end
414 end
446
415
447 end
416 end
448
417
449 # Uses the url to assert which format the response should be in
418 # Uses the url to assert which format the response should be in
450 #
419 #
451 # '/project/issues.xml' => should_be_a_valid_xml_string
420 # '/project/issues.xml' => should_be_a_valid_xml_string
452 # '/project/issues.json' => should_be_a_valid_json_string
421 # '/project/issues.json' => should_be_a_valid_json_string
453 #
422 #
454 # @param [String] url Request
423 # @param [String] url Request
455 def self.should_be_a_valid_response_string_based_on_url(url)
424 def self.should_be_a_valid_response_string_based_on_url(url)
456 case
425 case
457 when url.match(/xml/i)
426 when url.match(/xml/i)
458 should_be_a_valid_xml_string
427 should_be_a_valid_xml_string
459 when url.match(/json/i)
428 when url.match(/json/i)
460 should_be_a_valid_json_string
429 should_be_a_valid_json_string
461 else
430 else
462 raise "Unknown content type for should_be_a_valid_response_based_on_url: #{url}"
431 raise "Unknown content type for should_be_a_valid_response_based_on_url: #{url}"
463 end
432 end
464
433
465 end
434 end
466
435
467 # Checks that the response is a valid JSON string
436 # Checks that the response is a valid JSON string
468 def self.should_be_a_valid_json_string
437 def self.should_be_a_valid_json_string
469 should "be a valid JSON string (or empty)" do
438 should "be a valid JSON string (or empty)" do
470 assert(response.body.blank? || ActiveSupport::JSON.decode(response.body))
439 assert(response.body.blank? || ActiveSupport::JSON.decode(response.body))
471 end
440 end
472 end
441 end
473
442
474 # Checks that the response is a valid XML string
443 # Checks that the response is a valid XML string
475 def self.should_be_a_valid_xml_string
444 def self.should_be_a_valid_xml_string
476 should "be a valid XML string" do
445 should "be a valid XML string" do
477 assert REXML::Document.new(response.body)
446 assert REXML::Document.new(response.body)
478 end
447 end
479 end
448 end
480
449
481 end
450 end
482
451
483 # Simple module to "namespace" all of the API tests
452 # Simple module to "namespace" all of the API tests
484 module ApiTest
453 module ApiTest
485 end
454 end
@@ -1,171 +1,198
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
2 # Copyright (C) 2006-2011 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 File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class IssuesHelperTest < ActionView::TestCase
20 class IssuesHelperTest < ActionView::TestCase
21 include ApplicationHelper
21 include ApplicationHelper
22 include IssuesHelper
22 include IssuesHelper
23 include CustomFieldsHelper
23 include ERB::Util
24 include ERB::Util
24
25
25 fixtures :projects, :trackers, :issue_statuses, :issues,
26 fixtures :projects, :trackers, :issue_statuses, :issues,
26 :enumerations, :users, :issue_categories,
27 :enumerations, :users, :issue_categories,
27 :projects_trackers,
28 :projects_trackers,
28 :roles,
29 :roles,
29 :member_roles,
30 :member_roles,
30 :members,
31 :members,
31 :enabled_modules,
32 :enabled_modules,
32 :workflows
33 :workflows,
34 :custom_fields,
35 :attachments
33
36
34 def setup
37 def setup
35 super
38 super
36 set_language_if_valid('en')
39 set_language_if_valid('en')
37 User.current = nil
40 User.current = nil
38 end
41 end
39
42
40 def test_issue_heading
43 def test_issue_heading
41 assert_equal "Bug #1", issue_heading(Issue.find(1))
44 assert_equal "Bug #1", issue_heading(Issue.find(1))
42 end
45 end
43
46
44 def test_issues_destroy_confirmation_message_with_one_root_issue
47 def test_issues_destroy_confirmation_message_with_one_root_issue
45 assert_equal l(:text_issues_destroy_confirmation), issues_destroy_confirmation_message(Issue.find(1))
48 assert_equal l(:text_issues_destroy_confirmation), issues_destroy_confirmation_message(Issue.find(1))
46 end
49 end
47
50
48 def test_issues_destroy_confirmation_message_with_an_arrayt_of_root_issues
51 def test_issues_destroy_confirmation_message_with_an_arrayt_of_root_issues
49 assert_equal l(:text_issues_destroy_confirmation), issues_destroy_confirmation_message(Issue.find([1, 2]))
52 assert_equal l(:text_issues_destroy_confirmation), issues_destroy_confirmation_message(Issue.find([1, 2]))
50 end
53 end
51
54
52 def test_issues_destroy_confirmation_message_with_one_parent_issue
55 def test_issues_destroy_confirmation_message_with_one_parent_issue
53 Issue.find(2).update_attribute :parent_issue_id, 1
56 Issue.find(2).update_attribute :parent_issue_id, 1
54 assert_equal l(:text_issues_destroy_confirmation) + "\n" + l(:text_issues_destroy_descendants_confirmation, :count => 1),
57 assert_equal l(:text_issues_destroy_confirmation) + "\n" + l(:text_issues_destroy_descendants_confirmation, :count => 1),
55 issues_destroy_confirmation_message(Issue.find(1))
58 issues_destroy_confirmation_message(Issue.find(1))
56 end
59 end
57
60
58 def test_issues_destroy_confirmation_message_with_one_parent_issue_and_its_child
61 def test_issues_destroy_confirmation_message_with_one_parent_issue_and_its_child
59 Issue.find(2).update_attribute :parent_issue_id, 1
62 Issue.find(2).update_attribute :parent_issue_id, 1
60 assert_equal l(:text_issues_destroy_confirmation), issues_destroy_confirmation_message(Issue.find([1, 2]))
63 assert_equal l(:text_issues_destroy_confirmation), issues_destroy_confirmation_message(Issue.find([1, 2]))
61 end
64 end
62
65
63 context "IssuesHelper#show_detail" do
66 context "IssuesHelper#show_detail" do
64 context "with no_html" do
67 context "with no_html" do
65 should 'show a changing attribute' do
68 should 'show a changing attribute' do
66 @detail = JournalDetail.new(:property => 'attr', :old_value => '40', :value => '100', :prop_key => 'done_ratio')
69 @detail = JournalDetail.new(:property => 'attr', :old_value => '40', :value => '100', :prop_key => 'done_ratio')
67 assert_equal "% Done changed from 40 to 100", show_detail(@detail, true)
70 assert_equal "% Done changed from 40 to 100", show_detail(@detail, true)
68 end
71 end
69
72
70 should 'show a new attribute' do
73 should 'show a new attribute' do
71 @detail = JournalDetail.new(:property => 'attr', :old_value => nil, :value => '100', :prop_key => 'done_ratio')
74 @detail = JournalDetail.new(:property => 'attr', :old_value => nil, :value => '100', :prop_key => 'done_ratio')
72 assert_equal "% Done set to 100", show_detail(@detail, true)
75 assert_equal "% Done set to 100", show_detail(@detail, true)
73 end
76 end
74
77
75 should 'show a deleted attribute' do
78 should 'show a deleted attribute' do
76 @detail = JournalDetail.new(:property => 'attr', :old_value => '50', :value => nil, :prop_key => 'done_ratio')
79 @detail = JournalDetail.new(:property => 'attr', :old_value => '50', :value => nil, :prop_key => 'done_ratio')
77 assert_equal "% Done deleted (50)", show_detail(@detail, true)
80 assert_equal "% Done deleted (50)", show_detail(@detail, true)
78 end
81 end
79 end
82 end
80
83
81 context "with html" do
84 context "with html" do
82 should 'show a changing attribute with HTML highlights' do
85 should 'show a changing attribute with HTML highlights' do
83 @detail = JournalDetail.new(:property => 'attr', :old_value => '40', :value => '100', :prop_key => 'done_ratio')
86 @detail = JournalDetail.new(:property => 'attr', :old_value => '40', :value => '100', :prop_key => 'done_ratio')
84 html = show_detail(@detail, false)
87 html = show_detail(@detail, false)
85
88
86 assert_include '<strong>% Done</strong>', html
89 assert_include '<strong>% Done</strong>', html
87 assert_include '<i>40</i>', html
90 assert_include '<i>40</i>', html
88 assert_include '<i>100</i>', html
91 assert_include '<i>100</i>', html
89 end
92 end
90
93
91 should 'show a new attribute with HTML highlights' do
94 should 'show a new attribute with HTML highlights' do
92 @detail = JournalDetail.new(:property => 'attr', :old_value => nil, :value => '100', :prop_key => 'done_ratio')
95 @detail = JournalDetail.new(:property => 'attr', :old_value => nil, :value => '100', :prop_key => 'done_ratio')
93 html = show_detail(@detail, false)
96 html = show_detail(@detail, false)
94
97
95 assert_include '<strong>% Done</strong>', html
98 assert_include '<strong>% Done</strong>', html
96 assert_include '<i>100</i>', html
99 assert_include '<i>100</i>', html
97 end
100 end
98
101
99 should 'show a deleted attribute with HTML highlights' do
102 should 'show a deleted attribute with HTML highlights' do
100 @detail = JournalDetail.new(:property => 'attr', :old_value => '50', :value => nil, :prop_key => 'done_ratio')
103 @detail = JournalDetail.new(:property => 'attr', :old_value => '50', :value => nil, :prop_key => 'done_ratio')
101 html = show_detail(@detail, false)
104 html = show_detail(@detail, false)
102
105
103 assert_include '<strong>% Done</strong>', html
106 assert_include '<strong>% Done</strong>', html
104 assert_include '<strike><i>50</i></strike>', html
107 assert_include '<strike><i>50</i></strike>', html
105 end
108 end
106 end
109 end
107
110
108 context "with a start_date attribute" do
111 context "with a start_date attribute" do
109 should "format the current date" do
112 should "format the current date" do
110 @detail = JournalDetail.new(:property => 'attr', :old_value => '2010-01-01', :value => '2010-01-31', :prop_key => 'start_date')
113 @detail = JournalDetail.new(:property => 'attr', :old_value => '2010-01-01', :value => '2010-01-31', :prop_key => 'start_date')
111 assert_match "01/31/2010", show_detail(@detail, true)
114 assert_match "01/31/2010", show_detail(@detail, true)
112 end
115 end
113
116
114 should "format the old date" do
117 should "format the old date" do
115 @detail = JournalDetail.new(:property => 'attr', :old_value => '2010-01-01', :value => '2010-01-31', :prop_key => 'start_date')
118 @detail = JournalDetail.new(:property => 'attr', :old_value => '2010-01-01', :value => '2010-01-31', :prop_key => 'start_date')
116 assert_match "01/01/2010", show_detail(@detail, true)
119 assert_match "01/01/2010", show_detail(@detail, true)
117 end
120 end
118 end
121 end
119
122
120 context "with a due_date attribute" do
123 context "with a due_date attribute" do
121 should "format the current date" do
124 should "format the current date" do
122 @detail = JournalDetail.new(:property => 'attr', :old_value => '2010-01-01', :value => '2010-01-31', :prop_key => 'due_date')
125 @detail = JournalDetail.new(:property => 'attr', :old_value => '2010-01-01', :value => '2010-01-31', :prop_key => 'due_date')
123 assert_match "01/31/2010", show_detail(@detail, true)
126 assert_match "01/31/2010", show_detail(@detail, true)
124 end
127 end
125
128
126 should "format the old date" do
129 should "format the old date" do
127 @detail = JournalDetail.new(:property => 'attr', :old_value => '2010-01-01', :value => '2010-01-31', :prop_key => 'due_date')
130 @detail = JournalDetail.new(:property => 'attr', :old_value => '2010-01-01', :value => '2010-01-31', :prop_key => 'due_date')
128 assert_match "01/01/2010", show_detail(@detail, true)
131 assert_match "01/01/2010", show_detail(@detail, true)
129 end
132 end
130 end
133 end
131
134
132 context "with a project attribute" do
135 should "show old and new values with a project attribute" do
133 should_show_the_old_and_new_values_for('project_id', Project)
136 detail = JournalDetail.generate!(:property => 'attr', :prop_key => 'project_id', :old_value => 1, :value => 2)
137 assert_match 'eCookbook', show_detail(detail, true)
138 assert_match 'OnlineStore', show_detail(detail, true)
134 end
139 end
135
140
136 context "with a issue status attribute" do
141 should "show old and new values with a issue status attribute" do
137 should_show_the_old_and_new_values_for('status_id', IssueStatus)
142 detail = JournalDetail.generate!(:property => 'attr', :prop_key => 'status_id', :old_value => 1, :value => 2)
143 assert_match 'New', show_detail(detail, true)
144 assert_match 'Assigned', show_detail(detail, true)
138 end
145 end
139
146
140 context "with a tracker attribute" do
147 should "show old and new values with a tracker attribute" do
141 should_show_the_old_and_new_values_for('tracker_id', Tracker)
148 detail = JournalDetail.generate!(:property => 'attr', :prop_key => 'tracker_id', :old_value => 1, :value => 2)
149 assert_match 'Bug', show_detail(detail, true)
150 assert_match 'Feature request', show_detail(detail, true)
142 end
151 end
143
152
144 context "with a assigned to attribute" do
153 should "show old and new values with a assigned to attribute" do
145 should_show_the_old_and_new_values_for('assigned_to_id', User)
154 detail = JournalDetail.generate!(:property => 'attr', :prop_key => 'assigned_to_id', :old_value => 1, :value => 2)
155 assert_match 'redMine Admin', show_detail(detail, true)
156 assert_match 'John Smith', show_detail(detail, true)
146 end
157 end
147
158
148 context "with a priority attribute" do
159 should "show old and new values with a priority attribute" do
149 should_show_the_old_and_new_values_for('priority_id', IssuePriority) do
160 detail = JournalDetail.generate!(:property => 'attr', :prop_key => 'priority_id', :old_value => 4, :value => 5)
150 @old_value = IssuePriority.generate!(:type => 'IssuePriority')
161 assert_match 'Low', show_detail(detail, true)
151 @new_value = IssuePriority.generate!(:type => 'IssuePriority')
162 assert_match 'Normal', show_detail(detail, true)
152 end
163 end
164
165 should "show old and new values with a category attribute" do
166 detail = JournalDetail.generate!(:property => 'attr', :prop_key => 'category_id', :old_value => 1, :value => 2)
167 assert_match 'Printing', show_detail(detail, true)
168 assert_match 'Recipes', show_detail(detail, true)
153 end
169 end
154
170
155 context "with a category attribute" do
171 should "show old and new values with a fixed version attribute" do
156 should_show_the_old_and_new_values_for('category_id', IssueCategory)
172 detail = JournalDetail.generate!(:property => 'attr', :prop_key => 'fixed_version_id', :old_value => 1, :value => 2)
173 assert_match '0.1', show_detail(detail, true)
174 assert_match '1.0', show_detail(detail, true)
157 end
175 end
158
176
159 context "with a fixed version attribute" do
177 should "show old and new values with a estimated hours attribute" do
160 should_show_the_old_and_new_values_for('fixed_version_id', Version)
178 detail = JournalDetail.generate!(:property => 'attr', :prop_key => 'estimated_hours', :old_value => '5', :value => '6.3')
179 assert_match '5.00', show_detail(detail, true)
180 assert_match '6.30', show_detail(detail, true)
161 end
181 end
162
182
163 context "with a estimated hours attribute" do
183 should "show old and new values with a custom field" do
164 should "format the time into two decimal places"
184 detail = JournalDetail.generate!(:property => 'cf', :prop_key => '1', :old_value => 'MySQL', :value => 'PostgreSQL')
165 should "format the old time into two decimal places"
185 assert_equal 'Database changed from MySQL to PostgreSQL', show_detail(detail, true)
166 end
186 end
167
187
168 should "test custom fields"
188 should "show added file" do
169 should "test attachments"
189 detail = JournalDetail.generate!(:property => 'attachment', :prop_key => '1', :old_value => nil, :value => 'error281.txt')
190 assert_match 'error281.txt', show_detail(detail, true)
191 end
192
193 should "show removed file" do
194 detail = JournalDetail.generate!(:property => 'attachment', :prop_key => '1', :old_value => 'error281.txt', :value => nil)
195 assert_match 'error281.txt', show_detail(detail, true)
196 end
170 end
197 end
171 end
198 end
General Comments 0
You need to be logged in to leave comments. Login now