@@ -1,481 +1,485 | |||||
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 |
|
167 | assert_include expected, mail_body(mail) | |
168 | else |
|
168 | else | |
169 |
assert_match expected, 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 |
|
175 | assert_not_include expected, mail_body(mail) | |
176 | else |
|
176 | else | |
177 |
assert_no_match expected, mail |
|
177 | assert_no_match expected, mail_body(mail) | |
178 | end |
|
178 | end | |
179 | end |
|
179 | end | |
180 |
|
180 | |||
|
181 | def mail_body(mail) | |||
|
182 | mail.body | |||
|
183 | end | |||
|
184 | ||||
181 | # Shoulda macros |
|
185 | # Shoulda macros | |
182 | def self.should_render_404 |
|
186 | def self.should_render_404 | |
183 | should_respond_with :not_found |
|
187 | should_respond_with :not_found | |
184 | should_render_template 'common/error' |
|
188 | should_render_template 'common/error' | |
185 | end |
|
189 | end | |
186 |
|
190 | |||
187 | def self.should_have_before_filter(expected_method, options = {}) |
|
191 | def self.should_have_before_filter(expected_method, options = {}) | |
188 | should_have_filter('before', expected_method, options) |
|
192 | should_have_filter('before', expected_method, options) | |
189 | end |
|
193 | end | |
190 |
|
194 | |||
191 | def self.should_have_after_filter(expected_method, options = {}) |
|
195 | def self.should_have_after_filter(expected_method, options = {}) | |
192 | should_have_filter('after', expected_method, options) |
|
196 | should_have_filter('after', expected_method, options) | |
193 | end |
|
197 | end | |
194 |
|
198 | |||
195 | def self.should_have_filter(filter_type, expected_method, options) |
|
199 | def self.should_have_filter(filter_type, expected_method, options) | |
196 | description = "have #{filter_type}_filter :#{expected_method}" |
|
200 | description = "have #{filter_type}_filter :#{expected_method}" | |
197 | description << " with #{options.inspect}" unless options.empty? |
|
201 | description << " with #{options.inspect}" unless options.empty? | |
198 |
|
202 | |||
199 | should description do |
|
203 | should description do | |
200 | klass = "action_controller/filters/#{filter_type}_filter".classify.constantize |
|
204 | klass = "action_controller/filters/#{filter_type}_filter".classify.constantize | |
201 | expected = klass.new(:filter, expected_method.to_sym, options) |
|
205 | expected = klass.new(:filter, expected_method.to_sym, options) | |
202 | assert_equal 1, @controller.class.filter_chain.select { |filter| |
|
206 | assert_equal 1, @controller.class.filter_chain.select { |filter| | |
203 | filter.method == expected.method && filter.kind == expected.kind && |
|
207 | filter.method == expected.method && filter.kind == expected.kind && | |
204 | filter.options == expected.options && filter.class == expected.class |
|
208 | filter.options == expected.options && filter.class == expected.class | |
205 | }.size |
|
209 | }.size | |
206 | end |
|
210 | end | |
207 | end |
|
211 | end | |
208 |
|
212 | |||
209 | def self.should_show_the_old_and_new_values_for(prop_key, model, &block) |
|
213 | def self.should_show_the_old_and_new_values_for(prop_key, model, &block) | |
210 | context "" do |
|
214 | context "" do | |
211 | setup do |
|
215 | setup do | |
212 | if block_given? |
|
216 | if block_given? | |
213 | instance_eval &block |
|
217 | instance_eval &block | |
214 | else |
|
218 | else | |
215 | @old_value = model.generate! |
|
219 | @old_value = model.generate! | |
216 | @new_value = model.generate! |
|
220 | @new_value = model.generate! | |
217 | end |
|
221 | end | |
218 | end |
|
222 | end | |
219 |
|
223 | |||
220 | should "use the new value's name" do |
|
224 | should "use the new value's name" do | |
221 | @detail = JournalDetail.generate!(:property => 'attr', |
|
225 | @detail = JournalDetail.generate!(:property => 'attr', | |
222 | :old_value => @old_value.id, |
|
226 | :old_value => @old_value.id, | |
223 | :value => @new_value.id, |
|
227 | :value => @new_value.id, | |
224 | :prop_key => prop_key) |
|
228 | :prop_key => prop_key) | |
225 |
|
229 | |||
226 | assert_match @new_value.name, show_detail(@detail, true) |
|
230 | assert_match @new_value.name, show_detail(@detail, true) | |
227 | end |
|
231 | end | |
228 |
|
232 | |||
229 | should "use the old value's name" do |
|
233 | should "use the old value's name" do | |
230 | @detail = JournalDetail.generate!(:property => 'attr', |
|
234 | @detail = JournalDetail.generate!(:property => 'attr', | |
231 | :old_value => @old_value.id, |
|
235 | :old_value => @old_value.id, | |
232 | :value => @new_value.id, |
|
236 | :value => @new_value.id, | |
233 | :prop_key => prop_key) |
|
237 | :prop_key => prop_key) | |
234 |
|
238 | |||
235 | assert_match @old_value.name, show_detail(@detail, true) |
|
239 | assert_match @old_value.name, show_detail(@detail, true) | |
236 | end |
|
240 | end | |
237 | end |
|
241 | end | |
238 | end |
|
242 | end | |
239 |
|
243 | |||
240 | # Test that a request allows the three types of API authentication |
|
244 | # Test that a request allows the three types of API authentication | |
241 | # |
|
245 | # | |
242 | # * HTTP Basic with username and password |
|
246 | # * HTTP Basic with username and password | |
243 | # * HTTP Basic with an api key for the username |
|
247 | # * HTTP Basic with an api key for the username | |
244 | # * Key based with the key=X parameter |
|
248 | # * Key based with the key=X parameter | |
245 | # |
|
249 | # | |
246 | # @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) | |
247 | # @param [String] url the request url |
|
251 | # @param [String] url the request url | |
248 | # @param [optional, Hash] parameters additional request parameters |
|
252 | # @param [optional, Hash] parameters additional request parameters | |
249 | # @param [optional, Hash] options additional options |
|
253 | # @param [optional, Hash] options additional options | |
250 | # @option options [Symbol] :success_code Successful response code (:success) |
|
254 | # @option options [Symbol] :success_code Successful response code (:success) | |
251 | # @option options [Symbol] :failure_code Failure response code (:unauthorized) |
|
255 | # @option options [Symbol] :failure_code Failure response code (:unauthorized) | |
252 | def self.should_allow_api_authentication(http_method, url, parameters={}, options={}) |
|
256 | def self.should_allow_api_authentication(http_method, url, parameters={}, options={}) | |
253 | 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) | |
254 | 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) | |
255 | should_allow_key_based_auth(http_method, url, parameters, options) |
|
259 | should_allow_key_based_auth(http_method, url, parameters, options) | |
256 | end |
|
260 | end | |
257 |
|
261 | |||
258 | # 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 | |
259 | # |
|
263 | # | |
260 | # @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) | |
261 | # @param [String] url the request url |
|
265 | # @param [String] url the request url | |
262 | # @param [optional, Hash] parameters additional request parameters |
|
266 | # @param [optional, Hash] parameters additional request parameters | |
263 | # @param [optional, Hash] options additional options |
|
267 | # @param [optional, Hash] options additional options | |
264 | # @option options [Symbol] :success_code Successful response code (:success) |
|
268 | # @option options [Symbol] :success_code Successful response code (:success) | |
265 | # @option options [Symbol] :failure_code Failure response code (:unauthorized) |
|
269 | # @option options [Symbol] :failure_code Failure response code (:unauthorized) | |
266 | 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={}) | |
267 | success_code = options[:success_code] || :success |
|
271 | success_code = options[:success_code] || :success | |
268 | failure_code = options[:failure_code] || :unauthorized |
|
272 | failure_code = options[:failure_code] || :unauthorized | |
269 |
|
273 | |||
270 | 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 | |
271 | context "with a valid HTTP authentication" do |
|
275 | context "with a valid HTTP authentication" do | |
272 | setup do |
|
276 | setup do | |
273 | @user = User.generate_with_protected!(:password => 'my_password', :password_confirmation => 'my_password', :admin => true) # Admin so they can access the project |
|
277 | @user = User.generate_with_protected!(:password => 'my_password', :password_confirmation => 'my_password', :admin => true) # Admin so they can access the project | |
274 | send(http_method, url, parameters, credentials(@user.login, 'my_password')) |
|
278 | send(http_method, url, parameters, credentials(@user.login, 'my_password')) | |
275 | end |
|
279 | end | |
276 |
|
280 | |||
277 | should_respond_with success_code |
|
281 | should_respond_with success_code | |
278 | should_respond_with_content_type_based_on_url(url) |
|
282 | should_respond_with_content_type_based_on_url(url) | |
279 | should "login as the user" do |
|
283 | should "login as the user" do | |
280 | assert_equal @user, User.current |
|
284 | assert_equal @user, User.current | |
281 | end |
|
285 | end | |
282 | end |
|
286 | end | |
283 |
|
287 | |||
284 | context "with an invalid HTTP authentication" do |
|
288 | context "with an invalid HTTP authentication" do | |
285 | setup do |
|
289 | setup do | |
286 | @user = User.generate_with_protected! |
|
290 | @user = User.generate_with_protected! | |
287 | send(http_method, url, parameters, credentials(@user.login, 'wrong_password')) |
|
291 | send(http_method, url, parameters, credentials(@user.login, 'wrong_password')) | |
288 | end |
|
292 | end | |
289 |
|
293 | |||
290 | should_respond_with failure_code |
|
294 | should_respond_with failure_code | |
291 | should_respond_with_content_type_based_on_url(url) |
|
295 | should_respond_with_content_type_based_on_url(url) | |
292 | should "not login as the user" do |
|
296 | should "not login as the user" do | |
293 | assert_equal User.anonymous, User.current |
|
297 | assert_equal User.anonymous, User.current | |
294 | end |
|
298 | end | |
295 | end |
|
299 | end | |
296 |
|
300 | |||
297 | context "without credentials" do |
|
301 | context "without credentials" do | |
298 | setup do |
|
302 | setup do | |
299 | send(http_method, url, parameters) |
|
303 | send(http_method, url, parameters) | |
300 | end |
|
304 | end | |
301 |
|
305 | |||
302 | should_respond_with failure_code |
|
306 | should_respond_with failure_code | |
303 | should_respond_with_content_type_based_on_url(url) |
|
307 | should_respond_with_content_type_based_on_url(url) | |
304 | should "include_www_authenticate_header" do |
|
308 | should "include_www_authenticate_header" do | |
305 | assert @controller.response.headers.has_key?('WWW-Authenticate') |
|
309 | assert @controller.response.headers.has_key?('WWW-Authenticate') | |
306 | end |
|
310 | end | |
307 | end |
|
311 | end | |
308 | end |
|
312 | end | |
309 |
|
313 | |||
310 | end |
|
314 | end | |
311 |
|
315 | |||
312 | # Test that a request allows the API key with HTTP BASIC |
|
316 | # Test that a request allows the API key with HTTP BASIC | |
313 | # |
|
317 | # | |
314 | # @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete) |
|
318 | # @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete) | |
315 | # @param [String] url the request url |
|
319 | # @param [String] url the request url | |
316 | # @param [optional, Hash] parameters additional request parameters |
|
320 | # @param [optional, Hash] parameters additional request parameters | |
317 | # @param [optional, Hash] options additional options |
|
321 | # @param [optional, Hash] options additional options | |
318 | # @option options [Symbol] :success_code Successful response code (:success) |
|
322 | # @option options [Symbol] :success_code Successful response code (:success) | |
319 | # @option options [Symbol] :failure_code Failure response code (:unauthorized) |
|
323 | # @option options [Symbol] :failure_code Failure response code (:unauthorized) | |
320 | def self.should_allow_http_basic_auth_with_key(http_method, url, parameters={}, options={}) |
|
324 | def self.should_allow_http_basic_auth_with_key(http_method, url, parameters={}, options={}) | |
321 | success_code = options[:success_code] || :success |
|
325 | success_code = options[:success_code] || :success | |
322 | failure_code = options[:failure_code] || :unauthorized |
|
326 | failure_code = options[:failure_code] || :unauthorized | |
323 |
|
327 | |||
324 | context "should allow http basic auth with a key for #{http_method} #{url}" do |
|
328 | context "should allow http basic auth with a key for #{http_method} #{url}" do | |
325 | context "with a valid HTTP authentication using the API token" do |
|
329 | context "with a valid HTTP authentication using the API token" do | |
326 | setup do |
|
330 | setup do | |
327 | @user = User.generate_with_protected!(:admin => true) |
|
331 | @user = User.generate_with_protected!(:admin => true) | |
328 | @token = Token.generate!(:user => @user, :action => 'api') |
|
332 | @token = Token.generate!(:user => @user, :action => 'api') | |
329 | send(http_method, url, parameters, credentials(@token.value, 'X')) |
|
333 | send(http_method, url, parameters, credentials(@token.value, 'X')) | |
330 | end |
|
334 | end | |
331 |
|
335 | |||
332 | should_respond_with success_code |
|
336 | should_respond_with success_code | |
333 | should_respond_with_content_type_based_on_url(url) |
|
337 | should_respond_with_content_type_based_on_url(url) | |
334 | should_be_a_valid_response_string_based_on_url(url) |
|
338 | should_be_a_valid_response_string_based_on_url(url) | |
335 | should "login as the user" do |
|
339 | should "login as the user" do | |
336 | assert_equal @user, User.current |
|
340 | assert_equal @user, User.current | |
337 | end |
|
341 | end | |
338 | end |
|
342 | end | |
339 |
|
343 | |||
340 | context "with an invalid HTTP authentication" do |
|
344 | context "with an invalid HTTP authentication" do | |
341 | setup do |
|
345 | setup do | |
342 | @user = User.generate_with_protected! |
|
346 | @user = User.generate_with_protected! | |
343 | @token = Token.generate!(:user => @user, :action => 'feeds') |
|
347 | @token = Token.generate!(:user => @user, :action => 'feeds') | |
344 | send(http_method, url, parameters, credentials(@token.value, 'X')) |
|
348 | send(http_method, url, parameters, credentials(@token.value, 'X')) | |
345 | end |
|
349 | end | |
346 |
|
350 | |||
347 | should_respond_with failure_code |
|
351 | should_respond_with failure_code | |
348 | should_respond_with_content_type_based_on_url(url) |
|
352 | should_respond_with_content_type_based_on_url(url) | |
349 | should "not login as the user" do |
|
353 | should "not login as the user" do | |
350 | assert_equal User.anonymous, User.current |
|
354 | assert_equal User.anonymous, User.current | |
351 | end |
|
355 | end | |
352 | end |
|
356 | end | |
353 | end |
|
357 | end | |
354 | end |
|
358 | end | |
355 |
|
359 | |||
356 | # Test that a request allows full key authentication |
|
360 | # Test that a request allows full key authentication | |
357 | # |
|
361 | # | |
358 | # @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete) |
|
362 | # @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete) | |
359 | # @param [String] url the request url, without the key=ZXY parameter |
|
363 | # @param [String] url the request url, without the key=ZXY parameter | |
360 | # @param [optional, Hash] parameters additional request parameters |
|
364 | # @param [optional, Hash] parameters additional request parameters | |
361 | # @param [optional, Hash] options additional options |
|
365 | # @param [optional, Hash] options additional options | |
362 | # @option options [Symbol] :success_code Successful response code (:success) |
|
366 | # @option options [Symbol] :success_code Successful response code (:success) | |
363 | # @option options [Symbol] :failure_code Failure response code (:unauthorized) |
|
367 | # @option options [Symbol] :failure_code Failure response code (:unauthorized) | |
364 | def self.should_allow_key_based_auth(http_method, url, parameters={}, options={}) |
|
368 | def self.should_allow_key_based_auth(http_method, url, parameters={}, options={}) | |
365 | success_code = options[:success_code] || :success |
|
369 | success_code = options[:success_code] || :success | |
366 | failure_code = options[:failure_code] || :unauthorized |
|
370 | failure_code = options[:failure_code] || :unauthorized | |
367 |
|
371 | |||
368 | context "should allow key based auth using key=X for #{http_method} #{url}" do |
|
372 | context "should allow key based auth using key=X for #{http_method} #{url}" do | |
369 | context "with a valid api token" do |
|
373 | context "with a valid api token" do | |
370 | setup do |
|
374 | setup do | |
371 | @user = User.generate_with_protected!(:admin => true) |
|
375 | @user = User.generate_with_protected!(:admin => true) | |
372 | @token = Token.generate!(:user => @user, :action => 'api') |
|
376 | @token = Token.generate!(:user => @user, :action => 'api') | |
373 | # Simple url parse to add on ?key= or &key= |
|
377 | # Simple url parse to add on ?key= or &key= | |
374 | request_url = if url.match(/\?/) |
|
378 | request_url = if url.match(/\?/) | |
375 | url + "&key=#{@token.value}" |
|
379 | url + "&key=#{@token.value}" | |
376 | else |
|
380 | else | |
377 | url + "?key=#{@token.value}" |
|
381 | url + "?key=#{@token.value}" | |
378 | end |
|
382 | end | |
379 | send(http_method, request_url, parameters) |
|
383 | send(http_method, request_url, parameters) | |
380 | end |
|
384 | end | |
381 |
|
385 | |||
382 | should_respond_with success_code |
|
386 | should_respond_with success_code | |
383 | should_respond_with_content_type_based_on_url(url) |
|
387 | should_respond_with_content_type_based_on_url(url) | |
384 | should_be_a_valid_response_string_based_on_url(url) |
|
388 | should_be_a_valid_response_string_based_on_url(url) | |
385 | should "login as the user" do |
|
389 | should "login as the user" do | |
386 | assert_equal @user, User.current |
|
390 | assert_equal @user, User.current | |
387 | end |
|
391 | end | |
388 | end |
|
392 | end | |
389 |
|
393 | |||
390 | context "with an invalid api token" do |
|
394 | context "with an invalid api token" do | |
391 | setup do |
|
395 | setup do | |
392 | @user = User.generate_with_protected! |
|
396 | @user = User.generate_with_protected! | |
393 | @token = Token.generate!(:user => @user, :action => 'feeds') |
|
397 | @token = Token.generate!(:user => @user, :action => 'feeds') | |
394 | # Simple url parse to add on ?key= or &key= |
|
398 | # Simple url parse to add on ?key= or &key= | |
395 | request_url = if url.match(/\?/) |
|
399 | request_url = if url.match(/\?/) | |
396 | url + "&key=#{@token.value}" |
|
400 | url + "&key=#{@token.value}" | |
397 | else |
|
401 | else | |
398 | url + "?key=#{@token.value}" |
|
402 | url + "?key=#{@token.value}" | |
399 | end |
|
403 | end | |
400 | send(http_method, request_url, parameters) |
|
404 | send(http_method, request_url, parameters) | |
401 | end |
|
405 | end | |
402 |
|
406 | |||
403 | should_respond_with failure_code |
|
407 | should_respond_with failure_code | |
404 | should_respond_with_content_type_based_on_url(url) |
|
408 | should_respond_with_content_type_based_on_url(url) | |
405 | should "not login as the user" do |
|
409 | should "not login as the user" do | |
406 | assert_equal User.anonymous, User.current |
|
410 | assert_equal User.anonymous, User.current | |
407 | end |
|
411 | end | |
408 | end |
|
412 | end | |
409 | end |
|
413 | end | |
410 |
|
414 | |||
411 | context "should allow key based auth using X-Redmine-API-Key header for #{http_method} #{url}" do |
|
415 | context "should allow key based auth using X-Redmine-API-Key header for #{http_method} #{url}" do | |
412 | setup do |
|
416 | setup do | |
413 | @user = User.generate_with_protected!(:admin => true) |
|
417 | @user = User.generate_with_protected!(:admin => true) | |
414 | @token = Token.generate!(:user => @user, :action => 'api') |
|
418 | @token = Token.generate!(:user => @user, :action => 'api') | |
415 | send(http_method, url, parameters, {'X-Redmine-API-Key' => @token.value.to_s}) |
|
419 | send(http_method, url, parameters, {'X-Redmine-API-Key' => @token.value.to_s}) | |
416 | end |
|
420 | end | |
417 |
|
421 | |||
418 | should_respond_with success_code |
|
422 | should_respond_with success_code | |
419 | should_respond_with_content_type_based_on_url(url) |
|
423 | should_respond_with_content_type_based_on_url(url) | |
420 | should_be_a_valid_response_string_based_on_url(url) |
|
424 | should_be_a_valid_response_string_based_on_url(url) | |
421 | should "login as the user" do |
|
425 | should "login as the user" do | |
422 | assert_equal @user, User.current |
|
426 | assert_equal @user, User.current | |
423 | end |
|
427 | end | |
424 | end |
|
428 | end | |
425 | end |
|
429 | end | |
426 |
|
430 | |||
427 | # Uses should_respond_with_content_type based on what's in the url: |
|
431 | # Uses should_respond_with_content_type based on what's in the url: | |
428 | # |
|
432 | # | |
429 | # '/project/issues.xml' => should_respond_with_content_type :xml |
|
433 | # '/project/issues.xml' => should_respond_with_content_type :xml | |
430 | # '/project/issues.json' => should_respond_with_content_type :json |
|
434 | # '/project/issues.json' => should_respond_with_content_type :json | |
431 | # |
|
435 | # | |
432 | # @param [String] url Request |
|
436 | # @param [String] url Request | |
433 | def self.should_respond_with_content_type_based_on_url(url) |
|
437 | def self.should_respond_with_content_type_based_on_url(url) | |
434 | case |
|
438 | case | |
435 | when url.match(/xml/i) |
|
439 | when url.match(/xml/i) | |
436 | should_respond_with_content_type :xml |
|
440 | should_respond_with_content_type :xml | |
437 | when url.match(/json/i) |
|
441 | when url.match(/json/i) | |
438 | should_respond_with_content_type :json |
|
442 | should_respond_with_content_type :json | |
439 | else |
|
443 | else | |
440 | raise "Unknown content type for should_respond_with_content_type_based_on_url: #{url}" |
|
444 | raise "Unknown content type for should_respond_with_content_type_based_on_url: #{url}" | |
441 | end |
|
445 | end | |
442 |
|
446 | |||
443 | end |
|
447 | end | |
444 |
|
448 | |||
445 | # Uses the url to assert which format the response should be in |
|
449 | # Uses the url to assert which format the response should be in | |
446 | # |
|
450 | # | |
447 | # '/project/issues.xml' => should_be_a_valid_xml_string |
|
451 | # '/project/issues.xml' => should_be_a_valid_xml_string | |
448 | # '/project/issues.json' => should_be_a_valid_json_string |
|
452 | # '/project/issues.json' => should_be_a_valid_json_string | |
449 | # |
|
453 | # | |
450 | # @param [String] url Request |
|
454 | # @param [String] url Request | |
451 | def self.should_be_a_valid_response_string_based_on_url(url) |
|
455 | def self.should_be_a_valid_response_string_based_on_url(url) | |
452 | case |
|
456 | case | |
453 | when url.match(/xml/i) |
|
457 | when url.match(/xml/i) | |
454 | should_be_a_valid_xml_string |
|
458 | should_be_a_valid_xml_string | |
455 | when url.match(/json/i) |
|
459 | when url.match(/json/i) | |
456 | should_be_a_valid_json_string |
|
460 | should_be_a_valid_json_string | |
457 | else |
|
461 | else | |
458 | raise "Unknown content type for should_be_a_valid_response_based_on_url: #{url}" |
|
462 | raise "Unknown content type for should_be_a_valid_response_based_on_url: #{url}" | |
459 | end |
|
463 | end | |
460 |
|
464 | |||
461 | end |
|
465 | end | |
462 |
|
466 | |||
463 | # Checks that the response is a valid JSON string |
|
467 | # Checks that the response is a valid JSON string | |
464 | def self.should_be_a_valid_json_string |
|
468 | def self.should_be_a_valid_json_string | |
465 | should "be a valid JSON string (or empty)" do |
|
469 | should "be a valid JSON string (or empty)" do | |
466 | assert(response.body.blank? || ActiveSupport::JSON.decode(response.body)) |
|
470 | assert(response.body.blank? || ActiveSupport::JSON.decode(response.body)) | |
467 | end |
|
471 | end | |
468 | end |
|
472 | end | |
469 |
|
473 | |||
470 | # Checks that the response is a valid XML string |
|
474 | # Checks that the response is a valid XML string | |
471 | def self.should_be_a_valid_xml_string |
|
475 | def self.should_be_a_valid_xml_string | |
472 | should "be a valid XML string" do |
|
476 | should "be a valid XML string" do | |
473 | assert REXML::Document.new(response.body) |
|
477 | assert REXML::Document.new(response.body) | |
474 | end |
|
478 | end | |
475 | end |
|
479 | end | |
476 |
|
480 | |||
477 | end |
|
481 | end | |
478 |
|
482 | |||
479 | # Simple module to "namespace" all of the API tests |
|
483 | # Simple module to "namespace" all of the API tests | |
480 | module ApiTest |
|
484 | module ApiTest | |
481 | end |
|
485 | end |
@@ -1,172 +1,172 | |||||
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 Redmine::Hook::ManagerTest < ActiveSupport::TestCase |
|
20 | class Redmine::Hook::ManagerTest < ActiveSupport::TestCase | |
21 |
|
21 | |||
22 | fixtures :issues |
|
22 | fixtures :issues | |
23 |
|
23 | |||
24 | # Some hooks that are manually registered in these tests |
|
24 | # Some hooks that are manually registered in these tests | |
25 | class TestHook < Redmine::Hook::ViewListener; end |
|
25 | class TestHook < Redmine::Hook::ViewListener; end | |
26 |
|
26 | |||
27 | class TestHook1 < TestHook |
|
27 | class TestHook1 < TestHook | |
28 | def view_layouts_base_html_head(context) |
|
28 | def view_layouts_base_html_head(context) | |
29 | 'Test hook 1 listener.' |
|
29 | 'Test hook 1 listener.' | |
30 | end |
|
30 | end | |
31 | end |
|
31 | end | |
32 |
|
32 | |||
33 | class TestHook2 < TestHook |
|
33 | class TestHook2 < TestHook | |
34 | def view_layouts_base_html_head(context) |
|
34 | def view_layouts_base_html_head(context) | |
35 | 'Test hook 2 listener.' |
|
35 | 'Test hook 2 listener.' | |
36 | end |
|
36 | end | |
37 | end |
|
37 | end | |
38 |
|
38 | |||
39 | class TestHook3 < TestHook |
|
39 | class TestHook3 < TestHook | |
40 | def view_layouts_base_html_head(context) |
|
40 | def view_layouts_base_html_head(context) | |
41 | "Context keys: #{context.keys.collect(&:to_s).sort.join(', ')}." |
|
41 | "Context keys: #{context.keys.collect(&:to_s).sort.join(', ')}." | |
42 | end |
|
42 | end | |
43 | end |
|
43 | end | |
44 |
|
44 | |||
45 | class TestLinkToHook < TestHook |
|
45 | class TestLinkToHook < TestHook | |
46 | def view_layouts_base_html_head(context) |
|
46 | def view_layouts_base_html_head(context) | |
47 | link_to('Issues', :controller => 'issues') |
|
47 | link_to('Issues', :controller => 'issues') | |
48 | end |
|
48 | end | |
49 | end |
|
49 | end | |
50 |
|
50 | |||
51 | class TestHookHelperController < ActionController::Base |
|
51 | class TestHookHelperController < ActionController::Base | |
52 | include Redmine::Hook::Helper |
|
52 | include Redmine::Hook::Helper | |
53 | end |
|
53 | end | |
54 |
|
54 | |||
55 | class TestHookHelperView < ActionView::Base |
|
55 | class TestHookHelperView < ActionView::Base | |
56 | include Redmine::Hook::Helper |
|
56 | include Redmine::Hook::Helper | |
57 | end |
|
57 | end | |
58 |
|
58 | |||
59 | Redmine::Hook.clear_listeners |
|
59 | Redmine::Hook.clear_listeners | |
60 |
|
60 | |||
61 | def setup |
|
61 | def setup | |
62 | @hook_module = Redmine::Hook |
|
62 | @hook_module = Redmine::Hook | |
63 | end |
|
63 | end | |
64 |
|
64 | |||
65 | def teardown |
|
65 | def teardown | |
66 | @hook_module.clear_listeners |
|
66 | @hook_module.clear_listeners | |
67 | end |
|
67 | end | |
68 |
|
68 | |||
69 | def test_clear_listeners |
|
69 | def test_clear_listeners | |
70 | assert_equal 0, @hook_module.hook_listeners(:view_layouts_base_html_head).size |
|
70 | assert_equal 0, @hook_module.hook_listeners(:view_layouts_base_html_head).size | |
71 | @hook_module.add_listener(TestHook1) |
|
71 | @hook_module.add_listener(TestHook1) | |
72 | @hook_module.add_listener(TestHook2) |
|
72 | @hook_module.add_listener(TestHook2) | |
73 | assert_equal 2, @hook_module.hook_listeners(:view_layouts_base_html_head).size |
|
73 | assert_equal 2, @hook_module.hook_listeners(:view_layouts_base_html_head).size | |
74 |
|
74 | |||
75 | @hook_module.clear_listeners |
|
75 | @hook_module.clear_listeners | |
76 | assert_equal 0, @hook_module.hook_listeners(:view_layouts_base_html_head).size |
|
76 | assert_equal 0, @hook_module.hook_listeners(:view_layouts_base_html_head).size | |
77 | end |
|
77 | end | |
78 |
|
78 | |||
79 | def test_add_listener |
|
79 | def test_add_listener | |
80 | assert_equal 0, @hook_module.hook_listeners(:view_layouts_base_html_head).size |
|
80 | assert_equal 0, @hook_module.hook_listeners(:view_layouts_base_html_head).size | |
81 | @hook_module.add_listener(TestHook1) |
|
81 | @hook_module.add_listener(TestHook1) | |
82 | assert_equal 1, @hook_module.hook_listeners(:view_layouts_base_html_head).size |
|
82 | assert_equal 1, @hook_module.hook_listeners(:view_layouts_base_html_head).size | |
83 | end |
|
83 | end | |
84 |
|
84 | |||
85 | def test_call_hook |
|
85 | def test_call_hook | |
86 | @hook_module.add_listener(TestHook1) |
|
86 | @hook_module.add_listener(TestHook1) | |
87 | assert_equal ['Test hook 1 listener.'], hook_helper.call_hook(:view_layouts_base_html_head) |
|
87 | assert_equal ['Test hook 1 listener.'], hook_helper.call_hook(:view_layouts_base_html_head) | |
88 | end |
|
88 | end | |
89 |
|
89 | |||
90 | def test_call_hook_with_context |
|
90 | def test_call_hook_with_context | |
91 | @hook_module.add_listener(TestHook3) |
|
91 | @hook_module.add_listener(TestHook3) | |
92 | assert_equal ['Context keys: bar, controller, foo, project, request.'], |
|
92 | assert_equal ['Context keys: bar, controller, foo, project, request.'], | |
93 | hook_helper.call_hook(:view_layouts_base_html_head, :foo => 1, :bar => 'a') |
|
93 | hook_helper.call_hook(:view_layouts_base_html_head, :foo => 1, :bar => 'a') | |
94 | end |
|
94 | end | |
95 |
|
95 | |||
96 | def test_call_hook_with_multiple_listeners |
|
96 | def test_call_hook_with_multiple_listeners | |
97 | @hook_module.add_listener(TestHook1) |
|
97 | @hook_module.add_listener(TestHook1) | |
98 | @hook_module.add_listener(TestHook2) |
|
98 | @hook_module.add_listener(TestHook2) | |
99 | assert_equal ['Test hook 1 listener.', 'Test hook 2 listener.'], hook_helper.call_hook(:view_layouts_base_html_head) |
|
99 | assert_equal ['Test hook 1 listener.', 'Test hook 2 listener.'], hook_helper.call_hook(:view_layouts_base_html_head) | |
100 | end |
|
100 | end | |
101 |
|
101 | |||
102 | # Context: Redmine::Hook::Helper.call_hook default_url |
|
102 | # Context: Redmine::Hook::Helper.call_hook default_url | |
103 | def test_call_hook_default_url_options |
|
103 | def test_call_hook_default_url_options | |
104 | @hook_module.add_listener(TestLinkToHook) |
|
104 | @hook_module.add_listener(TestLinkToHook) | |
105 |
|
105 | |||
106 | assert_equal ['<a href="/issues">Issues</a>'], hook_helper.call_hook(:view_layouts_base_html_head) |
|
106 | assert_equal ['<a href="/issues">Issues</a>'], hook_helper.call_hook(:view_layouts_base_html_head) | |
107 | end |
|
107 | end | |
108 |
|
108 | |||
109 | # Context: Redmine::Hook::Helper.call_hook |
|
109 | # Context: Redmine::Hook::Helper.call_hook | |
110 | def test_call_hook_with_project_added_to_context |
|
110 | def test_call_hook_with_project_added_to_context | |
111 | @hook_module.add_listener(TestHook3) |
|
111 | @hook_module.add_listener(TestHook3) | |
112 | assert_match /project/i, hook_helper.call_hook(:view_layouts_base_html_head)[0] |
|
112 | assert_match /project/i, hook_helper.call_hook(:view_layouts_base_html_head)[0] | |
113 | end |
|
113 | end | |
114 |
|
114 | |||
115 | def test_call_hook_from_controller_with_controller_added_to_context |
|
115 | def test_call_hook_from_controller_with_controller_added_to_context | |
116 | @hook_module.add_listener(TestHook3) |
|
116 | @hook_module.add_listener(TestHook3) | |
117 | assert_match /controller/i, hook_helper.call_hook(:view_layouts_base_html_head)[0] |
|
117 | assert_match /controller/i, hook_helper.call_hook(:view_layouts_base_html_head)[0] | |
118 | end |
|
118 | end | |
119 |
|
119 | |||
120 | def test_call_hook_from_controller_with_request_added_to_context |
|
120 | def test_call_hook_from_controller_with_request_added_to_context | |
121 | @hook_module.add_listener(TestHook3) |
|
121 | @hook_module.add_listener(TestHook3) | |
122 | assert_match /request/i, hook_helper.call_hook(:view_layouts_base_html_head)[0] |
|
122 | assert_match /request/i, hook_helper.call_hook(:view_layouts_base_html_head)[0] | |
123 | end |
|
123 | end | |
124 |
|
124 | |||
125 | def test_call_hook_from_view_with_project_added_to_context |
|
125 | def test_call_hook_from_view_with_project_added_to_context | |
126 | @hook_module.add_listener(TestHook3) |
|
126 | @hook_module.add_listener(TestHook3) | |
127 | assert_match /project/i, view_hook_helper.call_hook(:view_layouts_base_html_head) |
|
127 | assert_match /project/i, view_hook_helper.call_hook(:view_layouts_base_html_head) | |
128 | end |
|
128 | end | |
129 |
|
129 | |||
130 | def test_call_hook_from_view_with_controller_added_to_context |
|
130 | def test_call_hook_from_view_with_controller_added_to_context | |
131 | @hook_module.add_listener(TestHook3) |
|
131 | @hook_module.add_listener(TestHook3) | |
132 | assert_match /controller/i, view_hook_helper.call_hook(:view_layouts_base_html_head) |
|
132 | assert_match /controller/i, view_hook_helper.call_hook(:view_layouts_base_html_head) | |
133 | end |
|
133 | end | |
134 |
|
134 | |||
135 | def test_call_hook_from_view_with_request_added_to_context |
|
135 | def test_call_hook_from_view_with_request_added_to_context | |
136 | @hook_module.add_listener(TestHook3) |
|
136 | @hook_module.add_listener(TestHook3) | |
137 | assert_match /request/i, view_hook_helper.call_hook(:view_layouts_base_html_head) |
|
137 | assert_match /request/i, view_hook_helper.call_hook(:view_layouts_base_html_head) | |
138 | end |
|
138 | end | |
139 |
|
139 | |||
140 | def test_call_hook_from_view_should_join_responses_with_a_space |
|
140 | def test_call_hook_from_view_should_join_responses_with_a_space | |
141 | @hook_module.add_listener(TestHook1) |
|
141 | @hook_module.add_listener(TestHook1) | |
142 | @hook_module.add_listener(TestHook2) |
|
142 | @hook_module.add_listener(TestHook2) | |
143 | assert_equal 'Test hook 1 listener. Test hook 2 listener.', |
|
143 | assert_equal 'Test hook 1 listener. Test hook 2 listener.', | |
144 | view_hook_helper.call_hook(:view_layouts_base_html_head) |
|
144 | view_hook_helper.call_hook(:view_layouts_base_html_head) | |
145 | end |
|
145 | end | |
146 |
|
146 | |||
147 | def test_call_hook_should_not_change_the_default_url_for_email_notifications |
|
147 | def test_call_hook_should_not_change_the_default_url_for_email_notifications | |
148 | issue = Issue.find(1) |
|
148 | issue = Issue.find(1) | |
149 |
|
149 | |||
150 | ActionMailer::Base.deliveries.clear |
|
150 | ActionMailer::Base.deliveries.clear | |
151 | Mailer.deliver_issue_add(issue) |
|
151 | Mailer.deliver_issue_add(issue) | |
152 | mail = ActionMailer::Base.deliveries.last |
|
152 | mail = ActionMailer::Base.deliveries.last | |
153 |
|
153 | |||
154 | @hook_module.add_listener(TestLinkToHook) |
|
154 | @hook_module.add_listener(TestLinkToHook) | |
155 | hook_helper.call_hook(:view_layouts_base_html_head) |
|
155 | hook_helper.call_hook(:view_layouts_base_html_head) | |
156 |
|
156 | |||
157 | ActionMailer::Base.deliveries.clear |
|
157 | ActionMailer::Base.deliveries.clear | |
158 | Mailer.deliver_issue_add(issue) |
|
158 | Mailer.deliver_issue_add(issue) | |
159 | mail2 = ActionMailer::Base.deliveries.last |
|
159 | mail2 = ActionMailer::Base.deliveries.last | |
160 |
|
160 | |||
161 |
assert_equal mail |
|
161 | assert_equal mail_body(mail), mail_body(mail2) | |
162 | end |
|
162 | end | |
163 |
|
163 | |||
164 | def hook_helper |
|
164 | def hook_helper | |
165 | @hook_helper ||= TestHookHelperController.new |
|
165 | @hook_helper ||= TestHookHelperController.new | |
166 | end |
|
166 | end | |
167 |
|
167 | |||
168 | def view_hook_helper |
|
168 | def view_hook_helper | |
169 | @view_hook_helper ||= TestHookHelperView.new(Rails.root.to_s + '/app/views') |
|
169 | @view_hook_helper ||= TestHookHelperView.new(Rails.root.to_s + '/app/views') | |
170 | end |
|
170 | end | |
171 | end |
|
171 | end | |
172 |
|
172 |
@@ -1,607 +1,607 | |||||
1 | # encoding: utf-8 |
|
1 | # encoding: utf-8 | |
2 | # |
|
2 | # | |
3 | # Redmine - project management software |
|
3 | # Redmine - project management software | |
4 | # Copyright (C) 2006-2011 Jean-Philippe Lang |
|
4 | # Copyright (C) 2006-2011 Jean-Philippe Lang | |
5 | # |
|
5 | # | |
6 | # This program is free software; you can redistribute it and/or |
|
6 | # This program is free software; you can redistribute it and/or | |
7 | # modify it under the terms of the GNU General Public License |
|
7 | # modify it under the terms of the GNU General Public License | |
8 | # as published by the Free Software Foundation; either version 2 |
|
8 | # as published by the Free Software Foundation; either version 2 | |
9 | # of the License, or (at your option) any later version. |
|
9 | # of the License, or (at your option) any later version. | |
10 | # |
|
10 | # | |
11 | # This program is distributed in the hope that it will be useful, |
|
11 | # This program is distributed in the hope that it will be useful, | |
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | # GNU General Public License for more details. |
|
14 | # GNU General Public License for more details. | |
15 | # |
|
15 | # | |
16 | # You should have received a copy of the GNU General Public License |
|
16 | # You should have received a copy of the GNU General Public License | |
17 | # along with this program; if not, write to the Free Software |
|
17 | # along with this program; if not, write to the Free Software | |
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
19 |
|
19 | |||
20 | require File.expand_path('../../test_helper', __FILE__) |
|
20 | require File.expand_path('../../test_helper', __FILE__) | |
21 |
|
21 | |||
22 | class MailHandlerTest < ActiveSupport::TestCase |
|
22 | class MailHandlerTest < ActiveSupport::TestCase | |
23 | fixtures :users, :projects, :enabled_modules, :roles, |
|
23 | fixtures :users, :projects, :enabled_modules, :roles, | |
24 | :members, :member_roles, :users, |
|
24 | :members, :member_roles, :users, | |
25 | :issues, :issue_statuses, |
|
25 | :issues, :issue_statuses, | |
26 | :workflows, :trackers, :projects_trackers, |
|
26 | :workflows, :trackers, :projects_trackers, | |
27 | :versions, :enumerations, :issue_categories, |
|
27 | :versions, :enumerations, :issue_categories, | |
28 | :custom_fields, :custom_fields_trackers, :custom_fields_projects, |
|
28 | :custom_fields, :custom_fields_trackers, :custom_fields_projects, | |
29 | :boards, :messages |
|
29 | :boards, :messages | |
30 |
|
30 | |||
31 | FIXTURES_PATH = File.dirname(__FILE__) + '/../fixtures/mail_handler' |
|
31 | FIXTURES_PATH = File.dirname(__FILE__) + '/../fixtures/mail_handler' | |
32 |
|
32 | |||
33 | def setup |
|
33 | def setup | |
34 | ActionMailer::Base.deliveries.clear |
|
34 | ActionMailer::Base.deliveries.clear | |
35 | Setting.notified_events = Redmine::Notifiable.all.collect(&:name) |
|
35 | Setting.notified_events = Redmine::Notifiable.all.collect(&:name) | |
36 | end |
|
36 | end | |
37 |
|
37 | |||
38 | def test_add_issue |
|
38 | def test_add_issue | |
39 | ActionMailer::Base.deliveries.clear |
|
39 | ActionMailer::Base.deliveries.clear | |
40 | # This email contains: 'Project: onlinestore' |
|
40 | # This email contains: 'Project: onlinestore' | |
41 | issue = submit_email('ticket_on_given_project.eml') |
|
41 | issue = submit_email('ticket_on_given_project.eml') | |
42 | assert issue.is_a?(Issue) |
|
42 | assert issue.is_a?(Issue) | |
43 | assert !issue.new_record? |
|
43 | assert !issue.new_record? | |
44 | issue.reload |
|
44 | issue.reload | |
45 | assert_equal Project.find(2), issue.project |
|
45 | assert_equal Project.find(2), issue.project | |
46 | assert_equal issue.project.trackers.first, issue.tracker |
|
46 | assert_equal issue.project.trackers.first, issue.tracker | |
47 | assert_equal 'New ticket on a given project', issue.subject |
|
47 | assert_equal 'New ticket on a given project', issue.subject | |
48 | assert_equal User.find_by_login('jsmith'), issue.author |
|
48 | assert_equal User.find_by_login('jsmith'), issue.author | |
49 | assert_equal IssueStatus.find_by_name('Resolved'), issue.status |
|
49 | assert_equal IssueStatus.find_by_name('Resolved'), issue.status | |
50 | assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.') |
|
50 | assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.') | |
51 | assert_equal '2010-01-01', issue.start_date.to_s |
|
51 | assert_equal '2010-01-01', issue.start_date.to_s | |
52 | assert_equal '2010-12-31', issue.due_date.to_s |
|
52 | assert_equal '2010-12-31', issue.due_date.to_s | |
53 | assert_equal User.find_by_login('jsmith'), issue.assigned_to |
|
53 | assert_equal User.find_by_login('jsmith'), issue.assigned_to | |
54 | assert_equal Version.find_by_name('Alpha'), issue.fixed_version |
|
54 | assert_equal Version.find_by_name('Alpha'), issue.fixed_version | |
55 | assert_equal 2.5, issue.estimated_hours |
|
55 | assert_equal 2.5, issue.estimated_hours | |
56 | assert_equal 30, issue.done_ratio |
|
56 | assert_equal 30, issue.done_ratio | |
57 | assert_equal [issue.id, 1, 2], [issue.root_id, issue.lft, issue.rgt] |
|
57 | assert_equal [issue.id, 1, 2], [issue.root_id, issue.lft, issue.rgt] | |
58 | # keywords should be removed from the email body |
|
58 | # keywords should be removed from the email body | |
59 | assert !issue.description.match(/^Project:/i) |
|
59 | assert !issue.description.match(/^Project:/i) | |
60 | assert !issue.description.match(/^Status:/i) |
|
60 | assert !issue.description.match(/^Status:/i) | |
61 | assert !issue.description.match(/^Start Date:/i) |
|
61 | assert !issue.description.match(/^Start Date:/i) | |
62 | # Email notification should be sent |
|
62 | # Email notification should be sent | |
63 | mail = ActionMailer::Base.deliveries.last |
|
63 | mail = ActionMailer::Base.deliveries.last | |
64 | assert_not_nil mail |
|
64 | assert_not_nil mail | |
65 | assert mail.subject.include?('New ticket on a given project') |
|
65 | assert mail.subject.include?('New ticket on a given project') | |
66 | end |
|
66 | end | |
67 |
|
67 | |||
68 | def test_add_issue_with_default_tracker |
|
68 | def test_add_issue_with_default_tracker | |
69 | # This email contains: 'Project: onlinestore' |
|
69 | # This email contains: 'Project: onlinestore' | |
70 | issue = submit_email( |
|
70 | issue = submit_email( | |
71 | 'ticket_on_given_project.eml', |
|
71 | 'ticket_on_given_project.eml', | |
72 | :issue => {:tracker => 'Support request'} |
|
72 | :issue => {:tracker => 'Support request'} | |
73 | ) |
|
73 | ) | |
74 | assert issue.is_a?(Issue) |
|
74 | assert issue.is_a?(Issue) | |
75 | assert !issue.new_record? |
|
75 | assert !issue.new_record? | |
76 | issue.reload |
|
76 | issue.reload | |
77 | assert_equal 'Support request', issue.tracker.name |
|
77 | assert_equal 'Support request', issue.tracker.name | |
78 | end |
|
78 | end | |
79 |
|
79 | |||
80 | def test_add_issue_with_status |
|
80 | def test_add_issue_with_status | |
81 | # This email contains: 'Project: onlinestore' and 'Status: Resolved' |
|
81 | # This email contains: 'Project: onlinestore' and 'Status: Resolved' | |
82 | issue = submit_email('ticket_on_given_project.eml') |
|
82 | issue = submit_email('ticket_on_given_project.eml') | |
83 | assert issue.is_a?(Issue) |
|
83 | assert issue.is_a?(Issue) | |
84 | assert !issue.new_record? |
|
84 | assert !issue.new_record? | |
85 | issue.reload |
|
85 | issue.reload | |
86 | assert_equal Project.find(2), issue.project |
|
86 | assert_equal Project.find(2), issue.project | |
87 | assert_equal IssueStatus.find_by_name("Resolved"), issue.status |
|
87 | assert_equal IssueStatus.find_by_name("Resolved"), issue.status | |
88 | end |
|
88 | end | |
89 |
|
89 | |||
90 | def test_add_issue_with_attributes_override |
|
90 | def test_add_issue_with_attributes_override | |
91 | issue = submit_email( |
|
91 | issue = submit_email( | |
92 | 'ticket_with_attributes.eml', |
|
92 | 'ticket_with_attributes.eml', | |
93 | :allow_override => 'tracker,category,priority' |
|
93 | :allow_override => 'tracker,category,priority' | |
94 | ) |
|
94 | ) | |
95 | assert issue.is_a?(Issue) |
|
95 | assert issue.is_a?(Issue) | |
96 | assert !issue.new_record? |
|
96 | assert !issue.new_record? | |
97 | issue.reload |
|
97 | issue.reload | |
98 | assert_equal 'New ticket on a given project', issue.subject |
|
98 | assert_equal 'New ticket on a given project', issue.subject | |
99 | assert_equal User.find_by_login('jsmith'), issue.author |
|
99 | assert_equal User.find_by_login('jsmith'), issue.author | |
100 | assert_equal Project.find(2), issue.project |
|
100 | assert_equal Project.find(2), issue.project | |
101 | assert_equal 'Feature request', issue.tracker.to_s |
|
101 | assert_equal 'Feature request', issue.tracker.to_s | |
102 | assert_equal 'Stock management', issue.category.to_s |
|
102 | assert_equal 'Stock management', issue.category.to_s | |
103 | assert_equal 'Urgent', issue.priority.to_s |
|
103 | assert_equal 'Urgent', issue.priority.to_s | |
104 | assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.') |
|
104 | assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.') | |
105 | end |
|
105 | end | |
106 |
|
106 | |||
107 | def test_add_issue_with_group_assignment |
|
107 | def test_add_issue_with_group_assignment | |
108 | with_settings :issue_group_assignment => '1' do |
|
108 | with_settings :issue_group_assignment => '1' do | |
109 | issue = submit_email('ticket_on_given_project.eml') do |email| |
|
109 | issue = submit_email('ticket_on_given_project.eml') do |email| | |
110 | email.gsub!('Assigned to: John Smith', 'Assigned to: B Team') |
|
110 | email.gsub!('Assigned to: John Smith', 'Assigned to: B Team') | |
111 | end |
|
111 | end | |
112 | assert issue.is_a?(Issue) |
|
112 | assert issue.is_a?(Issue) | |
113 | assert !issue.new_record? |
|
113 | assert !issue.new_record? | |
114 | issue.reload |
|
114 | issue.reload | |
115 | assert_equal Group.find(11), issue.assigned_to |
|
115 | assert_equal Group.find(11), issue.assigned_to | |
116 | end |
|
116 | end | |
117 | end |
|
117 | end | |
118 |
|
118 | |||
119 | def test_add_issue_with_partial_attributes_override |
|
119 | def test_add_issue_with_partial_attributes_override | |
120 | issue = submit_email( |
|
120 | issue = submit_email( | |
121 | 'ticket_with_attributes.eml', |
|
121 | 'ticket_with_attributes.eml', | |
122 | :issue => {:priority => 'High'}, |
|
122 | :issue => {:priority => 'High'}, | |
123 | :allow_override => ['tracker'] |
|
123 | :allow_override => ['tracker'] | |
124 | ) |
|
124 | ) | |
125 | assert issue.is_a?(Issue) |
|
125 | assert issue.is_a?(Issue) | |
126 | assert !issue.new_record? |
|
126 | assert !issue.new_record? | |
127 | issue.reload |
|
127 | issue.reload | |
128 | assert_equal 'New ticket on a given project', issue.subject |
|
128 | assert_equal 'New ticket on a given project', issue.subject | |
129 | assert_equal User.find_by_login('jsmith'), issue.author |
|
129 | assert_equal User.find_by_login('jsmith'), issue.author | |
130 | assert_equal Project.find(2), issue.project |
|
130 | assert_equal Project.find(2), issue.project | |
131 | assert_equal 'Feature request', issue.tracker.to_s |
|
131 | assert_equal 'Feature request', issue.tracker.to_s | |
132 | assert_nil issue.category |
|
132 | assert_nil issue.category | |
133 | assert_equal 'High', issue.priority.to_s |
|
133 | assert_equal 'High', issue.priority.to_s | |
134 | assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.') |
|
134 | assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.') | |
135 | end |
|
135 | end | |
136 |
|
136 | |||
137 | def test_add_issue_with_spaces_between_attribute_and_separator |
|
137 | def test_add_issue_with_spaces_between_attribute_and_separator | |
138 | issue = submit_email( |
|
138 | issue = submit_email( | |
139 | 'ticket_with_spaces_between_attribute_and_separator.eml', |
|
139 | 'ticket_with_spaces_between_attribute_and_separator.eml', | |
140 | :allow_override => 'tracker,category,priority' |
|
140 | :allow_override => 'tracker,category,priority' | |
141 | ) |
|
141 | ) | |
142 | assert issue.is_a?(Issue) |
|
142 | assert issue.is_a?(Issue) | |
143 | assert !issue.new_record? |
|
143 | assert !issue.new_record? | |
144 | issue.reload |
|
144 | issue.reload | |
145 | assert_equal 'New ticket on a given project', issue.subject |
|
145 | assert_equal 'New ticket on a given project', issue.subject | |
146 | assert_equal User.find_by_login('jsmith'), issue.author |
|
146 | assert_equal User.find_by_login('jsmith'), issue.author | |
147 | assert_equal Project.find(2), issue.project |
|
147 | assert_equal Project.find(2), issue.project | |
148 | assert_equal 'Feature request', issue.tracker.to_s |
|
148 | assert_equal 'Feature request', issue.tracker.to_s | |
149 | assert_equal 'Stock management', issue.category.to_s |
|
149 | assert_equal 'Stock management', issue.category.to_s | |
150 | assert_equal 'Urgent', issue.priority.to_s |
|
150 | assert_equal 'Urgent', issue.priority.to_s | |
151 | assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.') |
|
151 | assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.') | |
152 | end |
|
152 | end | |
153 |
|
153 | |||
154 | def test_add_issue_with_attachment_to_specific_project |
|
154 | def test_add_issue_with_attachment_to_specific_project | |
155 | issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'}) |
|
155 | issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'}) | |
156 | assert issue.is_a?(Issue) |
|
156 | assert issue.is_a?(Issue) | |
157 | assert !issue.new_record? |
|
157 | assert !issue.new_record? | |
158 | issue.reload |
|
158 | issue.reload | |
159 | assert_equal 'Ticket created by email with attachment', issue.subject |
|
159 | assert_equal 'Ticket created by email with attachment', issue.subject | |
160 | assert_equal User.find_by_login('jsmith'), issue.author |
|
160 | assert_equal User.find_by_login('jsmith'), issue.author | |
161 | assert_equal Project.find(2), issue.project |
|
161 | assert_equal Project.find(2), issue.project | |
162 | assert_equal 'This is a new ticket with attachments', issue.description |
|
162 | assert_equal 'This is a new ticket with attachments', issue.description | |
163 | # Attachment properties |
|
163 | # Attachment properties | |
164 | assert_equal 1, issue.attachments.size |
|
164 | assert_equal 1, issue.attachments.size | |
165 | assert_equal 'Paella.jpg', issue.attachments.first.filename |
|
165 | assert_equal 'Paella.jpg', issue.attachments.first.filename | |
166 | assert_equal 'image/jpeg', issue.attachments.first.content_type |
|
166 | assert_equal 'image/jpeg', issue.attachments.first.content_type | |
167 | assert_equal 10790, issue.attachments.first.filesize |
|
167 | assert_equal 10790, issue.attachments.first.filesize | |
168 | end |
|
168 | end | |
169 |
|
169 | |||
170 | def test_add_issue_with_custom_fields |
|
170 | def test_add_issue_with_custom_fields | |
171 | issue = submit_email('ticket_with_custom_fields.eml', :issue => {:project => 'onlinestore'}) |
|
171 | issue = submit_email('ticket_with_custom_fields.eml', :issue => {:project => 'onlinestore'}) | |
172 | assert issue.is_a?(Issue) |
|
172 | assert issue.is_a?(Issue) | |
173 | assert !issue.new_record? |
|
173 | assert !issue.new_record? | |
174 | issue.reload |
|
174 | issue.reload | |
175 | assert_equal 'New ticket with custom field values', issue.subject |
|
175 | assert_equal 'New ticket with custom field values', issue.subject | |
176 | assert_equal 'Value for a custom field', |
|
176 | assert_equal 'Value for a custom field', | |
177 | issue.custom_value_for(CustomField.find_by_name('Searchable field')).value |
|
177 | issue.custom_value_for(CustomField.find_by_name('Searchable field')).value | |
178 | assert !issue.description.match(/^searchable field:/i) |
|
178 | assert !issue.description.match(/^searchable field:/i) | |
179 | end |
|
179 | end | |
180 |
|
180 | |||
181 | def test_add_issue_with_cc |
|
181 | def test_add_issue_with_cc | |
182 | issue = submit_email('ticket_with_cc.eml', :issue => {:project => 'ecookbook'}) |
|
182 | issue = submit_email('ticket_with_cc.eml', :issue => {:project => 'ecookbook'}) | |
183 | assert issue.is_a?(Issue) |
|
183 | assert issue.is_a?(Issue) | |
184 | assert !issue.new_record? |
|
184 | assert !issue.new_record? | |
185 | issue.reload |
|
185 | issue.reload | |
186 | assert issue.watched_by?(User.find_by_mail('dlopper@somenet.foo')) |
|
186 | assert issue.watched_by?(User.find_by_mail('dlopper@somenet.foo')) | |
187 | assert_equal 1, issue.watcher_user_ids.size |
|
187 | assert_equal 1, issue.watcher_user_ids.size | |
188 | end |
|
188 | end | |
189 |
|
189 | |||
190 | def test_add_issue_by_unknown_user |
|
190 | def test_add_issue_by_unknown_user | |
191 | assert_no_difference 'User.count' do |
|
191 | assert_no_difference 'User.count' do | |
192 | assert_equal false, |
|
192 | assert_equal false, | |
193 | submit_email( |
|
193 | submit_email( | |
194 | 'ticket_by_unknown_user.eml', |
|
194 | 'ticket_by_unknown_user.eml', | |
195 | :issue => {:project => 'ecookbook'} |
|
195 | :issue => {:project => 'ecookbook'} | |
196 | ) |
|
196 | ) | |
197 | end |
|
197 | end | |
198 | end |
|
198 | end | |
199 |
|
199 | |||
200 | def test_add_issue_by_anonymous_user |
|
200 | def test_add_issue_by_anonymous_user | |
201 | Role.anonymous.add_permission!(:add_issues) |
|
201 | Role.anonymous.add_permission!(:add_issues) | |
202 | assert_no_difference 'User.count' do |
|
202 | assert_no_difference 'User.count' do | |
203 | issue = submit_email( |
|
203 | issue = submit_email( | |
204 | 'ticket_by_unknown_user.eml', |
|
204 | 'ticket_by_unknown_user.eml', | |
205 | :issue => {:project => 'ecookbook'}, |
|
205 | :issue => {:project => 'ecookbook'}, | |
206 | :unknown_user => 'accept' |
|
206 | :unknown_user => 'accept' | |
207 | ) |
|
207 | ) | |
208 | assert issue.is_a?(Issue) |
|
208 | assert issue.is_a?(Issue) | |
209 | assert issue.author.anonymous? |
|
209 | assert issue.author.anonymous? | |
210 | end |
|
210 | end | |
211 | end |
|
211 | end | |
212 |
|
212 | |||
213 | def test_add_issue_by_anonymous_user_with_no_from_address |
|
213 | def test_add_issue_by_anonymous_user_with_no_from_address | |
214 | Role.anonymous.add_permission!(:add_issues) |
|
214 | Role.anonymous.add_permission!(:add_issues) | |
215 | assert_no_difference 'User.count' do |
|
215 | assert_no_difference 'User.count' do | |
216 | issue = submit_email( |
|
216 | issue = submit_email( | |
217 | 'ticket_by_empty_user.eml', |
|
217 | 'ticket_by_empty_user.eml', | |
218 | :issue => {:project => 'ecookbook'}, |
|
218 | :issue => {:project => 'ecookbook'}, | |
219 | :unknown_user => 'accept' |
|
219 | :unknown_user => 'accept' | |
220 | ) |
|
220 | ) | |
221 | assert issue.is_a?(Issue) |
|
221 | assert issue.is_a?(Issue) | |
222 | assert issue.author.anonymous? |
|
222 | assert issue.author.anonymous? | |
223 | end |
|
223 | end | |
224 | end |
|
224 | end | |
225 |
|
225 | |||
226 | def test_add_issue_by_anonymous_user_on_private_project |
|
226 | def test_add_issue_by_anonymous_user_on_private_project | |
227 | Role.anonymous.add_permission!(:add_issues) |
|
227 | Role.anonymous.add_permission!(:add_issues) | |
228 | assert_no_difference 'User.count' do |
|
228 | assert_no_difference 'User.count' do | |
229 | assert_no_difference 'Issue.count' do |
|
229 | assert_no_difference 'Issue.count' do | |
230 | assert_equal false, |
|
230 | assert_equal false, | |
231 | submit_email( |
|
231 | submit_email( | |
232 | 'ticket_by_unknown_user.eml', |
|
232 | 'ticket_by_unknown_user.eml', | |
233 | :issue => {:project => 'onlinestore'}, |
|
233 | :issue => {:project => 'onlinestore'}, | |
234 | :unknown_user => 'accept' |
|
234 | :unknown_user => 'accept' | |
235 | ) |
|
235 | ) | |
236 | end |
|
236 | end | |
237 | end |
|
237 | end | |
238 | end |
|
238 | end | |
239 |
|
239 | |||
240 | def test_add_issue_by_anonymous_user_on_private_project_without_permission_check |
|
240 | def test_add_issue_by_anonymous_user_on_private_project_without_permission_check | |
241 | assert_no_difference 'User.count' do |
|
241 | assert_no_difference 'User.count' do | |
242 | assert_difference 'Issue.count' do |
|
242 | assert_difference 'Issue.count' do | |
243 | issue = submit_email( |
|
243 | issue = submit_email( | |
244 | 'ticket_by_unknown_user.eml', |
|
244 | 'ticket_by_unknown_user.eml', | |
245 | :issue => {:project => 'onlinestore'}, |
|
245 | :issue => {:project => 'onlinestore'}, | |
246 | :no_permission_check => '1', |
|
246 | :no_permission_check => '1', | |
247 | :unknown_user => 'accept' |
|
247 | :unknown_user => 'accept' | |
248 | ) |
|
248 | ) | |
249 | assert issue.is_a?(Issue) |
|
249 | assert issue.is_a?(Issue) | |
250 | assert issue.author.anonymous? |
|
250 | assert issue.author.anonymous? | |
251 | assert !issue.project.is_public? |
|
251 | assert !issue.project.is_public? | |
252 | assert_equal [issue.id, 1, 2], [issue.root_id, issue.lft, issue.rgt] |
|
252 | assert_equal [issue.id, 1, 2], [issue.root_id, issue.lft, issue.rgt] | |
253 | end |
|
253 | end | |
254 | end |
|
254 | end | |
255 | end |
|
255 | end | |
256 |
|
256 | |||
257 | def test_add_issue_by_created_user |
|
257 | def test_add_issue_by_created_user | |
258 | Setting.default_language = 'en' |
|
258 | Setting.default_language = 'en' | |
259 | assert_difference 'User.count' do |
|
259 | assert_difference 'User.count' do | |
260 | issue = submit_email( |
|
260 | issue = submit_email( | |
261 | 'ticket_by_unknown_user.eml', |
|
261 | 'ticket_by_unknown_user.eml', | |
262 | :issue => {:project => 'ecookbook'}, |
|
262 | :issue => {:project => 'ecookbook'}, | |
263 | :unknown_user => 'create' |
|
263 | :unknown_user => 'create' | |
264 | ) |
|
264 | ) | |
265 | assert issue.is_a?(Issue) |
|
265 | assert issue.is_a?(Issue) | |
266 | assert issue.author.active? |
|
266 | assert issue.author.active? | |
267 | assert_equal 'john.doe@somenet.foo', issue.author.mail |
|
267 | assert_equal 'john.doe@somenet.foo', issue.author.mail | |
268 | assert_equal 'John', issue.author.firstname |
|
268 | assert_equal 'John', issue.author.firstname | |
269 | assert_equal 'Doe', issue.author.lastname |
|
269 | assert_equal 'Doe', issue.author.lastname | |
270 |
|
270 | |||
271 | # account information |
|
271 | # account information | |
272 | email = ActionMailer::Base.deliveries.first |
|
272 | email = ActionMailer::Base.deliveries.first | |
273 | assert_not_nil email |
|
273 | assert_not_nil email | |
274 | assert email.subject.include?('account activation') |
|
274 | assert email.subject.include?('account activation') | |
275 |
login = email |
|
275 | login = mail_body(email).match(/\* Login: (.*)$/)[1] | |
276 |
password = email |
|
276 | password = mail_body(email).match(/\* Password: (.*)$/)[1] | |
277 | assert_equal issue.author, User.try_to_login(login, password) |
|
277 | assert_equal issue.author, User.try_to_login(login, password) | |
278 | end |
|
278 | end | |
279 | end |
|
279 | end | |
280 |
|
280 | |||
281 | def test_add_issue_without_from_header |
|
281 | def test_add_issue_without_from_header | |
282 | Role.anonymous.add_permission!(:add_issues) |
|
282 | Role.anonymous.add_permission!(:add_issues) | |
283 | assert_equal false, submit_email('ticket_without_from_header.eml') |
|
283 | assert_equal false, submit_email('ticket_without_from_header.eml') | |
284 | end |
|
284 | end | |
285 |
|
285 | |||
286 | def test_add_issue_with_invalid_attributes |
|
286 | def test_add_issue_with_invalid_attributes | |
287 | issue = submit_email( |
|
287 | issue = submit_email( | |
288 | 'ticket_with_invalid_attributes.eml', |
|
288 | 'ticket_with_invalid_attributes.eml', | |
289 | :allow_override => 'tracker,category,priority' |
|
289 | :allow_override => 'tracker,category,priority' | |
290 | ) |
|
290 | ) | |
291 | assert issue.is_a?(Issue) |
|
291 | assert issue.is_a?(Issue) | |
292 | assert !issue.new_record? |
|
292 | assert !issue.new_record? | |
293 | issue.reload |
|
293 | issue.reload | |
294 | assert_nil issue.assigned_to |
|
294 | assert_nil issue.assigned_to | |
295 | assert_nil issue.start_date |
|
295 | assert_nil issue.start_date | |
296 | assert_nil issue.due_date |
|
296 | assert_nil issue.due_date | |
297 | assert_equal 0, issue.done_ratio |
|
297 | assert_equal 0, issue.done_ratio | |
298 | assert_equal 'Normal', issue.priority.to_s |
|
298 | assert_equal 'Normal', issue.priority.to_s | |
299 | assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.') |
|
299 | assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.') | |
300 | end |
|
300 | end | |
301 |
|
301 | |||
302 | def test_add_issue_with_localized_attributes |
|
302 | def test_add_issue_with_localized_attributes | |
303 | User.find_by_mail('jsmith@somenet.foo').update_attribute 'language', 'fr' |
|
303 | User.find_by_mail('jsmith@somenet.foo').update_attribute 'language', 'fr' | |
304 | issue = submit_email( |
|
304 | issue = submit_email( | |
305 | 'ticket_with_localized_attributes.eml', |
|
305 | 'ticket_with_localized_attributes.eml', | |
306 | :allow_override => 'tracker,category,priority' |
|
306 | :allow_override => 'tracker,category,priority' | |
307 | ) |
|
307 | ) | |
308 | assert issue.is_a?(Issue) |
|
308 | assert issue.is_a?(Issue) | |
309 | assert !issue.new_record? |
|
309 | assert !issue.new_record? | |
310 | issue.reload |
|
310 | issue.reload | |
311 | assert_equal 'New ticket on a given project', issue.subject |
|
311 | assert_equal 'New ticket on a given project', issue.subject | |
312 | assert_equal User.find_by_login('jsmith'), issue.author |
|
312 | assert_equal User.find_by_login('jsmith'), issue.author | |
313 | assert_equal Project.find(2), issue.project |
|
313 | assert_equal Project.find(2), issue.project | |
314 | assert_equal 'Feature request', issue.tracker.to_s |
|
314 | assert_equal 'Feature request', issue.tracker.to_s | |
315 | assert_equal 'Stock management', issue.category.to_s |
|
315 | assert_equal 'Stock management', issue.category.to_s | |
316 | assert_equal 'Urgent', issue.priority.to_s |
|
316 | assert_equal 'Urgent', issue.priority.to_s | |
317 | assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.') |
|
317 | assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.') | |
318 | end |
|
318 | end | |
319 |
|
319 | |||
320 | def test_add_issue_with_japanese_keywords |
|
320 | def test_add_issue_with_japanese_keywords | |
321 | ja_dev = "\xe9\x96\x8b\xe7\x99\xba" |
|
321 | ja_dev = "\xe9\x96\x8b\xe7\x99\xba" | |
322 | ja_dev.force_encoding('UTF-8') if ja_dev.respond_to?(:force_encoding) |
|
322 | ja_dev.force_encoding('UTF-8') if ja_dev.respond_to?(:force_encoding) | |
323 | tracker = Tracker.create!(:name => ja_dev) |
|
323 | tracker = Tracker.create!(:name => ja_dev) | |
324 | Project.find(1).trackers << tracker |
|
324 | Project.find(1).trackers << tracker | |
325 | issue = submit_email( |
|
325 | issue = submit_email( | |
326 | 'japanese_keywords_iso_2022_jp.eml', |
|
326 | 'japanese_keywords_iso_2022_jp.eml', | |
327 | :issue => {:project => 'ecookbook'}, |
|
327 | :issue => {:project => 'ecookbook'}, | |
328 | :allow_override => 'tracker' |
|
328 | :allow_override => 'tracker' | |
329 | ) |
|
329 | ) | |
330 | assert_kind_of Issue, issue |
|
330 | assert_kind_of Issue, issue | |
331 | assert_equal tracker, issue.tracker |
|
331 | assert_equal tracker, issue.tracker | |
332 | end |
|
332 | end | |
333 |
|
333 | |||
334 | def test_add_issue_from_apple_mail |
|
334 | def test_add_issue_from_apple_mail | |
335 | issue = submit_email( |
|
335 | issue = submit_email( | |
336 | 'apple_mail_with_attachment.eml', |
|
336 | 'apple_mail_with_attachment.eml', | |
337 | :issue => {:project => 'ecookbook'} |
|
337 | :issue => {:project => 'ecookbook'} | |
338 | ) |
|
338 | ) | |
339 | assert_kind_of Issue, issue |
|
339 | assert_kind_of Issue, issue | |
340 | assert_equal 1, issue.attachments.size |
|
340 | assert_equal 1, issue.attachments.size | |
341 |
|
341 | |||
342 | attachment = issue.attachments.first |
|
342 | attachment = issue.attachments.first | |
343 | assert_equal 'paella.jpg', attachment.filename |
|
343 | assert_equal 'paella.jpg', attachment.filename | |
344 | assert_equal 10790, attachment.filesize |
|
344 | assert_equal 10790, attachment.filesize | |
345 | assert File.exist?(attachment.diskfile) |
|
345 | assert File.exist?(attachment.diskfile) | |
346 | assert_equal 10790, File.size(attachment.diskfile) |
|
346 | assert_equal 10790, File.size(attachment.diskfile) | |
347 | assert_equal 'caaf384198bcbc9563ab5c058acd73cd', attachment.digest |
|
347 | assert_equal 'caaf384198bcbc9563ab5c058acd73cd', attachment.digest | |
348 | end |
|
348 | end | |
349 |
|
349 | |||
350 | def test_should_ignore_emails_from_emission_address |
|
350 | def test_should_ignore_emails_from_emission_address | |
351 | Role.anonymous.add_permission!(:add_issues) |
|
351 | Role.anonymous.add_permission!(:add_issues) | |
352 | assert_no_difference 'User.count' do |
|
352 | assert_no_difference 'User.count' do | |
353 | assert_equal false, |
|
353 | assert_equal false, | |
354 | submit_email( |
|
354 | submit_email( | |
355 | 'ticket_from_emission_address.eml', |
|
355 | 'ticket_from_emission_address.eml', | |
356 | :issue => {:project => 'ecookbook'}, |
|
356 | :issue => {:project => 'ecookbook'}, | |
357 | :unknown_user => 'create' |
|
357 | :unknown_user => 'create' | |
358 | ) |
|
358 | ) | |
359 | end |
|
359 | end | |
360 | end |
|
360 | end | |
361 |
|
361 | |||
362 | def test_add_issue_should_send_email_notification |
|
362 | def test_add_issue_should_send_email_notification | |
363 | Setting.notified_events = ['issue_added'] |
|
363 | Setting.notified_events = ['issue_added'] | |
364 | ActionMailer::Base.deliveries.clear |
|
364 | ActionMailer::Base.deliveries.clear | |
365 | # This email contains: 'Project: onlinestore' |
|
365 | # This email contains: 'Project: onlinestore' | |
366 | issue = submit_email('ticket_on_given_project.eml') |
|
366 | issue = submit_email('ticket_on_given_project.eml') | |
367 | assert issue.is_a?(Issue) |
|
367 | assert issue.is_a?(Issue) | |
368 | assert_equal 1, ActionMailer::Base.deliveries.size |
|
368 | assert_equal 1, ActionMailer::Base.deliveries.size | |
369 | end |
|
369 | end | |
370 |
|
370 | |||
371 | def test_update_issue |
|
371 | def test_update_issue | |
372 | journal = submit_email('ticket_reply.eml') |
|
372 | journal = submit_email('ticket_reply.eml') | |
373 | assert journal.is_a?(Journal) |
|
373 | assert journal.is_a?(Journal) | |
374 | assert_equal User.find_by_login('jsmith'), journal.user |
|
374 | assert_equal User.find_by_login('jsmith'), journal.user | |
375 | assert_equal Issue.find(2), journal.journalized |
|
375 | assert_equal Issue.find(2), journal.journalized | |
376 | assert_match /This is reply/, journal.notes |
|
376 | assert_match /This is reply/, journal.notes | |
377 | assert_equal 'Feature request', journal.issue.tracker.name |
|
377 | assert_equal 'Feature request', journal.issue.tracker.name | |
378 | end |
|
378 | end | |
379 |
|
379 | |||
380 | def test_update_issue_with_attribute_changes |
|
380 | def test_update_issue_with_attribute_changes | |
381 | # This email contains: 'Status: Resolved' |
|
381 | # This email contains: 'Status: Resolved' | |
382 | journal = submit_email('ticket_reply_with_status.eml') |
|
382 | journal = submit_email('ticket_reply_with_status.eml') | |
383 | assert journal.is_a?(Journal) |
|
383 | assert journal.is_a?(Journal) | |
384 | issue = Issue.find(journal.issue.id) |
|
384 | issue = Issue.find(journal.issue.id) | |
385 | assert_equal User.find_by_login('jsmith'), journal.user |
|
385 | assert_equal User.find_by_login('jsmith'), journal.user | |
386 | assert_equal Issue.find(2), journal.journalized |
|
386 | assert_equal Issue.find(2), journal.journalized | |
387 | assert_match /This is reply/, journal.notes |
|
387 | assert_match /This is reply/, journal.notes | |
388 | assert_equal 'Feature request', journal.issue.tracker.name |
|
388 | assert_equal 'Feature request', journal.issue.tracker.name | |
389 | assert_equal IssueStatus.find_by_name("Resolved"), issue.status |
|
389 | assert_equal IssueStatus.find_by_name("Resolved"), issue.status | |
390 | assert_equal '2010-01-01', issue.start_date.to_s |
|
390 | assert_equal '2010-01-01', issue.start_date.to_s | |
391 | assert_equal '2010-12-31', issue.due_date.to_s |
|
391 | assert_equal '2010-12-31', issue.due_date.to_s | |
392 | assert_equal User.find_by_login('jsmith'), issue.assigned_to |
|
392 | assert_equal User.find_by_login('jsmith'), issue.assigned_to | |
393 | assert_equal "52.6", issue.custom_value_for(CustomField.find_by_name('Float field')).value |
|
393 | assert_equal "52.6", issue.custom_value_for(CustomField.find_by_name('Float field')).value | |
394 | # keywords should be removed from the email body |
|
394 | # keywords should be removed from the email body | |
395 | assert !journal.notes.match(/^Status:/i) |
|
395 | assert !journal.notes.match(/^Status:/i) | |
396 | assert !journal.notes.match(/^Start Date:/i) |
|
396 | assert !journal.notes.match(/^Start Date:/i) | |
397 | end |
|
397 | end | |
398 |
|
398 | |||
399 | def test_update_issue_with_attachment |
|
399 | def test_update_issue_with_attachment | |
400 | assert_difference 'Journal.count' do |
|
400 | assert_difference 'Journal.count' do | |
401 | assert_difference 'JournalDetail.count' do |
|
401 | assert_difference 'JournalDetail.count' do | |
402 | assert_difference 'Attachment.count' do |
|
402 | assert_difference 'Attachment.count' do | |
403 | assert_no_difference 'Issue.count' do |
|
403 | assert_no_difference 'Issue.count' do | |
404 | journal = submit_email('ticket_with_attachment.eml') do |raw| |
|
404 | journal = submit_email('ticket_with_attachment.eml') do |raw| | |
405 | raw.gsub! /^Subject: .*$/, 'Subject: Re: [Cookbook - Feature #2] (New) Add ingredients categories' |
|
405 | raw.gsub! /^Subject: .*$/, 'Subject: Re: [Cookbook - Feature #2] (New) Add ingredients categories' | |
406 | end |
|
406 | end | |
407 | end |
|
407 | end | |
408 | end |
|
408 | end | |
409 | end |
|
409 | end | |
410 | end |
|
410 | end | |
411 | journal = Journal.first(:order => 'id DESC') |
|
411 | journal = Journal.first(:order => 'id DESC') | |
412 | assert_equal Issue.find(2), journal.journalized |
|
412 | assert_equal Issue.find(2), journal.journalized | |
413 | assert_equal 1, journal.details.size |
|
413 | assert_equal 1, journal.details.size | |
414 |
|
414 | |||
415 | detail = journal.details.first |
|
415 | detail = journal.details.first | |
416 | assert_equal 'attachment', detail.property |
|
416 | assert_equal 'attachment', detail.property | |
417 | assert_equal 'Paella.jpg', detail.value |
|
417 | assert_equal 'Paella.jpg', detail.value | |
418 | end |
|
418 | end | |
419 |
|
419 | |||
420 | def test_update_issue_should_send_email_notification |
|
420 | def test_update_issue_should_send_email_notification | |
421 | ActionMailer::Base.deliveries.clear |
|
421 | ActionMailer::Base.deliveries.clear | |
422 | journal = submit_email('ticket_reply.eml') |
|
422 | journal = submit_email('ticket_reply.eml') | |
423 | assert journal.is_a?(Journal) |
|
423 | assert journal.is_a?(Journal) | |
424 | assert_equal 1, ActionMailer::Base.deliveries.size |
|
424 | assert_equal 1, ActionMailer::Base.deliveries.size | |
425 | end |
|
425 | end | |
426 |
|
426 | |||
427 | def test_update_issue_should_not_set_defaults |
|
427 | def test_update_issue_should_not_set_defaults | |
428 | journal = submit_email( |
|
428 | journal = submit_email( | |
429 | 'ticket_reply.eml', |
|
429 | 'ticket_reply.eml', | |
430 | :issue => {:tracker => 'Support request', :priority => 'High'} |
|
430 | :issue => {:tracker => 'Support request', :priority => 'High'} | |
431 | ) |
|
431 | ) | |
432 | assert journal.is_a?(Journal) |
|
432 | assert journal.is_a?(Journal) | |
433 | assert_match /This is reply/, journal.notes |
|
433 | assert_match /This is reply/, journal.notes | |
434 | assert_equal 'Feature request', journal.issue.tracker.name |
|
434 | assert_equal 'Feature request', journal.issue.tracker.name | |
435 | assert_equal 'Normal', journal.issue.priority.name |
|
435 | assert_equal 'Normal', journal.issue.priority.name | |
436 | end |
|
436 | end | |
437 |
|
437 | |||
438 | def test_reply_to_a_message |
|
438 | def test_reply_to_a_message | |
439 | m = submit_email('message_reply.eml') |
|
439 | m = submit_email('message_reply.eml') | |
440 | assert m.is_a?(Message) |
|
440 | assert m.is_a?(Message) | |
441 | assert !m.new_record? |
|
441 | assert !m.new_record? | |
442 | m.reload |
|
442 | m.reload | |
443 | assert_equal 'Reply via email', m.subject |
|
443 | assert_equal 'Reply via email', m.subject | |
444 | # The email replies to message #2 which is part of the thread of message #1 |
|
444 | # The email replies to message #2 which is part of the thread of message #1 | |
445 | assert_equal Message.find(1), m.parent |
|
445 | assert_equal Message.find(1), m.parent | |
446 | end |
|
446 | end | |
447 |
|
447 | |||
448 | def test_reply_to_a_message_by_subject |
|
448 | def test_reply_to_a_message_by_subject | |
449 | m = submit_email('message_reply_by_subject.eml') |
|
449 | m = submit_email('message_reply_by_subject.eml') | |
450 | assert m.is_a?(Message) |
|
450 | assert m.is_a?(Message) | |
451 | assert !m.new_record? |
|
451 | assert !m.new_record? | |
452 | m.reload |
|
452 | m.reload | |
453 | assert_equal 'Reply to the first post', m.subject |
|
453 | assert_equal 'Reply to the first post', m.subject | |
454 | assert_equal Message.find(1), m.parent |
|
454 | assert_equal Message.find(1), m.parent | |
455 | end |
|
455 | end | |
456 |
|
456 | |||
457 | def test_should_strip_tags_of_html_only_emails |
|
457 | def test_should_strip_tags_of_html_only_emails | |
458 | issue = submit_email('ticket_html_only.eml', :issue => {:project => 'ecookbook'}) |
|
458 | issue = submit_email('ticket_html_only.eml', :issue => {:project => 'ecookbook'}) | |
459 | assert issue.is_a?(Issue) |
|
459 | assert issue.is_a?(Issue) | |
460 | assert !issue.new_record? |
|
460 | assert !issue.new_record? | |
461 | issue.reload |
|
461 | issue.reload | |
462 | assert_equal 'HTML email', issue.subject |
|
462 | assert_equal 'HTML email', issue.subject | |
463 | assert_equal 'This is a html-only email.', issue.description |
|
463 | assert_equal 'This is a html-only email.', issue.description | |
464 | end |
|
464 | end | |
465 |
|
465 | |||
466 | context "truncate emails based on the Setting" do |
|
466 | context "truncate emails based on the Setting" do | |
467 | context "with no setting" do |
|
467 | context "with no setting" do | |
468 | setup do |
|
468 | setup do | |
469 | Setting.mail_handler_body_delimiters = '' |
|
469 | Setting.mail_handler_body_delimiters = '' | |
470 | end |
|
470 | end | |
471 |
|
471 | |||
472 | should "add the entire email into the issue" do |
|
472 | should "add the entire email into the issue" do | |
473 | issue = submit_email('ticket_on_given_project.eml') |
|
473 | issue = submit_email('ticket_on_given_project.eml') | |
474 | assert_issue_created(issue) |
|
474 | assert_issue_created(issue) | |
475 | assert issue.description.include?('---') |
|
475 | assert issue.description.include?('---') | |
476 | assert issue.description.include?('This paragraph is after the delimiter') |
|
476 | assert issue.description.include?('This paragraph is after the delimiter') | |
477 | end |
|
477 | end | |
478 | end |
|
478 | end | |
479 |
|
479 | |||
480 | context "with a single string" do |
|
480 | context "with a single string" do | |
481 | setup do |
|
481 | setup do | |
482 | Setting.mail_handler_body_delimiters = '---' |
|
482 | Setting.mail_handler_body_delimiters = '---' | |
483 | end |
|
483 | end | |
484 | should "truncate the email at the delimiter for the issue" do |
|
484 | should "truncate the email at the delimiter for the issue" do | |
485 | issue = submit_email('ticket_on_given_project.eml') |
|
485 | issue = submit_email('ticket_on_given_project.eml') | |
486 | assert_issue_created(issue) |
|
486 | assert_issue_created(issue) | |
487 | assert issue.description.include?('This paragraph is before delimiters') |
|
487 | assert issue.description.include?('This paragraph is before delimiters') | |
488 | assert issue.description.include?('--- This line starts with a delimiter') |
|
488 | assert issue.description.include?('--- This line starts with a delimiter') | |
489 | assert !issue.description.match(/^---$/) |
|
489 | assert !issue.description.match(/^---$/) | |
490 | assert !issue.description.include?('This paragraph is after the delimiter') |
|
490 | assert !issue.description.include?('This paragraph is after the delimiter') | |
491 | end |
|
491 | end | |
492 | end |
|
492 | end | |
493 |
|
493 | |||
494 | context "with a single quoted reply (e.g. reply to a Redmine email notification)" do |
|
494 | context "with a single quoted reply (e.g. reply to a Redmine email notification)" do | |
495 | setup do |
|
495 | setup do | |
496 | Setting.mail_handler_body_delimiters = '--- Reply above. Do not remove this line. ---' |
|
496 | Setting.mail_handler_body_delimiters = '--- Reply above. Do not remove this line. ---' | |
497 | end |
|
497 | end | |
498 | should "truncate the email at the delimiter with the quoted reply symbols (>)" do |
|
498 | should "truncate the email at the delimiter with the quoted reply symbols (>)" do | |
499 | journal = submit_email('issue_update_with_quoted_reply_above.eml') |
|
499 | journal = submit_email('issue_update_with_quoted_reply_above.eml') | |
500 | assert journal.is_a?(Journal) |
|
500 | assert journal.is_a?(Journal) | |
501 | assert journal.notes.include?('An update to the issue by the sender.') |
|
501 | assert journal.notes.include?('An update to the issue by the sender.') | |
502 | assert !journal.notes.match(Regexp.escape("--- Reply above. Do not remove this line. ---")) |
|
502 | assert !journal.notes.match(Regexp.escape("--- Reply above. Do not remove this line. ---")) | |
503 | assert !journal.notes.include?('Looks like the JSON api for projects was missed.') |
|
503 | assert !journal.notes.include?('Looks like the JSON api for projects was missed.') | |
504 | end |
|
504 | end | |
505 | end |
|
505 | end | |
506 |
|
506 | |||
507 | context "with multiple quoted replies (e.g. reply to a reply of a Redmine email notification)" do |
|
507 | context "with multiple quoted replies (e.g. reply to a reply of a Redmine email notification)" do | |
508 | setup do |
|
508 | setup do | |
509 | Setting.mail_handler_body_delimiters = '--- Reply above. Do not remove this line. ---' |
|
509 | Setting.mail_handler_body_delimiters = '--- Reply above. Do not remove this line. ---' | |
510 | end |
|
510 | end | |
511 | should "truncate the email at the delimiter with the quoted reply symbols (>)" do |
|
511 | should "truncate the email at the delimiter with the quoted reply symbols (>)" do | |
512 | journal = submit_email('issue_update_with_multiple_quoted_reply_above.eml') |
|
512 | journal = submit_email('issue_update_with_multiple_quoted_reply_above.eml') | |
513 | assert journal.is_a?(Journal) |
|
513 | assert journal.is_a?(Journal) | |
514 | assert journal.notes.include?('An update to the issue by the sender.') |
|
514 | assert journal.notes.include?('An update to the issue by the sender.') | |
515 | assert !journal.notes.match(Regexp.escape("--- Reply above. Do not remove this line. ---")) |
|
515 | assert !journal.notes.match(Regexp.escape("--- Reply above. Do not remove this line. ---")) | |
516 | assert !journal.notes.include?('Looks like the JSON api for projects was missed.') |
|
516 | assert !journal.notes.include?('Looks like the JSON api for projects was missed.') | |
517 | end |
|
517 | end | |
518 | end |
|
518 | end | |
519 |
|
519 | |||
520 | context "with multiple strings" do |
|
520 | context "with multiple strings" do | |
521 | setup do |
|
521 | setup do | |
522 | Setting.mail_handler_body_delimiters = "---\nBREAK" |
|
522 | Setting.mail_handler_body_delimiters = "---\nBREAK" | |
523 | end |
|
523 | end | |
524 | should "truncate the email at the first delimiter found (BREAK)" do |
|
524 | should "truncate the email at the first delimiter found (BREAK)" do | |
525 | issue = submit_email('ticket_on_given_project.eml') |
|
525 | issue = submit_email('ticket_on_given_project.eml') | |
526 | assert_issue_created(issue) |
|
526 | assert_issue_created(issue) | |
527 | assert issue.description.include?('This paragraph is before delimiters') |
|
527 | assert issue.description.include?('This paragraph is before delimiters') | |
528 | assert !issue.description.include?('BREAK') |
|
528 | assert !issue.description.include?('BREAK') | |
529 | assert !issue.description.include?('This paragraph is between delimiters') |
|
529 | assert !issue.description.include?('This paragraph is between delimiters') | |
530 | assert !issue.description.match(/^---$/) |
|
530 | assert !issue.description.match(/^---$/) | |
531 | assert !issue.description.include?('This paragraph is after the delimiter') |
|
531 | assert !issue.description.include?('This paragraph is after the delimiter') | |
532 | end |
|
532 | end | |
533 | end |
|
533 | end | |
534 | end |
|
534 | end | |
535 |
|
535 | |||
536 | def test_email_with_long_subject_line |
|
536 | def test_email_with_long_subject_line | |
537 | issue = submit_email('ticket_with_long_subject.eml') |
|
537 | issue = submit_email('ticket_with_long_subject.eml') | |
538 | assert issue.is_a?(Issue) |
|
538 | assert issue.is_a?(Issue) | |
539 | assert_equal issue.subject, 'New ticket on a given project with a very long subject line which exceeds 255 chars and should not be ignored but chopped off. And if the subject line is still not long enough, we just add more text. And more text. Wow, this is really annoying. Especially, if you have nothing to say...'[0,255] |
|
539 | assert_equal issue.subject, 'New ticket on a given project with a very long subject line which exceeds 255 chars and should not be ignored but chopped off. And if the subject line is still not long enough, we just add more text. And more text. Wow, this is really annoying. Especially, if you have nothing to say...'[0,255] | |
540 | end |
|
540 | end | |
541 |
|
541 | |||
542 | def test_new_user_from_attributes_should_return_valid_user |
|
542 | def test_new_user_from_attributes_should_return_valid_user | |
543 | to_test = { |
|
543 | to_test = { | |
544 | # [address, name] => [login, firstname, lastname] |
|
544 | # [address, name] => [login, firstname, lastname] | |
545 | ['jsmith@example.net', nil] => ['jsmith@example.net', 'jsmith', '-'], |
|
545 | ['jsmith@example.net', nil] => ['jsmith@example.net', 'jsmith', '-'], | |
546 | ['jsmith@example.net', 'John'] => ['jsmith@example.net', 'John', '-'], |
|
546 | ['jsmith@example.net', 'John'] => ['jsmith@example.net', 'John', '-'], | |
547 | ['jsmith@example.net', 'John Smith'] => ['jsmith@example.net', 'John', 'Smith'], |
|
547 | ['jsmith@example.net', 'John Smith'] => ['jsmith@example.net', 'John', 'Smith'], | |
548 | ['jsmith@example.net', 'John Paul Smith'] => ['jsmith@example.net', 'John', 'Paul Smith'], |
|
548 | ['jsmith@example.net', 'John Paul Smith'] => ['jsmith@example.net', 'John', 'Paul Smith'], | |
549 | ['jsmith@example.net', 'AVeryLongFirstnameThatExceedsTheMaximumLength Smith'] => ['jsmith@example.net', 'AVeryLongFirstnameThatExceedsT', 'Smith'], |
|
549 | ['jsmith@example.net', 'AVeryLongFirstnameThatExceedsTheMaximumLength Smith'] => ['jsmith@example.net', 'AVeryLongFirstnameThatExceedsT', 'Smith'], | |
550 | ['jsmith@example.net', 'John AVeryLongLastnameThatExceedsTheMaximumLength'] => ['jsmith@example.net', 'John', 'AVeryLongLastnameThatExceedsTh'] |
|
550 | ['jsmith@example.net', 'John AVeryLongLastnameThatExceedsTheMaximumLength'] => ['jsmith@example.net', 'John', 'AVeryLongLastnameThatExceedsTh'] | |
551 | } |
|
551 | } | |
552 |
|
552 | |||
553 | to_test.each do |attrs, expected| |
|
553 | to_test.each do |attrs, expected| | |
554 | user = MailHandler.new_user_from_attributes(attrs.first, attrs.last) |
|
554 | user = MailHandler.new_user_from_attributes(attrs.first, attrs.last) | |
555 |
|
555 | |||
556 | assert user.valid?, user.errors.full_messages.to_s |
|
556 | assert user.valid?, user.errors.full_messages.to_s | |
557 | assert_equal attrs.first, user.mail |
|
557 | assert_equal attrs.first, user.mail | |
558 | assert_equal expected[0], user.login |
|
558 | assert_equal expected[0], user.login | |
559 | assert_equal expected[1], user.firstname |
|
559 | assert_equal expected[1], user.firstname | |
560 | assert_equal expected[2], user.lastname |
|
560 | assert_equal expected[2], user.lastname | |
561 | end |
|
561 | end | |
562 | end |
|
562 | end | |
563 |
|
563 | |||
564 | def test_new_user_from_attributes_should_respect_minimum_password_length |
|
564 | def test_new_user_from_attributes_should_respect_minimum_password_length | |
565 | with_settings :password_min_length => 15 do |
|
565 | with_settings :password_min_length => 15 do | |
566 | user = MailHandler.new_user_from_attributes('jsmith@example.net') |
|
566 | user = MailHandler.new_user_from_attributes('jsmith@example.net') | |
567 | assert user.valid? |
|
567 | assert user.valid? | |
568 | assert user.password.length >= 15 |
|
568 | assert user.password.length >= 15 | |
569 | end |
|
569 | end | |
570 | end |
|
570 | end | |
571 |
|
571 | |||
572 | def test_new_user_from_attributes_should_use_default_login_if_invalid |
|
572 | def test_new_user_from_attributes_should_use_default_login_if_invalid | |
573 | user = MailHandler.new_user_from_attributes('foo+bar@example.net') |
|
573 | user = MailHandler.new_user_from_attributes('foo+bar@example.net') | |
574 | assert user.valid? |
|
574 | assert user.valid? | |
575 | assert user.login =~ /^user[a-f0-9]+$/ |
|
575 | assert user.login =~ /^user[a-f0-9]+$/ | |
576 | assert_equal 'foo+bar@example.net', user.mail |
|
576 | assert_equal 'foo+bar@example.net', user.mail | |
577 | end |
|
577 | end | |
578 |
|
578 | |||
579 | def test_new_user_with_utf8_encoded_fullname_should_be_decoded |
|
579 | def test_new_user_with_utf8_encoded_fullname_should_be_decoded | |
580 | assert_difference 'User.count' do |
|
580 | assert_difference 'User.count' do | |
581 | issue = submit_email( |
|
581 | issue = submit_email( | |
582 | 'fullname_of_sender_as_utf8_encoded.eml', |
|
582 | 'fullname_of_sender_as_utf8_encoded.eml', | |
583 | :issue => {:project => 'ecookbook'}, |
|
583 | :issue => {:project => 'ecookbook'}, | |
584 | :unknown_user => 'create' |
|
584 | :unknown_user => 'create' | |
585 | ) |
|
585 | ) | |
586 | end |
|
586 | end | |
587 |
|
587 | |||
588 | user = User.first(:order => 'id DESC') |
|
588 | user = User.first(:order => 'id DESC') | |
589 | assert_equal "foo@example.org", user.mail |
|
589 | assert_equal "foo@example.org", user.mail | |
590 | assert_equal "Ää", user.firstname |
|
590 | assert_equal "Ää", user.firstname | |
591 | assert_equal "Öö", user.lastname |
|
591 | assert_equal "Öö", user.lastname | |
592 | end |
|
592 | end | |
593 |
|
593 | |||
594 | private |
|
594 | private | |
595 |
|
595 | |||
596 | def submit_email(filename, options={}) |
|
596 | def submit_email(filename, options={}) | |
597 | raw = IO.read(File.join(FIXTURES_PATH, filename)) |
|
597 | raw = IO.read(File.join(FIXTURES_PATH, filename)) | |
598 | yield raw if block_given? |
|
598 | yield raw if block_given? | |
599 | MailHandler.receive(raw, options) |
|
599 | MailHandler.receive(raw, options) | |
600 | end |
|
600 | end | |
601 |
|
601 | |||
602 | def assert_issue_created(issue) |
|
602 | def assert_issue_created(issue) | |
603 | assert issue.is_a?(Issue) |
|
603 | assert issue.is_a?(Issue) | |
604 | assert !issue.new_record? |
|
604 | assert !issue.new_record? | |
605 | issue.reload |
|
605 | issue.reload | |
606 | end |
|
606 | end | |
607 | end |
|
607 | end |
General Comments 0
You need to be logged in to leave comments.
Login now