##// END OF EJS Templates
Adds tests for creating an issue with multiple uploads....
Jean-Philippe Lang -
r13409:9b082712a091
parent child
Show More
@@ -1,590 +1,647
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2014 Jean-Philippe Lang
2 # Copyright (C) 2006-2014 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class Redmine::ApiTest::IssuesTest < Redmine::ApiTest::Base
20 class Redmine::ApiTest::IssuesTest < Redmine::ApiTest::Base
21 fixtures :projects,
21 fixtures :projects,
22 :users,
22 :users,
23 :roles,
23 :roles,
24 :members,
24 :members,
25 :member_roles,
25 :member_roles,
26 :issues,
26 :issues,
27 :issue_statuses,
27 :issue_statuses,
28 :issue_relations,
28 :issue_relations,
29 :versions,
29 :versions,
30 :trackers,
30 :trackers,
31 :projects_trackers,
31 :projects_trackers,
32 :issue_categories,
32 :issue_categories,
33 :enabled_modules,
33 :enabled_modules,
34 :enumerations,
34 :enumerations,
35 :attachments,
35 :attachments,
36 :workflows,
36 :workflows,
37 :custom_fields,
37 :custom_fields,
38 :custom_values,
38 :custom_values,
39 :custom_fields_projects,
39 :custom_fields_projects,
40 :custom_fields_trackers,
40 :custom_fields_trackers,
41 :time_entries,
41 :time_entries,
42 :journals,
42 :journals,
43 :journal_details,
43 :journal_details,
44 :queries,
44 :queries,
45 :attachments
45 :attachments
46
46
47 test "GET /issues.xml should contain metadata" do
47 test "GET /issues.xml should contain metadata" do
48 get '/issues.xml'
48 get '/issues.xml'
49 assert_select 'issues[type=array][total_count=?][limit="25"][offset="0"]',
49 assert_select 'issues[type=array][total_count=?][limit="25"][offset="0"]',
50 assigns(:issue_count).to_s
50 assigns(:issue_count).to_s
51 end
51 end
52
52
53 test "GET /issues.xml with nometa param should not contain metadata" do
53 test "GET /issues.xml with nometa param should not contain metadata" do
54 get '/issues.xml?nometa=1'
54 get '/issues.xml?nometa=1'
55 assert_select 'issues[type=array]:not([total_count]):not([limit]):not([offset])'
55 assert_select 'issues[type=array]:not([total_count]):not([limit]):not([offset])'
56 end
56 end
57
57
58 test "GET /issues.xml with nometa header should not contain metadata" do
58 test "GET /issues.xml with nometa header should not contain metadata" do
59 get '/issues.xml', {}, {'X-Redmine-Nometa' => '1'}
59 get '/issues.xml', {}, {'X-Redmine-Nometa' => '1'}
60 assert_select 'issues[type=array]:not([total_count]):not([limit]):not([offset])'
60 assert_select 'issues[type=array]:not([total_count]):not([limit]):not([offset])'
61 end
61 end
62
62
63 test "GET /issues.xml with offset and limit" do
63 test "GET /issues.xml with offset and limit" do
64 get '/issues.xml?offset=2&limit=3'
64 get '/issues.xml?offset=2&limit=3'
65
65
66 assert_equal 3, assigns(:limit)
66 assert_equal 3, assigns(:limit)
67 assert_equal 2, assigns(:offset)
67 assert_equal 2, assigns(:offset)
68 assert_select 'issues issue', 3
68 assert_select 'issues issue', 3
69 end
69 end
70
70
71 test "GET /issues.xml with relations" do
71 test "GET /issues.xml with relations" do
72 get '/issues.xml?include=relations'
72 get '/issues.xml?include=relations'
73
73
74 assert_response :success
74 assert_response :success
75 assert_equal 'application/xml', @response.content_type
75 assert_equal 'application/xml', @response.content_type
76
76
77 assert_select 'issue id:content(3)' do
77 assert_select 'issue id:content(3)' do
78 assert_select '~ relations relation', 1
78 assert_select '~ relations relation', 1
79 assert_select '~ relations relation[id="2"][issue_id="2"][issue_to_id="3"][relation_type=relates]'
79 assert_select '~ relations relation[id="2"][issue_id="2"][issue_to_id="3"][relation_type=relates]'
80 end
80 end
81
81
82 assert_select 'issue id:content(1)' do
82 assert_select 'issue id:content(1)' do
83 assert_select '~ relations'
83 assert_select '~ relations'
84 assert_select '~ relations relation', 0
84 assert_select '~ relations relation', 0
85 end
85 end
86 end
86 end
87
87
88 test "GET /issues.xml with invalid query params" do
88 test "GET /issues.xml with invalid query params" do
89 get '/issues.xml', {:f => ['start_date'], :op => {:start_date => '='}}
89 get '/issues.xml', {:f => ['start_date'], :op => {:start_date => '='}}
90
90
91 assert_response :unprocessable_entity
91 assert_response :unprocessable_entity
92 assert_equal 'application/xml', @response.content_type
92 assert_equal 'application/xml', @response.content_type
93 assert_select 'errors error', :text => "Start date cannot be blank"
93 assert_select 'errors error', :text => "Start date cannot be blank"
94 end
94 end
95
95
96 test "GET /issues.xml with custom field filter" do
96 test "GET /issues.xml with custom field filter" do
97 get '/issues.xml',
97 get '/issues.xml',
98 {:set_filter => 1, :f => ['cf_1'], :op => {:cf_1 => '='}, :v => {:cf_1 => ['MySQL']}}
98 {:set_filter => 1, :f => ['cf_1'], :op => {:cf_1 => '='}, :v => {:cf_1 => ['MySQL']}}
99
99
100 expected_ids = Issue.visible.
100 expected_ids = Issue.visible.
101 joins(:custom_values).
101 joins(:custom_values).
102 where(:custom_values => {:custom_field_id => 1, :value => 'MySQL'}).map(&:id)
102 where(:custom_values => {:custom_field_id => 1, :value => 'MySQL'}).map(&:id)
103 assert expected_ids.any?
103 assert expected_ids.any?
104
104
105 assert_select 'issues > issue > id', :count => expected_ids.count do |ids|
105 assert_select 'issues > issue > id', :count => expected_ids.count do |ids|
106 ids.each { |id| assert expected_ids.delete(id.children.first.content.to_i) }
106 ids.each { |id| assert expected_ids.delete(id.children.first.content.to_i) }
107 end
107 end
108 end
108 end
109
109
110 test "GET /issues.xml with custom field filter (shorthand method)" do
110 test "GET /issues.xml with custom field filter (shorthand method)" do
111 get '/issues.xml', {:cf_1 => 'MySQL'}
111 get '/issues.xml', {:cf_1 => 'MySQL'}
112
112
113 expected_ids = Issue.visible.
113 expected_ids = Issue.visible.
114 joins(:custom_values).
114 joins(:custom_values).
115 where(:custom_values => {:custom_field_id => 1, :value => 'MySQL'}).map(&:id)
115 where(:custom_values => {:custom_field_id => 1, :value => 'MySQL'}).map(&:id)
116 assert expected_ids.any?
116 assert expected_ids.any?
117
117
118 assert_select 'issues > issue > id', :count => expected_ids.count do |ids|
118 assert_select 'issues > issue > id', :count => expected_ids.count do |ids|
119 ids.each { |id| assert expected_ids.delete(id.children.first.content.to_i) }
119 ids.each { |id| assert expected_ids.delete(id.children.first.content.to_i) }
120 end
120 end
121 end
121 end
122
122
123 def test_index_should_include_issue_attributes
123 def test_index_should_include_issue_attributes
124 get '/issues.xml'
124 get '/issues.xml'
125 assert_select 'issues>issue>is_private', :text => 'false'
125 assert_select 'issues>issue>is_private', :text => 'false'
126 end
126 end
127
127
128 def test_index_should_allow_timestamp_filtering
128 def test_index_should_allow_timestamp_filtering
129 Issue.delete_all
129 Issue.delete_all
130 Issue.generate!(:subject => '1').update_column(:updated_on, Time.parse("2014-01-02T10:25:00Z"))
130 Issue.generate!(:subject => '1').update_column(:updated_on, Time.parse("2014-01-02T10:25:00Z"))
131 Issue.generate!(:subject => '2').update_column(:updated_on, Time.parse("2014-01-02T12:13:00Z"))
131 Issue.generate!(:subject => '2').update_column(:updated_on, Time.parse("2014-01-02T12:13:00Z"))
132
132
133 get '/issues.xml',
133 get '/issues.xml',
134 {:set_filter => 1, :f => ['updated_on'], :op => {:updated_on => '<='},
134 {:set_filter => 1, :f => ['updated_on'], :op => {:updated_on => '<='},
135 :v => {:updated_on => ['2014-01-02T12:00:00Z']}}
135 :v => {:updated_on => ['2014-01-02T12:00:00Z']}}
136 assert_select 'issues>issue', :count => 1
136 assert_select 'issues>issue', :count => 1
137 assert_select 'issues>issue>subject', :text => '1'
137 assert_select 'issues>issue>subject', :text => '1'
138
138
139 get '/issues.xml',
139 get '/issues.xml',
140 {:set_filter => 1, :f => ['updated_on'], :op => {:updated_on => '>='},
140 {:set_filter => 1, :f => ['updated_on'], :op => {:updated_on => '>='},
141 :v => {:updated_on => ['2014-01-02T12:00:00Z']}}
141 :v => {:updated_on => ['2014-01-02T12:00:00Z']}}
142 assert_select 'issues>issue', :count => 1
142 assert_select 'issues>issue', :count => 1
143 assert_select 'issues>issue>subject', :text => '2'
143 assert_select 'issues>issue>subject', :text => '2'
144
144
145 get '/issues.xml',
145 get '/issues.xml',
146 {:set_filter => 1, :f => ['updated_on'], :op => {:updated_on => '>='},
146 {:set_filter => 1, :f => ['updated_on'], :op => {:updated_on => '>='},
147 :v => {:updated_on => ['2014-01-02T08:00:00Z']}}
147 :v => {:updated_on => ['2014-01-02T08:00:00Z']}}
148 assert_select 'issues>issue', :count => 2
148 assert_select 'issues>issue', :count => 2
149 end
149 end
150
150
151 test "GET /issues.xml with filter" do
151 test "GET /issues.xml with filter" do
152 get '/issues.xml?status_id=5'
152 get '/issues.xml?status_id=5'
153
153
154 expected_ids = Issue.visible.where(:status_id => 5).map(&:id)
154 expected_ids = Issue.visible.where(:status_id => 5).map(&:id)
155 assert expected_ids.any?
155 assert expected_ids.any?
156
156
157 assert_select 'issues > issue > id', :count => expected_ids.count do |ids|
157 assert_select 'issues > issue > id', :count => expected_ids.count do |ids|
158 ids.each { |id| assert expected_ids.delete(id.children.first.content.to_i) }
158 ids.each { |id| assert expected_ids.delete(id.children.first.content.to_i) }
159 end
159 end
160 end
160 end
161
161
162 test "GET /issues.json with filter" do
162 test "GET /issues.json with filter" do
163 get '/issues.json?status_id=5'
163 get '/issues.json?status_id=5'
164
164
165 json = ActiveSupport::JSON.decode(response.body)
165 json = ActiveSupport::JSON.decode(response.body)
166 status_ids_used = json['issues'].collect {|j| j['status']['id'] }
166 status_ids_used = json['issues'].collect {|j| j['status']['id'] }
167 assert_equal 3, status_ids_used.length
167 assert_equal 3, status_ids_used.length
168 assert status_ids_used.all? {|id| id == 5 }
168 assert status_ids_used.all? {|id| id == 5 }
169 end
169 end
170
170
171 test "GET /issues/:id.xml with journals" do
171 test "GET /issues/:id.xml with journals" do
172 get '/issues/1.xml?include=journals'
172 get '/issues/1.xml?include=journals'
173
173
174 assert_select 'issue journals[type=array]' do
174 assert_select 'issue journals[type=array]' do
175 assert_select 'journal[id="1"]' do
175 assert_select 'journal[id="1"]' do
176 assert_select 'details[type=array]' do
176 assert_select 'details[type=array]' do
177 assert_select 'detail[name=status_id]' do
177 assert_select 'detail[name=status_id]' do
178 assert_select 'old_value', :text => '1'
178 assert_select 'old_value', :text => '1'
179 assert_select 'new_value', :text => '2'
179 assert_select 'new_value', :text => '2'
180 end
180 end
181 end
181 end
182 end
182 end
183 end
183 end
184 end
184 end
185
185
186 test "GET /issues/:id.xml with custom fields" do
186 test "GET /issues/:id.xml with custom fields" do
187 get '/issues/3.xml'
187 get '/issues/3.xml'
188
188
189 assert_select 'issue custom_fields[type=array]' do
189 assert_select 'issue custom_fields[type=array]' do
190 assert_select 'custom_field[id="1"]' do
190 assert_select 'custom_field[id="1"]' do
191 assert_select 'value', :text => 'MySQL'
191 assert_select 'value', :text => 'MySQL'
192 end
192 end
193 end
193 end
194 assert_nothing_raised do
194 assert_nothing_raised do
195 Hash.from_xml(response.body).to_xml
195 Hash.from_xml(response.body).to_xml
196 end
196 end
197 end
197 end
198
198
199 test "GET /issues/:id.xml with multi custom fields" do
199 test "GET /issues/:id.xml with multi custom fields" do
200 field = CustomField.find(1)
200 field = CustomField.find(1)
201 field.update_attribute :multiple, true
201 field.update_attribute :multiple, true
202 issue = Issue.find(3)
202 issue = Issue.find(3)
203 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
203 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
204 issue.save!
204 issue.save!
205
205
206 get '/issues/3.xml'
206 get '/issues/3.xml'
207 assert_response :success
207 assert_response :success
208
208
209 assert_select 'issue custom_fields[type=array]' do
209 assert_select 'issue custom_fields[type=array]' do
210 assert_select 'custom_field[id="1"]' do
210 assert_select 'custom_field[id="1"]' do
211 assert_select 'value[type=array] value', 2
211 assert_select 'value[type=array] value', 2
212 end
212 end
213 end
213 end
214 xml = Hash.from_xml(response.body)
214 xml = Hash.from_xml(response.body)
215 custom_fields = xml['issue']['custom_fields']
215 custom_fields = xml['issue']['custom_fields']
216 assert_kind_of Array, custom_fields
216 assert_kind_of Array, custom_fields
217 field = custom_fields.detect {|f| f['id'] == '1'}
217 field = custom_fields.detect {|f| f['id'] == '1'}
218 assert_kind_of Hash, field
218 assert_kind_of Hash, field
219 assert_equal ['MySQL', 'Oracle'], field['value'].sort
219 assert_equal ['MySQL', 'Oracle'], field['value'].sort
220 end
220 end
221
221
222 test "GET /issues/:id.json with multi custom fields" do
222 test "GET /issues/:id.json with multi custom fields" do
223 field = CustomField.find(1)
223 field = CustomField.find(1)
224 field.update_attribute :multiple, true
224 field.update_attribute :multiple, true
225 issue = Issue.find(3)
225 issue = Issue.find(3)
226 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
226 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
227 issue.save!
227 issue.save!
228
228
229 get '/issues/3.json'
229 get '/issues/3.json'
230 assert_response :success
230 assert_response :success
231
231
232 json = ActiveSupport::JSON.decode(response.body)
232 json = ActiveSupport::JSON.decode(response.body)
233 custom_fields = json['issue']['custom_fields']
233 custom_fields = json['issue']['custom_fields']
234 assert_kind_of Array, custom_fields
234 assert_kind_of Array, custom_fields
235 field = custom_fields.detect {|f| f['id'] == 1}
235 field = custom_fields.detect {|f| f['id'] == 1}
236 assert_kind_of Hash, field
236 assert_kind_of Hash, field
237 assert_equal ['MySQL', 'Oracle'], field['value'].sort
237 assert_equal ['MySQL', 'Oracle'], field['value'].sort
238 end
238 end
239
239
240 test "GET /issues/:id.xml with empty value for multi custom field" do
240 test "GET /issues/:id.xml with empty value for multi custom field" do
241 field = CustomField.find(1)
241 field = CustomField.find(1)
242 field.update_attribute :multiple, true
242 field.update_attribute :multiple, true
243 issue = Issue.find(3)
243 issue = Issue.find(3)
244 issue.custom_field_values = {1 => ['']}
244 issue.custom_field_values = {1 => ['']}
245 issue.save!
245 issue.save!
246
246
247 get '/issues/3.xml'
247 get '/issues/3.xml'
248
248
249 assert_select 'issue custom_fields[type=array]' do
249 assert_select 'issue custom_fields[type=array]' do
250 assert_select 'custom_field[id="1"]' do
250 assert_select 'custom_field[id="1"]' do
251 assert_select 'value[type=array]:empty'
251 assert_select 'value[type=array]:empty'
252 end
252 end
253 end
253 end
254 xml = Hash.from_xml(response.body)
254 xml = Hash.from_xml(response.body)
255 custom_fields = xml['issue']['custom_fields']
255 custom_fields = xml['issue']['custom_fields']
256 assert_kind_of Array, custom_fields
256 assert_kind_of Array, custom_fields
257 field = custom_fields.detect {|f| f['id'] == '1'}
257 field = custom_fields.detect {|f| f['id'] == '1'}
258 assert_kind_of Hash, field
258 assert_kind_of Hash, field
259 assert_equal [], field['value']
259 assert_equal [], field['value']
260 end
260 end
261
261
262 test "GET /issues/:id.json with empty value for multi custom field" do
262 test "GET /issues/:id.json with empty value for multi custom field" do
263 field = CustomField.find(1)
263 field = CustomField.find(1)
264 field.update_attribute :multiple, true
264 field.update_attribute :multiple, true
265 issue = Issue.find(3)
265 issue = Issue.find(3)
266 issue.custom_field_values = {1 => ['']}
266 issue.custom_field_values = {1 => ['']}
267 issue.save!
267 issue.save!
268
268
269 get '/issues/3.json'
269 get '/issues/3.json'
270 assert_response :success
270 assert_response :success
271 json = ActiveSupport::JSON.decode(response.body)
271 json = ActiveSupport::JSON.decode(response.body)
272 custom_fields = json['issue']['custom_fields']
272 custom_fields = json['issue']['custom_fields']
273 assert_kind_of Array, custom_fields
273 assert_kind_of Array, custom_fields
274 field = custom_fields.detect {|f| f['id'] == 1}
274 field = custom_fields.detect {|f| f['id'] == 1}
275 assert_kind_of Hash, field
275 assert_kind_of Hash, field
276 assert_equal [], field['value'].sort
276 assert_equal [], field['value'].sort
277 end
277 end
278
278
279 test "GET /issues/:id.xml with attachments" do
279 test "GET /issues/:id.xml with attachments" do
280 get '/issues/3.xml?include=attachments'
280 get '/issues/3.xml?include=attachments'
281
281
282 assert_select 'issue attachments[type=array]' do
282 assert_select 'issue attachments[type=array]' do
283 assert_select 'attachment', 5
283 assert_select 'attachment', 5
284 assert_select 'attachment id:content(4)' do
284 assert_select 'attachment id:content(4)' do
285 assert_select '~ filename', :text => 'source.rb'
285 assert_select '~ filename', :text => 'source.rb'
286 assert_select '~ content_url', :text => 'http://www.example.com/attachments/download/4/source.rb'
286 assert_select '~ content_url', :text => 'http://www.example.com/attachments/download/4/source.rb'
287 end
287 end
288 end
288 end
289 end
289 end
290
290
291 test "GET /issues/:id.xml with subtasks" do
291 test "GET /issues/:id.xml with subtasks" do
292 issue = Issue.generate_with_descendants!(:project_id => 1)
292 issue = Issue.generate_with_descendants!(:project_id => 1)
293 get "/issues/#{issue.id}.xml?include=children"
293 get "/issues/#{issue.id}.xml?include=children"
294
294
295 assert_select 'issue children[type=array]' do
295 assert_select 'issue children[type=array]' do
296 assert_select 'issue', 2
296 assert_select 'issue', 2
297 assert_select 'issue children', 1
297 assert_select 'issue children', 1
298 end
298 end
299 end
299 end
300
300
301 test "GET /issues/:id.json with subtasks" do
301 test "GET /issues/:id.json with subtasks" do
302 issue = Issue.generate_with_descendants!(:project_id => 1)
302 issue = Issue.generate_with_descendants!(:project_id => 1)
303 get "/issues/#{issue.id}.json?include=children"
303 get "/issues/#{issue.id}.json?include=children"
304
304
305 json = ActiveSupport::JSON.decode(response.body)
305 json = ActiveSupport::JSON.decode(response.body)
306 assert_equal 2, json['issue']['children'].size
306 assert_equal 2, json['issue']['children'].size
307 assert_equal 1, json['issue']['children'].select {|child| child.key?('children')}.size
307 assert_equal 1, json['issue']['children'].select {|child| child.key?('children')}.size
308 end
308 end
309
309
310 def test_show_should_include_issue_attributes
310 def test_show_should_include_issue_attributes
311 get '/issues/1.xml'
311 get '/issues/1.xml'
312 assert_select 'issue>is_private', :text => 'false'
312 assert_select 'issue>is_private', :text => 'false'
313 end
313 end
314
314
315 test "GET /issues/:id.xml?include=watchers should include watchers" do
315 test "GET /issues/:id.xml?include=watchers should include watchers" do
316 Watcher.create!(:user_id => 3, :watchable => Issue.find(1))
316 Watcher.create!(:user_id => 3, :watchable => Issue.find(1))
317
317
318 get '/issues/1.xml?include=watchers', {}, credentials('jsmith')
318 get '/issues/1.xml?include=watchers', {}, credentials('jsmith')
319
319
320 assert_response :ok
320 assert_response :ok
321 assert_equal 'application/xml', response.content_type
321 assert_equal 'application/xml', response.content_type
322 assert_select 'issue' do
322 assert_select 'issue' do
323 assert_select 'watchers', Issue.find(1).watchers.count
323 assert_select 'watchers', Issue.find(1).watchers.count
324 assert_select 'watchers' do
324 assert_select 'watchers' do
325 assert_select 'user[id=3]'
325 assert_select 'user[id=3]'
326 end
326 end
327 end
327 end
328 end
328 end
329
329
330 test "POST /issues.xml should create an issue with the attributes" do
330 test "POST /issues.xml should create an issue with the attributes" do
331 assert_difference('Issue.count') do
331 assert_difference('Issue.count') do
332 post '/issues.xml',
332 post '/issues.xml',
333 {:issue => {:project_id => 1, :subject => 'API test',
333 {:issue => {:project_id => 1, :subject => 'API test',
334 :tracker_id => 2, :status_id => 3}}, credentials('jsmith')
334 :tracker_id => 2, :status_id => 3}}, credentials('jsmith')
335 end
335 end
336 issue = Issue.order('id DESC').first
336 issue = Issue.order('id DESC').first
337 assert_equal 1, issue.project_id
337 assert_equal 1, issue.project_id
338 assert_equal 2, issue.tracker_id
338 assert_equal 2, issue.tracker_id
339 assert_equal 3, issue.status_id
339 assert_equal 3, issue.status_id
340 assert_equal 'API test', issue.subject
340 assert_equal 'API test', issue.subject
341
341
342 assert_response :created
342 assert_response :created
343 assert_equal 'application/xml', @response.content_type
343 assert_equal 'application/xml', @response.content_type
344 assert_select 'issue > id', :text => issue.id.to_s
344 assert_select 'issue > id', :text => issue.id.to_s
345 end
345 end
346
346
347 test "POST /issues.xml with watcher_user_ids should create issue with watchers" do
347 test "POST /issues.xml with watcher_user_ids should create issue with watchers" do
348 assert_difference('Issue.count') do
348 assert_difference('Issue.count') do
349 post '/issues.xml',
349 post '/issues.xml',
350 {:issue => {:project_id => 1, :subject => 'Watchers',
350 {:issue => {:project_id => 1, :subject => 'Watchers',
351 :tracker_id => 2, :status_id => 3, :watcher_user_ids => [3, 1]}}, credentials('jsmith')
351 :tracker_id => 2, :status_id => 3, :watcher_user_ids => [3, 1]}}, credentials('jsmith')
352 assert_response :created
352 assert_response :created
353 end
353 end
354 issue = Issue.order('id desc').first
354 issue = Issue.order('id desc').first
355 assert_equal 2, issue.watchers.size
355 assert_equal 2, issue.watchers.size
356 assert_equal [1, 3], issue.watcher_user_ids.sort
356 assert_equal [1, 3], issue.watcher_user_ids.sort
357 end
357 end
358
358
359 test "POST /issues.xml with failure should return errors" do
359 test "POST /issues.xml with failure should return errors" do
360 assert_no_difference('Issue.count') do
360 assert_no_difference('Issue.count') do
361 post '/issues.xml', {:issue => {:project_id => 1}}, credentials('jsmith')
361 post '/issues.xml', {:issue => {:project_id => 1}}, credentials('jsmith')
362 end
362 end
363
363
364 assert_select 'errors error', :text => "Subject cannot be blank"
364 assert_select 'errors error', :text => "Subject cannot be blank"
365 end
365 end
366
366
367 test "POST /issues.json should create an issue with the attributes" do
367 test "POST /issues.json should create an issue with the attributes" do
368 assert_difference('Issue.count') do
368 assert_difference('Issue.count') do
369 post '/issues.json',
369 post '/issues.json',
370 {:issue => {:project_id => 1, :subject => 'API test',
370 {:issue => {:project_id => 1, :subject => 'API test',
371 :tracker_id => 2, :status_id => 3}},
371 :tracker_id => 2, :status_id => 3}},
372 credentials('jsmith')
372 credentials('jsmith')
373 end
373 end
374
374
375 issue = Issue.order('id DESC').first
375 issue = Issue.order('id DESC').first
376 assert_equal 1, issue.project_id
376 assert_equal 1, issue.project_id
377 assert_equal 2, issue.tracker_id
377 assert_equal 2, issue.tracker_id
378 assert_equal 3, issue.status_id
378 assert_equal 3, issue.status_id
379 assert_equal 'API test', issue.subject
379 assert_equal 'API test', issue.subject
380 end
380 end
381
381
382 test "POST /issues.json with failure should return errors" do
382 test "POST /issues.json with failure should return errors" do
383 assert_no_difference('Issue.count') do
383 assert_no_difference('Issue.count') do
384 post '/issues.json', {:issue => {:project_id => 1}}, credentials('jsmith')
384 post '/issues.json', {:issue => {:project_id => 1}}, credentials('jsmith')
385 end
385 end
386
386
387 json = ActiveSupport::JSON.decode(response.body)
387 json = ActiveSupport::JSON.decode(response.body)
388 assert json['errors'].include?("Subject cannot be blank")
388 assert json['errors'].include?("Subject cannot be blank")
389 end
389 end
390
390
391 test "PUT /issues/:id.xml" do
391 test "PUT /issues/:id.xml" do
392 assert_difference('Journal.count') do
392 assert_difference('Journal.count') do
393 put '/issues/6.xml',
393 put '/issues/6.xml',
394 {:issue => {:subject => 'API update', :notes => 'A new note'}},
394 {:issue => {:subject => 'API update', :notes => 'A new note'}},
395 credentials('jsmith')
395 credentials('jsmith')
396 end
396 end
397
397
398 issue = Issue.find(6)
398 issue = Issue.find(6)
399 assert_equal "API update", issue.subject
399 assert_equal "API update", issue.subject
400 journal = Journal.last
400 journal = Journal.last
401 assert_equal "A new note", journal.notes
401 assert_equal "A new note", journal.notes
402 end
402 end
403
403
404 test "PUT /issues/:id.xml with custom fields" do
404 test "PUT /issues/:id.xml with custom fields" do
405 put '/issues/3.xml',
405 put '/issues/3.xml',
406 {:issue => {:custom_fields => [
406 {:issue => {:custom_fields => [
407 {'id' => '1', 'value' => 'PostgreSQL' },
407 {'id' => '1', 'value' => 'PostgreSQL' },
408 {'id' => '2', 'value' => '150'}
408 {'id' => '2', 'value' => '150'}
409 ]}},
409 ]}},
410 credentials('jsmith')
410 credentials('jsmith')
411
411
412 issue = Issue.find(3)
412 issue = Issue.find(3)
413 assert_equal '150', issue.custom_value_for(2).value
413 assert_equal '150', issue.custom_value_for(2).value
414 assert_equal 'PostgreSQL', issue.custom_value_for(1).value
414 assert_equal 'PostgreSQL', issue.custom_value_for(1).value
415 end
415 end
416
416
417 test "PUT /issues/:id.xml with multi custom fields" do
417 test "PUT /issues/:id.xml with multi custom fields" do
418 field = CustomField.find(1)
418 field = CustomField.find(1)
419 field.update_attribute :multiple, true
419 field.update_attribute :multiple, true
420
420
421 put '/issues/3.xml',
421 put '/issues/3.xml',
422 {:issue => {:custom_fields => [
422 {:issue => {:custom_fields => [
423 {'id' => '1', 'value' => ['MySQL', 'PostgreSQL'] },
423 {'id' => '1', 'value' => ['MySQL', 'PostgreSQL'] },
424 {'id' => '2', 'value' => '150'}
424 {'id' => '2', 'value' => '150'}
425 ]}},
425 ]}},
426 credentials('jsmith')
426 credentials('jsmith')
427
427
428 issue = Issue.find(3)
428 issue = Issue.find(3)
429 assert_equal '150', issue.custom_value_for(2).value
429 assert_equal '150', issue.custom_value_for(2).value
430 assert_equal ['MySQL', 'PostgreSQL'], issue.custom_field_value(1).sort
430 assert_equal ['MySQL', 'PostgreSQL'], issue.custom_field_value(1).sort
431 end
431 end
432
432
433 test "PUT /issues/:id.xml with project change" do
433 test "PUT /issues/:id.xml with project change" do
434 put '/issues/3.xml',
434 put '/issues/3.xml',
435 {:issue => {:project_id => 2, :subject => 'Project changed'}},
435 {:issue => {:project_id => 2, :subject => 'Project changed'}},
436 credentials('jsmith')
436 credentials('jsmith')
437
437
438 issue = Issue.find(3)
438 issue = Issue.find(3)
439 assert_equal 2, issue.project_id
439 assert_equal 2, issue.project_id
440 assert_equal 'Project changed', issue.subject
440 assert_equal 'Project changed', issue.subject
441 end
441 end
442
442
443 test "PUT /issues/:id.xml with failed update" do
443 test "PUT /issues/:id.xml with failed update" do
444 put '/issues/6.xml', {:issue => {:subject => ''}}, credentials('jsmith')
444 put '/issues/6.xml', {:issue => {:subject => ''}}, credentials('jsmith')
445
445
446 assert_response :unprocessable_entity
446 assert_response :unprocessable_entity
447 assert_select 'errors error', :text => "Subject cannot be blank"
447 assert_select 'errors error', :text => "Subject cannot be blank"
448 end
448 end
449
449
450 test "PUT /issues/:id.json" do
450 test "PUT /issues/:id.json" do
451 assert_difference('Journal.count') do
451 assert_difference('Journal.count') do
452 put '/issues/6.json',
452 put '/issues/6.json',
453 {:issue => {:subject => 'API update', :notes => 'A new note'}},
453 {:issue => {:subject => 'API update', :notes => 'A new note'}},
454 credentials('jsmith')
454 credentials('jsmith')
455
455
456 assert_response :ok
456 assert_response :ok
457 assert_equal '', response.body
457 assert_equal '', response.body
458 end
458 end
459
459
460 issue = Issue.find(6)
460 issue = Issue.find(6)
461 assert_equal "API update", issue.subject
461 assert_equal "API update", issue.subject
462 journal = Journal.last
462 journal = Journal.last
463 assert_equal "A new note", journal.notes
463 assert_equal "A new note", journal.notes
464 end
464 end
465
465
466 test "PUT /issues/:id.json with failed update" do
466 test "PUT /issues/:id.json with failed update" do
467 put '/issues/6.json', {:issue => {:subject => ''}}, credentials('jsmith')
467 put '/issues/6.json', {:issue => {:subject => ''}}, credentials('jsmith')
468
468
469 assert_response :unprocessable_entity
469 assert_response :unprocessable_entity
470 json = ActiveSupport::JSON.decode(response.body)
470 json = ActiveSupport::JSON.decode(response.body)
471 assert json['errors'].include?("Subject cannot be blank")
471 assert json['errors'].include?("Subject cannot be blank")
472 end
472 end
473
473
474 test "DELETE /issues/:id.xml" do
474 test "DELETE /issues/:id.xml" do
475 assert_difference('Issue.count', -1) do
475 assert_difference('Issue.count', -1) do
476 delete '/issues/6.xml', {}, credentials('jsmith')
476 delete '/issues/6.xml', {}, credentials('jsmith')
477
477
478 assert_response :ok
478 assert_response :ok
479 assert_equal '', response.body
479 assert_equal '', response.body
480 end
480 end
481 assert_nil Issue.find_by_id(6)
481 assert_nil Issue.find_by_id(6)
482 end
482 end
483
483
484 test "DELETE /issues/:id.json" do
484 test "DELETE /issues/:id.json" do
485 assert_difference('Issue.count', -1) do
485 assert_difference('Issue.count', -1) do
486 delete '/issues/6.json', {}, credentials('jsmith')
486 delete '/issues/6.json', {}, credentials('jsmith')
487
487
488 assert_response :ok
488 assert_response :ok
489 assert_equal '', response.body
489 assert_equal '', response.body
490 end
490 end
491 assert_nil Issue.find_by_id(6)
491 assert_nil Issue.find_by_id(6)
492 end
492 end
493
493
494 test "POST /issues/:id/watchers.xml should add watcher" do
494 test "POST /issues/:id/watchers.xml should add watcher" do
495 assert_difference 'Watcher.count' do
495 assert_difference 'Watcher.count' do
496 post '/issues/1/watchers.xml', {:user_id => 3}, credentials('jsmith')
496 post '/issues/1/watchers.xml', {:user_id => 3}, credentials('jsmith')
497
497
498 assert_response :ok
498 assert_response :ok
499 assert_equal '', response.body
499 assert_equal '', response.body
500 end
500 end
501 watcher = Watcher.order('id desc').first
501 watcher = Watcher.order('id desc').first
502 assert_equal Issue.find(1), watcher.watchable
502 assert_equal Issue.find(1), watcher.watchable
503 assert_equal User.find(3), watcher.user
503 assert_equal User.find(3), watcher.user
504 end
504 end
505
505
506 test "DELETE /issues/:id/watchers/:user_id.xml should remove watcher" do
506 test "DELETE /issues/:id/watchers/:user_id.xml should remove watcher" do
507 Watcher.create!(:user_id => 3, :watchable => Issue.find(1))
507 Watcher.create!(:user_id => 3, :watchable => Issue.find(1))
508
508
509 assert_difference 'Watcher.count', -1 do
509 assert_difference 'Watcher.count', -1 do
510 delete '/issues/1/watchers/3.xml', {}, credentials('jsmith')
510 delete '/issues/1/watchers/3.xml', {}, credentials('jsmith')
511
511
512 assert_response :ok
512 assert_response :ok
513 assert_equal '', response.body
513 assert_equal '', response.body
514 end
514 end
515 assert_equal false, Issue.find(1).watched_by?(User.find(3))
515 assert_equal false, Issue.find(1).watched_by?(User.find(3))
516 end
516 end
517
517
518 def test_create_issue_with_uploaded_file
518 def test_create_issue_with_uploaded_file
519 set_tmp_attachments_directory
519 set_tmp_attachments_directory
520 # upload the file
520 # upload the file
521 assert_difference 'Attachment.count' do
521 assert_difference 'Attachment.count' do
522 post '/uploads.xml', 'test_create_with_upload',
522 post '/uploads.xml', 'test_create_with_upload',
523 {"CONTENT_TYPE" => 'application/octet-stream'}.merge(credentials('jsmith'))
523 {"CONTENT_TYPE" => 'application/octet-stream'}.merge(credentials('jsmith'))
524 assert_response :created
524 assert_response :created
525 end
525 end
526 xml = Hash.from_xml(response.body)
526 xml = Hash.from_xml(response.body)
527 token = xml['upload']['token']
527 token = xml['upload']['token']
528 attachment = Attachment.order('id DESC').first
528 attachment = Attachment.order('id DESC').first
529
529
530 # create the issue with the upload's token
530 # create the issue with the upload's token
531 assert_difference 'Issue.count' do
531 assert_difference 'Issue.count' do
532 post '/issues.xml',
532 post '/issues.xml',
533 {:issue => {:project_id => 1, :subject => 'Uploaded file',
533 {:issue => {:project_id => 1, :subject => 'Uploaded file',
534 :uploads => [{:token => token, :filename => 'test.txt',
534 :uploads => [{:token => token, :filename => 'test.txt',
535 :content_type => 'text/plain'}]}},
535 :content_type => 'text/plain'}]}},
536 credentials('jsmith')
536 credentials('jsmith')
537 assert_response :created
537 assert_response :created
538 end
538 end
539 issue = Issue.order('id DESC').first
539 issue = Issue.order('id DESC').first
540 assert_equal 1, issue.attachments.count
540 assert_equal 1, issue.attachments.count
541 assert_equal attachment, issue.attachments.first
541 assert_equal attachment, issue.attachments.first
542
542
543 attachment.reload
543 attachment.reload
544 assert_equal 'test.txt', attachment.filename
544 assert_equal 'test.txt', attachment.filename
545 assert_equal 'text/plain', attachment.content_type
545 assert_equal 'text/plain', attachment.content_type
546 assert_equal 'test_create_with_upload'.size, attachment.filesize
546 assert_equal 'test_create_with_upload'.size, attachment.filesize
547 assert_equal 2, attachment.author_id
547 assert_equal 2, attachment.author_id
548
548
549 # get the issue with its attachments
549 # get the issue with its attachments
550 get "/issues/#{issue.id}.xml", :include => 'attachments'
550 get "/issues/#{issue.id}.xml", :include => 'attachments'
551 assert_response :success
551 assert_response :success
552 xml = Hash.from_xml(response.body)
552 xml = Hash.from_xml(response.body)
553 attachments = xml['issue']['attachments']
553 attachments = xml['issue']['attachments']
554 assert_kind_of Array, attachments
554 assert_kind_of Array, attachments
555 assert_equal 1, attachments.size
555 assert_equal 1, attachments.size
556 url = attachments.first['content_url']
556 url = attachments.first['content_url']
557 assert_not_nil url
557 assert_not_nil url
558
558
559 # download the attachment
559 # download the attachment
560 get url
560 get url
561 assert_response :success
561 assert_response :success
562 end
562 end
563
563
564 def test_create_issue_with_multiple_uploaded_files_as_xml
565 token1 = xml_upload('File content 1', credentials('jsmith'))
566 token2 = xml_upload('File content 2', credentials('jsmith'))
567
568 payload = <<-XML
569 <?xml version="1.0" encoding="UTF-8" ?>
570 <issue>
571 <project_id>1</project_id>
572 <tracker_id>1</tracker_id>
573 <subject>Issue with multiple attachments</subject>
574 <uploads type="array">
575 <upload>
576 <token>#{token1}</token>
577 <filename>test1.txt</filename>
578 </upload>
579 <upload>
580 <token>#{token2}</token>
581 <filename>test1.txt</filename>
582 </upload>
583 </uploads>
584 </issue>
585 XML
586
587 assert_difference 'Issue.count' do
588 post '/issues.xml', payload, {"CONTENT_TYPE" => 'application/xml'}.merge(credentials('jsmith'))
589 assert_response :created
590 end
591 issue = Issue.order('id DESC').first
592 assert_equal 2, issue.attachments.count
593 end
594
595 def test_create_issue_with_multiple_uploaded_files_as_json
596 token1 = json_upload('File content 1', credentials('jsmith'))
597 token2 = json_upload('File content 2', credentials('jsmith'))
598
599 payload = <<-JSON
600 {
601 "issue": {
602 "project_id": "1",
603 "tracker_id": "1",
604 "subject": "Issue with multiple attachments",
605 "uploads": [
606 {"token": "#{token1}", "filename": "test1.txt"},
607 {"token": "#{token2}", "filename": "test2.txt"}
608 ]
609 }
610 }
611 JSON
612
613 assert_difference 'Issue.count' do
614 post '/issues.json', payload, {"CONTENT_TYPE" => 'application/json'}.merge(credentials('jsmith'))
615 assert_response :created
616 end
617 issue = Issue.order('id DESC').first
618 assert_equal 2, issue.attachments.count
619 end
620
564 def test_update_issue_with_uploaded_file
621 def test_update_issue_with_uploaded_file
565 set_tmp_attachments_directory
622 set_tmp_attachments_directory
566 # upload the file
623 # upload the file
567 assert_difference 'Attachment.count' do
624 assert_difference 'Attachment.count' do
568 post '/uploads.xml', 'test_upload_with_upload',
625 post '/uploads.xml', 'test_upload_with_upload',
569 {"CONTENT_TYPE" => 'application/octet-stream'}.merge(credentials('jsmith'))
626 {"CONTENT_TYPE" => 'application/octet-stream'}.merge(credentials('jsmith'))
570 assert_response :created
627 assert_response :created
571 end
628 end
572 xml = Hash.from_xml(response.body)
629 xml = Hash.from_xml(response.body)
573 token = xml['upload']['token']
630 token = xml['upload']['token']
574 attachment = Attachment.order('id DESC').first
631 attachment = Attachment.order('id DESC').first
575
632
576 # update the issue with the upload's token
633 # update the issue with the upload's token
577 assert_difference 'Journal.count' do
634 assert_difference 'Journal.count' do
578 put '/issues/1.xml',
635 put '/issues/1.xml',
579 {:issue => {:notes => 'Attachment added',
636 {:issue => {:notes => 'Attachment added',
580 :uploads => [{:token => token, :filename => 'test.txt',
637 :uploads => [{:token => token, :filename => 'test.txt',
581 :content_type => 'text/plain'}]}},
638 :content_type => 'text/plain'}]}},
582 credentials('jsmith')
639 credentials('jsmith')
583 assert_response :ok
640 assert_response :ok
584 assert_equal '', @response.body
641 assert_equal '', @response.body
585 end
642 end
586
643
587 issue = Issue.find(1)
644 issue = Issue.find(1)
588 assert_include attachment, issue.attachments
645 assert_include attachment, issue.attachments
589 end
646 end
590 end
647 end
@@ -1,290 +1,329
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2014 Jean-Philippe Lang
2 # Copyright (C) 2006-2014 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 if ENV["COVERAGE"]
18 if ENV["COVERAGE"]
19 require 'simplecov'
19 require 'simplecov'
20 require File.expand_path(File.dirname(__FILE__) + "/coverage/html_formatter")
20 require File.expand_path(File.dirname(__FILE__) + "/coverage/html_formatter")
21 SimpleCov.formatter = Redmine::Coverage::HtmlFormatter
21 SimpleCov.formatter = Redmine::Coverage::HtmlFormatter
22 SimpleCov.start 'rails'
22 SimpleCov.start 'rails'
23 end
23 end
24
24
25 ENV["RAILS_ENV"] = "test"
25 ENV["RAILS_ENV"] = "test"
26 require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
26 require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
27 require 'rails/test_help'
27 require 'rails/test_help'
28 require Rails.root.join('test', 'mocks', 'open_id_authentication_mock.rb').to_s
28 require Rails.root.join('test', 'mocks', 'open_id_authentication_mock.rb').to_s
29
29
30 require File.expand_path(File.dirname(__FILE__) + '/object_helpers')
30 require File.expand_path(File.dirname(__FILE__) + '/object_helpers')
31 include ObjectHelpers
31 include ObjectHelpers
32
32
33 require 'awesome_nested_set/version'
33 require 'awesome_nested_set/version'
34 require 'net/ldap'
34 require 'net/ldap'
35
35
36 class ActionView::TestCase
36 class ActionView::TestCase
37 helper :application
37 helper :application
38 include ApplicationHelper
38 include ApplicationHelper
39 end
39 end
40
40
41 class ActiveSupport::TestCase
41 class ActiveSupport::TestCase
42 include ActionDispatch::TestProcess
42 include ActionDispatch::TestProcess
43
43
44 self.use_transactional_fixtures = true
44 self.use_transactional_fixtures = true
45 self.use_instantiated_fixtures = false
45 self.use_instantiated_fixtures = false
46
46
47 def uploaded_test_file(name, mime)
47 def uploaded_test_file(name, mime)
48 fixture_file_upload("files/#{name}", mime, true)
48 fixture_file_upload("files/#{name}", mime, true)
49 end
49 end
50
50
51 # Mock out a file
51 # Mock out a file
52 def self.mock_file
52 def self.mock_file
53 file = 'a_file.png'
53 file = 'a_file.png'
54 file.stubs(:size).returns(32)
54 file.stubs(:size).returns(32)
55 file.stubs(:original_filename).returns('a_file.png')
55 file.stubs(:original_filename).returns('a_file.png')
56 file.stubs(:content_type).returns('image/png')
56 file.stubs(:content_type).returns('image/png')
57 file.stubs(:read).returns(false)
57 file.stubs(:read).returns(false)
58 file
58 file
59 end
59 end
60
60
61 def mock_file
61 def mock_file
62 self.class.mock_file
62 self.class.mock_file
63 end
63 end
64
64
65 def mock_file_with_options(options={})
65 def mock_file_with_options(options={})
66 file = ''
66 file = ''
67 file.stubs(:size).returns(32)
67 file.stubs(:size).returns(32)
68 original_filename = options[:original_filename] || nil
68 original_filename = options[:original_filename] || nil
69 file.stubs(:original_filename).returns(original_filename)
69 file.stubs(:original_filename).returns(original_filename)
70 content_type = options[:content_type] || nil
70 content_type = options[:content_type] || nil
71 file.stubs(:content_type).returns(content_type)
71 file.stubs(:content_type).returns(content_type)
72 file.stubs(:read).returns(false)
72 file.stubs(:read).returns(false)
73 file
73 file
74 end
74 end
75
75
76 # Use a temporary directory for attachment related tests
76 # Use a temporary directory for attachment related tests
77 def set_tmp_attachments_directory
77 def set_tmp_attachments_directory
78 Dir.mkdir "#{Rails.root}/tmp/test" unless File.directory?("#{Rails.root}/tmp/test")
78 Dir.mkdir "#{Rails.root}/tmp/test" unless File.directory?("#{Rails.root}/tmp/test")
79 unless File.directory?("#{Rails.root}/tmp/test/attachments")
79 unless File.directory?("#{Rails.root}/tmp/test/attachments")
80 Dir.mkdir "#{Rails.root}/tmp/test/attachments"
80 Dir.mkdir "#{Rails.root}/tmp/test/attachments"
81 end
81 end
82 Attachment.storage_path = "#{Rails.root}/tmp/test/attachments"
82 Attachment.storage_path = "#{Rails.root}/tmp/test/attachments"
83 end
83 end
84
84
85 def set_fixtures_attachments_directory
85 def set_fixtures_attachments_directory
86 Attachment.storage_path = "#{Rails.root}/test/fixtures/files"
86 Attachment.storage_path = "#{Rails.root}/test/fixtures/files"
87 end
87 end
88
88
89 def with_settings(options, &block)
89 def with_settings(options, &block)
90 saved_settings = options.keys.inject({}) do |h, k|
90 saved_settings = options.keys.inject({}) do |h, k|
91 h[k] = case Setting[k]
91 h[k] = case Setting[k]
92 when Symbol, false, true, nil
92 when Symbol, false, true, nil
93 Setting[k]
93 Setting[k]
94 else
94 else
95 Setting[k].dup
95 Setting[k].dup
96 end
96 end
97 h
97 h
98 end
98 end
99 options.each {|k, v| Setting[k] = v}
99 options.each {|k, v| Setting[k] = v}
100 yield
100 yield
101 ensure
101 ensure
102 saved_settings.each {|k, v| Setting[k] = v} if saved_settings
102 saved_settings.each {|k, v| Setting[k] = v} if saved_settings
103 end
103 end
104
104
105 # Yields the block with user as the current user
105 # Yields the block with user as the current user
106 def with_current_user(user, &block)
106 def with_current_user(user, &block)
107 saved_user = User.current
107 saved_user = User.current
108 User.current = user
108 User.current = user
109 yield
109 yield
110 ensure
110 ensure
111 User.current = saved_user
111 User.current = saved_user
112 end
112 end
113
113
114 def with_locale(locale, &block)
114 def with_locale(locale, &block)
115 saved_localed = ::I18n.locale
115 saved_localed = ::I18n.locale
116 ::I18n.locale = locale
116 ::I18n.locale = locale
117 yield
117 yield
118 ensure
118 ensure
119 ::I18n.locale = saved_localed
119 ::I18n.locale = saved_localed
120 end
120 end
121
121
122 def self.ldap_configured?
122 def self.ldap_configured?
123 @test_ldap = Net::LDAP.new(:host => '127.0.0.1', :port => 389)
123 @test_ldap = Net::LDAP.new(:host => '127.0.0.1', :port => 389)
124 return @test_ldap.bind
124 return @test_ldap.bind
125 rescue Exception => e
125 rescue Exception => e
126 # LDAP is not listening
126 # LDAP is not listening
127 return nil
127 return nil
128 end
128 end
129
129
130 def self.convert_installed?
130 def self.convert_installed?
131 Redmine::Thumbnail.convert_available?
131 Redmine::Thumbnail.convert_available?
132 end
132 end
133
133
134 # Returns the path to the test +vendor+ repository
134 # Returns the path to the test +vendor+ repository
135 def self.repository_path(vendor)
135 def self.repository_path(vendor)
136 path = Rails.root.join("tmp/test/#{vendor.downcase}_repository").to_s
136 path = Rails.root.join("tmp/test/#{vendor.downcase}_repository").to_s
137 # Unlike ruby, JRuby returns Rails.root with backslashes under Windows
137 # Unlike ruby, JRuby returns Rails.root with backslashes under Windows
138 path.tr("\\", "/")
138 path.tr("\\", "/")
139 end
139 end
140
140
141 # Returns the url of the subversion test repository
141 # Returns the url of the subversion test repository
142 def self.subversion_repository_url
142 def self.subversion_repository_url
143 path = repository_path('subversion')
143 path = repository_path('subversion')
144 path = '/' + path unless path.starts_with?('/')
144 path = '/' + path unless path.starts_with?('/')
145 "file://#{path}"
145 "file://#{path}"
146 end
146 end
147
147
148 # Returns true if the +vendor+ test repository is configured
148 # Returns true if the +vendor+ test repository is configured
149 def self.repository_configured?(vendor)
149 def self.repository_configured?(vendor)
150 File.directory?(repository_path(vendor))
150 File.directory?(repository_path(vendor))
151 end
151 end
152
152
153 def repository_path_hash(arr)
153 def repository_path_hash(arr)
154 hs = {}
154 hs = {}
155 hs[:path] = arr.join("/")
155 hs[:path] = arr.join("/")
156 hs[:param] = arr.join("/")
156 hs[:param] = arr.join("/")
157 hs
157 hs
158 end
158 end
159
159
160 def sqlite?
160 def sqlite?
161 ActiveRecord::Base.connection.adapter_name =~ /sqlite/i
161 ActiveRecord::Base.connection.adapter_name =~ /sqlite/i
162 end
162 end
163
163
164 def mysql?
164 def mysql?
165 ActiveRecord::Base.connection.adapter_name =~ /mysql/i
165 ActiveRecord::Base.connection.adapter_name =~ /mysql/i
166 end
166 end
167
167
168 def assert_save(object)
168 def assert_save(object)
169 saved = object.save
169 saved = object.save
170 message = "#{object.class} could not be saved"
170 message = "#{object.class} could not be saved"
171 errors = object.errors.full_messages.map {|m| "- #{m}"}
171 errors = object.errors.full_messages.map {|m| "- #{m}"}
172 message << ":\n#{errors.join("\n")}" if errors.any?
172 message << ":\n#{errors.join("\n")}" if errors.any?
173 assert_equal true, saved, message
173 assert_equal true, saved, message
174 end
174 end
175
175
176 def assert_select_error(arg)
176 def assert_select_error(arg)
177 assert_select '#errorExplanation', :text => arg
177 assert_select '#errorExplanation', :text => arg
178 end
178 end
179
179
180 def assert_include(expected, s, message=nil)
180 def assert_include(expected, s, message=nil)
181 assert s.include?(expected), (message || "\"#{expected}\" not found in \"#{s}\"")
181 assert s.include?(expected), (message || "\"#{expected}\" not found in \"#{s}\"")
182 end
182 end
183
183
184 def assert_not_include(expected, s, message=nil)
184 def assert_not_include(expected, s, message=nil)
185 assert !s.include?(expected), (message || "\"#{expected}\" found in \"#{s}\"")
185 assert !s.include?(expected), (message || "\"#{expected}\" found in \"#{s}\"")
186 end
186 end
187
187
188 def assert_select_in(text, *args, &block)
188 def assert_select_in(text, *args, &block)
189 d = HTML::Document.new(CGI::unescapeHTML(String.new(text))).root
189 d = HTML::Document.new(CGI::unescapeHTML(String.new(text))).root
190 assert_select(d, *args, &block)
190 assert_select(d, *args, &block)
191 end
191 end
192
192
193 def assert_mail_body_match(expected, mail, message=nil)
193 def assert_mail_body_match(expected, mail, message=nil)
194 if expected.is_a?(String)
194 if expected.is_a?(String)
195 assert_include expected, mail_body(mail), message
195 assert_include expected, mail_body(mail), message
196 else
196 else
197 assert_match expected, mail_body(mail), message
197 assert_match expected, mail_body(mail), message
198 end
198 end
199 end
199 end
200
200
201 def assert_mail_body_no_match(expected, mail, message=nil)
201 def assert_mail_body_no_match(expected, mail, message=nil)
202 if expected.is_a?(String)
202 if expected.is_a?(String)
203 assert_not_include expected, mail_body(mail), message
203 assert_not_include expected, mail_body(mail), message
204 else
204 else
205 assert_no_match expected, mail_body(mail), message
205 assert_no_match expected, mail_body(mail), message
206 end
206 end
207 end
207 end
208
208
209 def mail_body(mail)
209 def mail_body(mail)
210 mail.parts.first.body.encoded
210 mail.parts.first.body.encoded
211 end
211 end
212
212
213 # awesome_nested_set new node lft and rgt value changed this refactor revision.
213 # awesome_nested_set new node lft and rgt value changed this refactor revision.
214 # https://github.com/collectiveidea/awesome_nested_set/commit/199fca9bb938e40200cd90714dc69247ef017c61
214 # https://github.com/collectiveidea/awesome_nested_set/commit/199fca9bb938e40200cd90714dc69247ef017c61
215 # The reason of behavior change is that "self.class.base_class.unscoped" was added to this line.
215 # The reason of behavior change is that "self.class.base_class.unscoped" was added to this line.
216 # https://github.com/collectiveidea/awesome_nested_set/commit/199fca9bb9#diff-f61b59a5e6319024e211b0ffdd0e4ef1R273
216 # https://github.com/collectiveidea/awesome_nested_set/commit/199fca9bb9#diff-f61b59a5e6319024e211b0ffdd0e4ef1R273
217 # It seems correct behavior because of this line comment.
217 # It seems correct behavior because of this line comment.
218 # https://github.com/collectiveidea/awesome_nested_set/blame/199fca9bb9/lib/awesome_nested_set/model.rb#L278
218 # https://github.com/collectiveidea/awesome_nested_set/blame/199fca9bb9/lib/awesome_nested_set/model.rb#L278
219 def new_issue_lft
219 def new_issue_lft
220 # ::AwesomeNestedSet::VERSION > "2.1.6" ? Issue.maximum(:rgt) + 1 : 1
220 # ::AwesomeNestedSet::VERSION > "2.1.6" ? Issue.maximum(:rgt) + 1 : 1
221 Issue.maximum(:rgt) + 1
221 Issue.maximum(:rgt) + 1
222 end
222 end
223 end
223 end
224
224
225 module Redmine
225 module Redmine
226 class RoutingTest < ActionDispatch::IntegrationTest
226 class RoutingTest < ActionDispatch::IntegrationTest
227 def should_route(arg)
227 def should_route(arg)
228 arg = arg.dup
228 arg = arg.dup
229 request = arg.keys.detect {|key| key.is_a?(String)}
229 request = arg.keys.detect {|key| key.is_a?(String)}
230 raise ArgumentError unless request
230 raise ArgumentError unless request
231 options = arg.slice!(request)
231 options = arg.slice!(request)
232
232
233 raise ArgumentError unless request =~ /\A(GET|POST|PUT|PATCH|DELETE)\s+(.+)\z/
233 raise ArgumentError unless request =~ /\A(GET|POST|PUT|PATCH|DELETE)\s+(.+)\z/
234 method, path = $1.downcase.to_sym, $2
234 method, path = $1.downcase.to_sym, $2
235
235
236 raise ArgumentError unless arg.values.first =~ /\A(.+)#(.+)\z/
236 raise ArgumentError unless arg.values.first =~ /\A(.+)#(.+)\z/
237 controller, action = $1, $2
237 controller, action = $1, $2
238
238
239 assert_routing(
239 assert_routing(
240 {:method => method, :path => path},
240 {:method => method, :path => path},
241 options.merge(:controller => controller, :action => action)
241 options.merge(:controller => controller, :action => action)
242 )
242 )
243 end
243 end
244 end
244 end
245
245
246 class IntegrationTest < ActionDispatch::IntegrationTest
246 class IntegrationTest < ActionDispatch::IntegrationTest
247 def log_user(login, password)
247 def log_user(login, password)
248 User.anonymous
248 User.anonymous
249 get "/login"
249 get "/login"
250 assert_equal nil, session[:user_id]
250 assert_equal nil, session[:user_id]
251 assert_response :success
251 assert_response :success
252 assert_template "account/login"
252 assert_template "account/login"
253 post "/login", :username => login, :password => password
253 post "/login", :username => login, :password => password
254 assert_equal login, User.find(session[:user_id]).login
254 assert_equal login, User.find(session[:user_id]).login
255 end
255 end
256
256
257 def credentials(user, password=nil)
257 def credentials(user, password=nil)
258 {'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Basic.encode_credentials(user, password || user)}
258 {'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Basic.encode_credentials(user, password || user)}
259 end
259 end
260 end
260 end
261
261
262 module ApiTest
262 module ApiTest
263 API_FORMATS = %w(json xml).freeze
263 API_FORMATS = %w(json xml).freeze
264
264
265 # Base class for API tests
265 # Base class for API tests
266 class Base < Redmine::IntegrationTest
266 class Base < Redmine::IntegrationTest
267 def setup
267 def setup
268 Setting.rest_api_enabled = '1'
268 Setting.rest_api_enabled = '1'
269 end
269 end
270
270
271 def teardown
271 def teardown
272 Setting.rest_api_enabled = '0'
272 Setting.rest_api_enabled = '0'
273 end
273 end
274
275 # Uploads content using the XML API and returns the attachment token
276 def xml_upload(content, credentials)
277 upload('xml', content, credentials)
278 end
279
280 # Uploads content using the JSON API and returns the attachment token
281 def json_upload(content, credentials)
282 upload('json', content, credentials)
283 end
284
285 def upload(format, content, credentials)
286 set_tmp_attachments_directory
287 assert_difference 'Attachment.count' do
288 post "/uploads.#{format}", content, {"CONTENT_TYPE" => 'application/octet-stream'}.merge(credentials)
289 assert_response :created
290 end
291 data = response_data
292 assert_kind_of Hash, data['upload']
293 token = data['upload']['token']
294 assert_not_nil token
295 token
296 end
297
298 # Parses the response body based on its content type
299 def response_data
300 unless response.content_type.to_s =~ /^application\/(.+)/
301 raise "Unexpected response type: #{response.content_type}"
302 end
303 format = $1
304 case format
305 when 'xml'
306 Hash.from_xml(response.body)
307 when 'json'
308 ActiveSupport::JSON.decode(response.body)
309 else
310 raise "Unknown response format: #{format}"
311 end
312 end
274 end
313 end
275
314
276 class Routing < Redmine::RoutingTest
315 class Routing < Redmine::RoutingTest
277 def should_route(arg)
316 def should_route(arg)
278 arg = arg.dup
317 arg = arg.dup
279 request = arg.keys.detect {|key| key.is_a?(String)}
318 request = arg.keys.detect {|key| key.is_a?(String)}
280 raise ArgumentError unless request
319 raise ArgumentError unless request
281 options = arg.slice!(request)
320 options = arg.slice!(request)
282
321
283 API_FORMATS.each do |format|
322 API_FORMATS.each do |format|
284 format_request = request.sub /$/, ".#{format}"
323 format_request = request.sub /$/, ".#{format}"
285 super options.merge(format_request => arg[request], :format => format)
324 super options.merge(format_request => arg[request], :format => format)
286 end
325 end
287 end
326 end
288 end
327 end
289 end
328 end
290 end
329 end
General Comments 0
You need to be logged in to leave comments. Login now