##// END OF EJS Templates
Merged r10571 from trunk...
Toshi MARUYAMA -
r10365:46a76ecb6ed5
parent child
Show More
@@ -1,764 +1,765
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2012 Jean-Philippe Lang
2 # Copyright (C) 2006-2012 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class ApiTest::IssuesTest < ActionController::IntegrationTest
20 class ApiTest::IssuesTest < ActionController::IntegrationTest
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 :versions,
29 :versions,
29 :trackers,
30 :trackers,
30 :projects_trackers,
31 :projects_trackers,
31 :issue_categories,
32 :issue_categories,
32 :enabled_modules,
33 :enabled_modules,
33 :enumerations,
34 :enumerations,
34 :attachments,
35 :attachments,
35 :workflows,
36 :workflows,
36 :custom_fields,
37 :custom_fields,
37 :custom_values,
38 :custom_values,
38 :custom_fields_projects,
39 :custom_fields_projects,
39 :custom_fields_trackers,
40 :custom_fields_trackers,
40 :time_entries,
41 :time_entries,
41 :journals,
42 :journals,
42 :journal_details,
43 :journal_details,
43 :queries,
44 :queries,
44 :attachments
45 :attachments
45
46
46 def setup
47 def setup
47 Setting.rest_api_enabled = '1'
48 Setting.rest_api_enabled = '1'
48 end
49 end
49
50
50 context "/issues" do
51 context "/issues" do
51 # Use a private project to make sure auth is really working and not just
52 # Use a private project to make sure auth is really working and not just
52 # only showing public issues.
53 # only showing public issues.
53 should_allow_api_authentication(:get, "/projects/private-child/issues.xml")
54 should_allow_api_authentication(:get, "/projects/private-child/issues.xml")
54
55
55 should "contain metadata" do
56 should "contain metadata" do
56 get '/issues.xml'
57 get '/issues.xml'
57
58
58 assert_tag :tag => 'issues',
59 assert_tag :tag => 'issues',
59 :attributes => {
60 :attributes => {
60 :type => 'array',
61 :type => 'array',
61 :total_count => assigns(:issue_count),
62 :total_count => assigns(:issue_count),
62 :limit => 25,
63 :limit => 25,
63 :offset => 0
64 :offset => 0
64 }
65 }
65 end
66 end
66
67
67 context "with offset and limit" do
68 context "with offset and limit" do
68 should "use the params" do
69 should "use the params" do
69 get '/issues.xml?offset=2&limit=3'
70 get '/issues.xml?offset=2&limit=3'
70
71
71 assert_equal 3, assigns(:limit)
72 assert_equal 3, assigns(:limit)
72 assert_equal 2, assigns(:offset)
73 assert_equal 2, assigns(:offset)
73 assert_tag :tag => 'issues', :children => {:count => 3, :only => {:tag => 'issue'}}
74 assert_tag :tag => 'issues', :children => {:count => 3, :only => {:tag => 'issue'}}
74 end
75 end
75 end
76 end
76
77
77 context "with nometa param" do
78 context "with nometa param" do
78 should "not contain metadata" do
79 should "not contain metadata" do
79 get '/issues.xml?nometa=1'
80 get '/issues.xml?nometa=1'
80
81
81 assert_tag :tag => 'issues',
82 assert_tag :tag => 'issues',
82 :attributes => {
83 :attributes => {
83 :type => 'array',
84 :type => 'array',
84 :total_count => nil,
85 :total_count => nil,
85 :limit => nil,
86 :limit => nil,
86 :offset => nil
87 :offset => nil
87 }
88 }
88 end
89 end
89 end
90 end
90
91
91 context "with nometa header" do
92 context "with nometa header" do
92 should "not contain metadata" do
93 should "not contain metadata" do
93 get '/issues.xml', {}, {'X-Redmine-Nometa' => '1'}
94 get '/issues.xml', {}, {'X-Redmine-Nometa' => '1'}
94
95
95 assert_tag :tag => 'issues',
96 assert_tag :tag => 'issues',
96 :attributes => {
97 :attributes => {
97 :type => 'array',
98 :type => 'array',
98 :total_count => nil,
99 :total_count => nil,
99 :limit => nil,
100 :limit => nil,
100 :offset => nil
101 :offset => nil
101 }
102 }
102 end
103 end
103 end
104 end
104
105
105 context "with relations" do
106 context "with relations" do
106 should "display relations" do
107 should "display relations" do
107 get '/issues.xml?include=relations'
108 get '/issues.xml?include=relations'
108
109
109 assert_response :success
110 assert_response :success
110 assert_equal 'application/xml', @response.content_type
111 assert_equal 'application/xml', @response.content_type
111 assert_tag 'relations',
112 assert_tag 'relations',
112 :parent => {:tag => 'issue', :child => {:tag => 'id', :content => '3'}},
113 :parent => {:tag => 'issue', :child => {:tag => 'id', :content => '3'}},
113 :children => {:count => 1},
114 :children => {:count => 1},
114 :child => {
115 :child => {
115 :tag => 'relation',
116 :tag => 'relation',
116 :attributes => {:id => '2', :issue_id => '2', :issue_to_id => '3', :relation_type => 'relates'}
117 :attributes => {:id => '2', :issue_id => '2', :issue_to_id => '3', :relation_type => 'relates'}
117 }
118 }
118 assert_tag 'relations',
119 assert_tag 'relations',
119 :parent => {:tag => 'issue', :child => {:tag => 'id', :content => '1'}},
120 :parent => {:tag => 'issue', :child => {:tag => 'id', :content => '1'}},
120 :children => {:count => 0}
121 :children => {:count => 0}
121 end
122 end
122 end
123 end
123
124
124 context "with invalid query params" do
125 context "with invalid query params" do
125 should "return errors" do
126 should "return errors" do
126 get '/issues.xml', {:f => ['start_date'], :op => {:start_date => '='}}
127 get '/issues.xml', {:f => ['start_date'], :op => {:start_date => '='}}
127
128
128 assert_response :unprocessable_entity
129 assert_response :unprocessable_entity
129 assert_equal 'application/xml', @response.content_type
130 assert_equal 'application/xml', @response.content_type
130 assert_tag 'errors', :child => {:tag => 'error', :content => "Start date can't be blank"}
131 assert_tag 'errors', :child => {:tag => 'error', :content => "Start date can't be blank"}
131 end
132 end
132 end
133 end
133
134
134 context "with custom field filter" do
135 context "with custom field filter" do
135 should "show only issues with the custom field value" do
136 should "show only issues with the custom field value" do
136 get '/issues.xml', { :set_filter => 1, :f => ['cf_1'], :op => {:cf_1 => '='}, :v => {:cf_1 => ['MySQL']}}
137 get '/issues.xml', { :set_filter => 1, :f => ['cf_1'], :op => {:cf_1 => '='}, :v => {:cf_1 => ['MySQL']}}
137
138
138 expected_ids = Issue.visible.all(
139 expected_ids = Issue.visible.all(
139 :include => :custom_values,
140 :include => :custom_values,
140 :conditions => {:custom_values => {:custom_field_id => 1, :value => 'MySQL'}}).map(&:id)
141 :conditions => {:custom_values => {:custom_field_id => 1, :value => 'MySQL'}}).map(&:id)
141
142
142 assert_select 'issues > issue > id', :count => expected_ids.count do |ids|
143 assert_select 'issues > issue > id', :count => expected_ids.count do |ids|
143 ids.each { |id| assert expected_ids.delete(id.children.first.content.to_i) }
144 ids.each { |id| assert expected_ids.delete(id.children.first.content.to_i) }
144 end
145 end
145 end
146 end
146 end
147 end
147
148
148 context "with custom field filter (shorthand method)" do
149 context "with custom field filter (shorthand method)" do
149 should "show only issues with the custom field value" do
150 should "show only issues with the custom field value" do
150 get '/issues.xml', { :cf_1 => 'MySQL' }
151 get '/issues.xml', { :cf_1 => 'MySQL' }
151
152
152 expected_ids = Issue.visible.all(
153 expected_ids = Issue.visible.all(
153 :include => :custom_values,
154 :include => :custom_values,
154 :conditions => {:custom_values => {:custom_field_id => 1, :value => 'MySQL'}}).map(&:id)
155 :conditions => {:custom_values => {:custom_field_id => 1, :value => 'MySQL'}}).map(&:id)
155
156
156 assert_select 'issues > issue > id', :count => expected_ids.count do |ids|
157 assert_select 'issues > issue > id', :count => expected_ids.count do |ids|
157 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) }
158 end
159 end
159 end
160 end
160 end
161 end
161 end
162 end
162
163
163 context "/index.json" do
164 context "/index.json" do
164 should_allow_api_authentication(:get, "/projects/private-child/issues.json")
165 should_allow_api_authentication(:get, "/projects/private-child/issues.json")
165 end
166 end
166
167
167 context "/index.xml with filter" do
168 context "/index.xml with filter" do
168 should "show only issues with the status_id" do
169 should "show only issues with the status_id" do
169 get '/issues.xml?status_id=5'
170 get '/issues.xml?status_id=5'
170
171
171 expected_ids = Issue.visible.all(:conditions => {:status_id => 5}).map(&:id)
172 expected_ids = Issue.visible.all(:conditions => {:status_id => 5}).map(&:id)
172
173
173 assert_select 'issues > issue > id', :count => expected_ids.count do |ids|
174 assert_select 'issues > issue > id', :count => expected_ids.count do |ids|
174 ids.each { |id| assert expected_ids.delete(id.children.first.content.to_i) }
175 ids.each { |id| assert expected_ids.delete(id.children.first.content.to_i) }
175 end
176 end
176 end
177 end
177 end
178 end
178
179
179 context "/index.json with filter" do
180 context "/index.json with filter" do
180 should "show only issues with the status_id" do
181 should "show only issues with the status_id" do
181 get '/issues.json?status_id=5'
182 get '/issues.json?status_id=5'
182
183
183 json = ActiveSupport::JSON.decode(response.body)
184 json = ActiveSupport::JSON.decode(response.body)
184 status_ids_used = json['issues'].collect {|j| j['status']['id'] }
185 status_ids_used = json['issues'].collect {|j| j['status']['id'] }
185 assert_equal 3, status_ids_used.length
186 assert_equal 3, status_ids_used.length
186 assert status_ids_used.all? {|id| id == 5 }
187 assert status_ids_used.all? {|id| id == 5 }
187 end
188 end
188
189
189 end
190 end
190
191
191 # Issue 6 is on a private project
192 # Issue 6 is on a private project
192 context "/issues/6.xml" do
193 context "/issues/6.xml" do
193 should_allow_api_authentication(:get, "/issues/6.xml")
194 should_allow_api_authentication(:get, "/issues/6.xml")
194 end
195 end
195
196
196 context "/issues/6.json" do
197 context "/issues/6.json" do
197 should_allow_api_authentication(:get, "/issues/6.json")
198 should_allow_api_authentication(:get, "/issues/6.json")
198 end
199 end
199
200
200 context "GET /issues/:id" do
201 context "GET /issues/:id" do
201 context "with journals" do
202 context "with journals" do
202 context ".xml" do
203 context ".xml" do
203 should "display journals" do
204 should "display journals" do
204 get '/issues/1.xml?include=journals'
205 get '/issues/1.xml?include=journals'
205
206
206 assert_tag :tag => 'issue',
207 assert_tag :tag => 'issue',
207 :child => {
208 :child => {
208 :tag => 'journals',
209 :tag => 'journals',
209 :attributes => { :type => 'array' },
210 :attributes => { :type => 'array' },
210 :child => {
211 :child => {
211 :tag => 'journal',
212 :tag => 'journal',
212 :attributes => { :id => '1'},
213 :attributes => { :id => '1'},
213 :child => {
214 :child => {
214 :tag => 'details',
215 :tag => 'details',
215 :attributes => { :type => 'array' },
216 :attributes => { :type => 'array' },
216 :child => {
217 :child => {
217 :tag => 'detail',
218 :tag => 'detail',
218 :attributes => { :name => 'status_id' },
219 :attributes => { :name => 'status_id' },
219 :child => {
220 :child => {
220 :tag => 'old_value',
221 :tag => 'old_value',
221 :content => '1',
222 :content => '1',
222 :sibling => {
223 :sibling => {
223 :tag => 'new_value',
224 :tag => 'new_value',
224 :content => '2'
225 :content => '2'
225 }
226 }
226 }
227 }
227 }
228 }
228 }
229 }
229 }
230 }
230 }
231 }
231 end
232 end
232 end
233 end
233 end
234 end
234
235
235 context "with custom fields" do
236 context "with custom fields" do
236 context ".xml" do
237 context ".xml" do
237 should "display custom fields" do
238 should "display custom fields" do
238 get '/issues/3.xml'
239 get '/issues/3.xml'
239
240
240 assert_tag :tag => 'issue',
241 assert_tag :tag => 'issue',
241 :child => {
242 :child => {
242 :tag => 'custom_fields',
243 :tag => 'custom_fields',
243 :attributes => { :type => 'array' },
244 :attributes => { :type => 'array' },
244 :child => {
245 :child => {
245 :tag => 'custom_field',
246 :tag => 'custom_field',
246 :attributes => { :id => '1'},
247 :attributes => { :id => '1'},
247 :child => {
248 :child => {
248 :tag => 'value',
249 :tag => 'value',
249 :content => 'MySQL'
250 :content => 'MySQL'
250 }
251 }
251 }
252 }
252 }
253 }
253
254
254 assert_nothing_raised do
255 assert_nothing_raised do
255 Hash.from_xml(response.body).to_xml
256 Hash.from_xml(response.body).to_xml
256 end
257 end
257 end
258 end
258 end
259 end
259 end
260 end
260
261
261 context "with multi custom fields" do
262 context "with multi custom fields" do
262 setup do
263 setup do
263 field = CustomField.find(1)
264 field = CustomField.find(1)
264 field.update_attribute :multiple, true
265 field.update_attribute :multiple, true
265 issue = Issue.find(3)
266 issue = Issue.find(3)
266 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
267 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
267 issue.save!
268 issue.save!
268 end
269 end
269
270
270 context ".xml" do
271 context ".xml" do
271 should "display custom fields" do
272 should "display custom fields" do
272 get '/issues/3.xml'
273 get '/issues/3.xml'
273 assert_response :success
274 assert_response :success
274 assert_tag :tag => 'issue',
275 assert_tag :tag => 'issue',
275 :child => {
276 :child => {
276 :tag => 'custom_fields',
277 :tag => 'custom_fields',
277 :attributes => { :type => 'array' },
278 :attributes => { :type => 'array' },
278 :child => {
279 :child => {
279 :tag => 'custom_field',
280 :tag => 'custom_field',
280 :attributes => { :id => '1'},
281 :attributes => { :id => '1'},
281 :child => {
282 :child => {
282 :tag => 'value',
283 :tag => 'value',
283 :attributes => { :type => 'array' },
284 :attributes => { :type => 'array' },
284 :children => { :count => 2 }
285 :children => { :count => 2 }
285 }
286 }
286 }
287 }
287 }
288 }
288
289
289 xml = Hash.from_xml(response.body)
290 xml = Hash.from_xml(response.body)
290 custom_fields = xml['issue']['custom_fields']
291 custom_fields = xml['issue']['custom_fields']
291 assert_kind_of Array, custom_fields
292 assert_kind_of Array, custom_fields
292 field = custom_fields.detect {|f| f['id'] == '1'}
293 field = custom_fields.detect {|f| f['id'] == '1'}
293 assert_kind_of Hash, field
294 assert_kind_of Hash, field
294 assert_equal ['MySQL', 'Oracle'], field['value'].sort
295 assert_equal ['MySQL', 'Oracle'], field['value'].sort
295 end
296 end
296 end
297 end
297
298
298 context ".json" do
299 context ".json" do
299 should "display custom fields" do
300 should "display custom fields" do
300 get '/issues/3.json'
301 get '/issues/3.json'
301 assert_response :success
302 assert_response :success
302 json = ActiveSupport::JSON.decode(response.body)
303 json = ActiveSupport::JSON.decode(response.body)
303 custom_fields = json['issue']['custom_fields']
304 custom_fields = json['issue']['custom_fields']
304 assert_kind_of Array, custom_fields
305 assert_kind_of Array, custom_fields
305 field = custom_fields.detect {|f| f['id'] == 1}
306 field = custom_fields.detect {|f| f['id'] == 1}
306 assert_kind_of Hash, field
307 assert_kind_of Hash, field
307 assert_equal ['MySQL', 'Oracle'], field['value'].sort
308 assert_equal ['MySQL', 'Oracle'], field['value'].sort
308 end
309 end
309 end
310 end
310 end
311 end
311
312
312 context "with empty value for multi custom field" do
313 context "with empty value for multi custom field" do
313 setup do
314 setup do
314 field = CustomField.find(1)
315 field = CustomField.find(1)
315 field.update_attribute :multiple, true
316 field.update_attribute :multiple, true
316 issue = Issue.find(3)
317 issue = Issue.find(3)
317 issue.custom_field_values = {1 => ['']}
318 issue.custom_field_values = {1 => ['']}
318 issue.save!
319 issue.save!
319 end
320 end
320
321
321 context ".xml" do
322 context ".xml" do
322 should "display custom fields" do
323 should "display custom fields" do
323 get '/issues/3.xml'
324 get '/issues/3.xml'
324 assert_response :success
325 assert_response :success
325 assert_tag :tag => 'issue',
326 assert_tag :tag => 'issue',
326 :child => {
327 :child => {
327 :tag => 'custom_fields',
328 :tag => 'custom_fields',
328 :attributes => { :type => 'array' },
329 :attributes => { :type => 'array' },
329 :child => {
330 :child => {
330 :tag => 'custom_field',
331 :tag => 'custom_field',
331 :attributes => { :id => '1'},
332 :attributes => { :id => '1'},
332 :child => {
333 :child => {
333 :tag => 'value',
334 :tag => 'value',
334 :attributes => { :type => 'array' },
335 :attributes => { :type => 'array' },
335 :children => { :count => 0 }
336 :children => { :count => 0 }
336 }
337 }
337 }
338 }
338 }
339 }
339
340
340 xml = Hash.from_xml(response.body)
341 xml = Hash.from_xml(response.body)
341 custom_fields = xml['issue']['custom_fields']
342 custom_fields = xml['issue']['custom_fields']
342 assert_kind_of Array, custom_fields
343 assert_kind_of Array, custom_fields
343 field = custom_fields.detect {|f| f['id'] == '1'}
344 field = custom_fields.detect {|f| f['id'] == '1'}
344 assert_kind_of Hash, field
345 assert_kind_of Hash, field
345 assert_equal [], field['value']
346 assert_equal [], field['value']
346 end
347 end
347 end
348 end
348
349
349 context ".json" do
350 context ".json" do
350 should "display custom fields" do
351 should "display custom fields" do
351 get '/issues/3.json'
352 get '/issues/3.json'
352 assert_response :success
353 assert_response :success
353 json = ActiveSupport::JSON.decode(response.body)
354 json = ActiveSupport::JSON.decode(response.body)
354 custom_fields = json['issue']['custom_fields']
355 custom_fields = json['issue']['custom_fields']
355 assert_kind_of Array, custom_fields
356 assert_kind_of Array, custom_fields
356 field = custom_fields.detect {|f| f['id'] == 1}
357 field = custom_fields.detect {|f| f['id'] == 1}
357 assert_kind_of Hash, field
358 assert_kind_of Hash, field
358 assert_equal [], field['value'].sort
359 assert_equal [], field['value'].sort
359 end
360 end
360 end
361 end
361 end
362 end
362
363
363 context "with attachments" do
364 context "with attachments" do
364 context ".xml" do
365 context ".xml" do
365 should "display attachments" do
366 should "display attachments" do
366 get '/issues/3.xml?include=attachments'
367 get '/issues/3.xml?include=attachments'
367
368
368 assert_tag :tag => 'issue',
369 assert_tag :tag => 'issue',
369 :child => {
370 :child => {
370 :tag => 'attachments',
371 :tag => 'attachments',
371 :children => {:count => 5},
372 :children => {:count => 5},
372 :child => {
373 :child => {
373 :tag => 'attachment',
374 :tag => 'attachment',
374 :child => {
375 :child => {
375 :tag => 'filename',
376 :tag => 'filename',
376 :content => 'source.rb',
377 :content => 'source.rb',
377 :sibling => {
378 :sibling => {
378 :tag => 'content_url',
379 :tag => 'content_url',
379 :content => 'http://www.example.com/attachments/download/4/source.rb'
380 :content => 'http://www.example.com/attachments/download/4/source.rb'
380 }
381 }
381 }
382 }
382 }
383 }
383 }
384 }
384 end
385 end
385 end
386 end
386 end
387 end
387
388
388 context "with subtasks" do
389 context "with subtasks" do
389 setup do
390 setup do
390 @c1 = Issue.create!(:status_id => 1, :subject => "child c1", :tracker_id => 1, :project_id => 1, :author_id => 1, :parent_issue_id => 1)
391 @c1 = Issue.create!(:status_id => 1, :subject => "child c1", :tracker_id => 1, :project_id => 1, :author_id => 1, :parent_issue_id => 1)
391 @c2 = Issue.create!(:status_id => 1, :subject => "child c2", :tracker_id => 1, :project_id => 1, :author_id => 1, :parent_issue_id => 1)
392 @c2 = Issue.create!(:status_id => 1, :subject => "child c2", :tracker_id => 1, :project_id => 1, :author_id => 1, :parent_issue_id => 1)
392 @c3 = Issue.create!(:status_id => 1, :subject => "child c3", :tracker_id => 1, :project_id => 1, :author_id => 1, :parent_issue_id => @c1.id)
393 @c3 = Issue.create!(:status_id => 1, :subject => "child c3", :tracker_id => 1, :project_id => 1, :author_id => 1, :parent_issue_id => @c1.id)
393 end
394 end
394
395
395 context ".xml" do
396 context ".xml" do
396 should "display children" do
397 should "display children" do
397 get '/issues/1.xml?include=children'
398 get '/issues/1.xml?include=children'
398
399
399 assert_tag :tag => 'issue',
400 assert_tag :tag => 'issue',
400 :child => {
401 :child => {
401 :tag => 'children',
402 :tag => 'children',
402 :children => {:count => 2},
403 :children => {:count => 2},
403 :child => {
404 :child => {
404 :tag => 'issue',
405 :tag => 'issue',
405 :attributes => {:id => @c1.id.to_s},
406 :attributes => {:id => @c1.id.to_s},
406 :child => {
407 :child => {
407 :tag => 'subject',
408 :tag => 'subject',
408 :content => 'child c1',
409 :content => 'child c1',
409 :sibling => {
410 :sibling => {
410 :tag => 'children',
411 :tag => 'children',
411 :children => {:count => 1},
412 :children => {:count => 1},
412 :child => {
413 :child => {
413 :tag => 'issue',
414 :tag => 'issue',
414 :attributes => {:id => @c3.id.to_s}
415 :attributes => {:id => @c3.id.to_s}
415 }
416 }
416 }
417 }
417 }
418 }
418 }
419 }
419 }
420 }
420 end
421 end
421
422
422 context ".json" do
423 context ".json" do
423 should "display children" do
424 should "display children" do
424 get '/issues/1.json?include=children'
425 get '/issues/1.json?include=children'
425
426
426 json = ActiveSupport::JSON.decode(response.body)
427 json = ActiveSupport::JSON.decode(response.body)
427 assert_equal([
428 assert_equal([
428 {
429 {
429 'id' => @c1.id, 'subject' => 'child c1', 'tracker' => {'id' => 1, 'name' => 'Bug'},
430 'id' => @c1.id, 'subject' => 'child c1', 'tracker' => {'id' => 1, 'name' => 'Bug'},
430 'children' => [{ 'id' => @c3.id, 'subject' => 'child c3', 'tracker' => {'id' => 1, 'name' => 'Bug'} }]
431 'children' => [{ 'id' => @c3.id, 'subject' => 'child c3', 'tracker' => {'id' => 1, 'name' => 'Bug'} }]
431 },
432 },
432 { 'id' => @c2.id, 'subject' => 'child c2', 'tracker' => {'id' => 1, 'name' => 'Bug'} }
433 { 'id' => @c2.id, 'subject' => 'child c2', 'tracker' => {'id' => 1, 'name' => 'Bug'} }
433 ],
434 ],
434 json['issue']['children'])
435 json['issue']['children'])
435 end
436 end
436 end
437 end
437 end
438 end
438 end
439 end
439 end
440 end
440
441
441 context "POST /issues.xml" do
442 context "POST /issues.xml" do
442 should_allow_api_authentication(:post,
443 should_allow_api_authentication(:post,
443 '/issues.xml',
444 '/issues.xml',
444 {:issue => {:project_id => 1, :subject => 'API test', :tracker_id => 2, :status_id => 3}},
445 {:issue => {:project_id => 1, :subject => 'API test', :tracker_id => 2, :status_id => 3}},
445 {:success_code => :created})
446 {:success_code => :created})
446
447
447 should "create an issue with the attributes" do
448 should "create an issue with the attributes" do
448 assert_difference('Issue.count') do
449 assert_difference('Issue.count') do
449 post '/issues.xml', {:issue => {:project_id => 1, :subject => 'API test', :tracker_id => 2, :status_id => 3}}, credentials('jsmith')
450 post '/issues.xml', {:issue => {:project_id => 1, :subject => 'API test', :tracker_id => 2, :status_id => 3}}, credentials('jsmith')
450 end
451 end
451
452
452 issue = Issue.first(:order => 'id DESC')
453 issue = Issue.first(:order => 'id DESC')
453 assert_equal 1, issue.project_id
454 assert_equal 1, issue.project_id
454 assert_equal 2, issue.tracker_id
455 assert_equal 2, issue.tracker_id
455 assert_equal 3, issue.status_id
456 assert_equal 3, issue.status_id
456 assert_equal 'API test', issue.subject
457 assert_equal 'API test', issue.subject
457
458
458 assert_response :created
459 assert_response :created
459 assert_equal 'application/xml', @response.content_type
460 assert_equal 'application/xml', @response.content_type
460 assert_tag 'issue', :child => {:tag => 'id', :content => issue.id.to_s}
461 assert_tag 'issue', :child => {:tag => 'id', :content => issue.id.to_s}
461 end
462 end
462 end
463 end
463
464
464 context "POST /issues.xml with failure" do
465 context "POST /issues.xml with failure" do
465 should "have an errors tag" do
466 should "have an errors tag" do
466 assert_no_difference('Issue.count') do
467 assert_no_difference('Issue.count') do
467 post '/issues.xml', {:issue => {:project_id => 1}}, credentials('jsmith')
468 post '/issues.xml', {:issue => {:project_id => 1}}, credentials('jsmith')
468 end
469 end
469
470
470 assert_tag :errors, :child => {:tag => 'error', :content => "Subject can't be blank"}
471 assert_tag :errors, :child => {:tag => 'error', :content => "Subject can't be blank"}
471 end
472 end
472 end
473 end
473
474
474 context "POST /issues.json" do
475 context "POST /issues.json" do
475 should_allow_api_authentication(:post,
476 should_allow_api_authentication(:post,
476 '/issues.json',
477 '/issues.json',
477 {:issue => {:project_id => 1, :subject => 'API test', :tracker_id => 2, :status_id => 3}},
478 {:issue => {:project_id => 1, :subject => 'API test', :tracker_id => 2, :status_id => 3}},
478 {:success_code => :created})
479 {:success_code => :created})
479
480
480 should "create an issue with the attributes" do
481 should "create an issue with the attributes" do
481 assert_difference('Issue.count') do
482 assert_difference('Issue.count') do
482 post '/issues.json', {:issue => {:project_id => 1, :subject => 'API test', :tracker_id => 2, :status_id => 3}}, credentials('jsmith')
483 post '/issues.json', {:issue => {:project_id => 1, :subject => 'API test', :tracker_id => 2, :status_id => 3}}, credentials('jsmith')
483 end
484 end
484
485
485 issue = Issue.first(:order => 'id DESC')
486 issue = Issue.first(:order => 'id DESC')
486 assert_equal 1, issue.project_id
487 assert_equal 1, issue.project_id
487 assert_equal 2, issue.tracker_id
488 assert_equal 2, issue.tracker_id
488 assert_equal 3, issue.status_id
489 assert_equal 3, issue.status_id
489 assert_equal 'API test', issue.subject
490 assert_equal 'API test', issue.subject
490 end
491 end
491
492
492 end
493 end
493
494
494 context "POST /issues.json with failure" do
495 context "POST /issues.json with failure" do
495 should "have an errors element" do
496 should "have an errors element" do
496 assert_no_difference('Issue.count') do
497 assert_no_difference('Issue.count') do
497 post '/issues.json', {:issue => {:project_id => 1}}, credentials('jsmith')
498 post '/issues.json', {:issue => {:project_id => 1}}, credentials('jsmith')
498 end
499 end
499
500
500 json = ActiveSupport::JSON.decode(response.body)
501 json = ActiveSupport::JSON.decode(response.body)
501 assert json['errors'].include?("Subject can't be blank")
502 assert json['errors'].include?("Subject can't be blank")
502 end
503 end
503 end
504 end
504
505
505 # Issue 6 is on a private project
506 # Issue 6 is on a private project
506 context "PUT /issues/6.xml" do
507 context "PUT /issues/6.xml" do
507 setup do
508 setup do
508 @parameters = {:issue => {:subject => 'API update', :notes => 'A new note'}}
509 @parameters = {:issue => {:subject => 'API update', :notes => 'A new note'}}
509 end
510 end
510
511
511 should_allow_api_authentication(:put,
512 should_allow_api_authentication(:put,
512 '/issues/6.xml',
513 '/issues/6.xml',
513 {:issue => {:subject => 'API update', :notes => 'A new note'}},
514 {:issue => {:subject => 'API update', :notes => 'A new note'}},
514 {:success_code => :ok})
515 {:success_code => :ok})
515
516
516 should "not create a new issue" do
517 should "not create a new issue" do
517 assert_no_difference('Issue.count') do
518 assert_no_difference('Issue.count') do
518 put '/issues/6.xml', @parameters, credentials('jsmith')
519 put '/issues/6.xml', @parameters, credentials('jsmith')
519 end
520 end
520 end
521 end
521
522
522 should "create a new journal" do
523 should "create a new journal" do
523 assert_difference('Journal.count') do
524 assert_difference('Journal.count') do
524 put '/issues/6.xml', @parameters, credentials('jsmith')
525 put '/issues/6.xml', @parameters, credentials('jsmith')
525 end
526 end
526 end
527 end
527
528
528 should "add the note to the journal" do
529 should "add the note to the journal" do
529 put '/issues/6.xml', @parameters, credentials('jsmith')
530 put '/issues/6.xml', @parameters, credentials('jsmith')
530
531
531 journal = Journal.last
532 journal = Journal.last
532 assert_equal "A new note", journal.notes
533 assert_equal "A new note", journal.notes
533 end
534 end
534
535
535 should "update the issue" do
536 should "update the issue" do
536 put '/issues/6.xml', @parameters, credentials('jsmith')
537 put '/issues/6.xml', @parameters, credentials('jsmith')
537
538
538 issue = Issue.find(6)
539 issue = Issue.find(6)
539 assert_equal "API update", issue.subject
540 assert_equal "API update", issue.subject
540 end
541 end
541
542
542 end
543 end
543
544
544 context "PUT /issues/3.xml with custom fields" do
545 context "PUT /issues/3.xml with custom fields" do
545 setup do
546 setup do
546 @parameters = {:issue => {:custom_fields => [{'id' => '1', 'value' => 'PostgreSQL' }, {'id' => '2', 'value' => '150'}]}}
547 @parameters = {:issue => {:custom_fields => [{'id' => '1', 'value' => 'PostgreSQL' }, {'id' => '2', 'value' => '150'}]}}
547 end
548 end
548
549
549 should "update custom fields" do
550 should "update custom fields" do
550 assert_no_difference('Issue.count') do
551 assert_no_difference('Issue.count') do
551 put '/issues/3.xml', @parameters, credentials('jsmith')
552 put '/issues/3.xml', @parameters, credentials('jsmith')
552 end
553 end
553
554
554 issue = Issue.find(3)
555 issue = Issue.find(3)
555 assert_equal '150', issue.custom_value_for(2).value
556 assert_equal '150', issue.custom_value_for(2).value
556 assert_equal 'PostgreSQL', issue.custom_value_for(1).value
557 assert_equal 'PostgreSQL', issue.custom_value_for(1).value
557 end
558 end
558 end
559 end
559
560
560 context "PUT /issues/3.xml with multi custom fields" do
561 context "PUT /issues/3.xml with multi custom fields" do
561 setup do
562 setup do
562 field = CustomField.find(1)
563 field = CustomField.find(1)
563 field.update_attribute :multiple, true
564 field.update_attribute :multiple, true
564 @parameters = {:issue => {:custom_fields => [{'id' => '1', 'value' => ['MySQL', 'PostgreSQL'] }, {'id' => '2', 'value' => '150'}]}}
565 @parameters = {:issue => {:custom_fields => [{'id' => '1', 'value' => ['MySQL', 'PostgreSQL'] }, {'id' => '2', 'value' => '150'}]}}
565 end
566 end
566
567
567 should "update custom fields" do
568 should "update custom fields" do
568 assert_no_difference('Issue.count') do
569 assert_no_difference('Issue.count') do
569 put '/issues/3.xml', @parameters, credentials('jsmith')
570 put '/issues/3.xml', @parameters, credentials('jsmith')
570 end
571 end
571
572
572 issue = Issue.find(3)
573 issue = Issue.find(3)
573 assert_equal '150', issue.custom_value_for(2).value
574 assert_equal '150', issue.custom_value_for(2).value
574 assert_equal ['MySQL', 'PostgreSQL'], issue.custom_field_value(1).sort
575 assert_equal ['MySQL', 'PostgreSQL'], issue.custom_field_value(1).sort
575 end
576 end
576 end
577 end
577
578
578 context "PUT /issues/3.xml with project change" do
579 context "PUT /issues/3.xml with project change" do
579 setup do
580 setup do
580 @parameters = {:issue => {:project_id => 2, :subject => 'Project changed'}}
581 @parameters = {:issue => {:project_id => 2, :subject => 'Project changed'}}
581 end
582 end
582
583
583 should "update project" do
584 should "update project" do
584 assert_no_difference('Issue.count') do
585 assert_no_difference('Issue.count') do
585 put '/issues/3.xml', @parameters, credentials('jsmith')
586 put '/issues/3.xml', @parameters, credentials('jsmith')
586 end
587 end
587
588
588 issue = Issue.find(3)
589 issue = Issue.find(3)
589 assert_equal 2, issue.project_id
590 assert_equal 2, issue.project_id
590 assert_equal 'Project changed', issue.subject
591 assert_equal 'Project changed', issue.subject
591 end
592 end
592 end
593 end
593
594
594 context "PUT /issues/6.xml with failed update" do
595 context "PUT /issues/6.xml with failed update" do
595 setup do
596 setup do
596 @parameters = {:issue => {:subject => ''}}
597 @parameters = {:issue => {:subject => ''}}
597 end
598 end
598
599
599 should "not create a new issue" do
600 should "not create a new issue" do
600 assert_no_difference('Issue.count') do
601 assert_no_difference('Issue.count') do
601 put '/issues/6.xml', @parameters, credentials('jsmith')
602 put '/issues/6.xml', @parameters, credentials('jsmith')
602 end
603 end
603 end
604 end
604
605
605 should "not create a new journal" do
606 should "not create a new journal" do
606 assert_no_difference('Journal.count') do
607 assert_no_difference('Journal.count') do
607 put '/issues/6.xml', @parameters, credentials('jsmith')
608 put '/issues/6.xml', @parameters, credentials('jsmith')
608 end
609 end
609 end
610 end
610
611
611 should "have an errors tag" do
612 should "have an errors tag" do
612 put '/issues/6.xml', @parameters, credentials('jsmith')
613 put '/issues/6.xml', @parameters, credentials('jsmith')
613
614
614 assert_tag :errors, :child => {:tag => 'error', :content => "Subject can't be blank"}
615 assert_tag :errors, :child => {:tag => 'error', :content => "Subject can't be blank"}
615 end
616 end
616 end
617 end
617
618
618 context "PUT /issues/6.json" do
619 context "PUT /issues/6.json" do
619 setup do
620 setup do
620 @parameters = {:issue => {:subject => 'API update', :notes => 'A new note'}}
621 @parameters = {:issue => {:subject => 'API update', :notes => 'A new note'}}
621 end
622 end
622
623
623 should_allow_api_authentication(:put,
624 should_allow_api_authentication(:put,
624 '/issues/6.json',
625 '/issues/6.json',
625 {:issue => {:subject => 'API update', :notes => 'A new note'}},
626 {:issue => {:subject => 'API update', :notes => 'A new note'}},
626 {:success_code => :ok})
627 {:success_code => :ok})
627
628
628 should "update the issue" do
629 should "update the issue" do
629 assert_no_difference('Issue.count') do
630 assert_no_difference('Issue.count') do
630 assert_difference('Journal.count') do
631 assert_difference('Journal.count') do
631 put '/issues/6.json', @parameters, credentials('jsmith')
632 put '/issues/6.json', @parameters, credentials('jsmith')
632
633
633 assert_response :ok
634 assert_response :ok
634 assert_equal '', response.body
635 assert_equal '', response.body
635 end
636 end
636 end
637 end
637
638
638 issue = Issue.find(6)
639 issue = Issue.find(6)
639 assert_equal "API update", issue.subject
640 assert_equal "API update", issue.subject
640 journal = Journal.last
641 journal = Journal.last
641 assert_equal "A new note", journal.notes
642 assert_equal "A new note", journal.notes
642 end
643 end
643 end
644 end
644
645
645 context "PUT /issues/6.json with failed update" do
646 context "PUT /issues/6.json with failed update" do
646 should "return errors" do
647 should "return errors" do
647 assert_no_difference('Issue.count') do
648 assert_no_difference('Issue.count') do
648 assert_no_difference('Journal.count') do
649 assert_no_difference('Journal.count') do
649 put '/issues/6.json', {:issue => {:subject => ''}}, credentials('jsmith')
650 put '/issues/6.json', {:issue => {:subject => ''}}, credentials('jsmith')
650
651
651 assert_response :unprocessable_entity
652 assert_response :unprocessable_entity
652 end
653 end
653 end
654 end
654
655
655 json = ActiveSupport::JSON.decode(response.body)
656 json = ActiveSupport::JSON.decode(response.body)
656 assert json['errors'].include?("Subject can't be blank")
657 assert json['errors'].include?("Subject can't be blank")
657 end
658 end
658 end
659 end
659
660
660 context "DELETE /issues/1.xml" do
661 context "DELETE /issues/1.xml" do
661 should_allow_api_authentication(:delete,
662 should_allow_api_authentication(:delete,
662 '/issues/6.xml',
663 '/issues/6.xml',
663 {},
664 {},
664 {:success_code => :ok})
665 {:success_code => :ok})
665
666
666 should "delete the issue" do
667 should "delete the issue" do
667 assert_difference('Issue.count', -1) do
668 assert_difference('Issue.count', -1) do
668 delete '/issues/6.xml', {}, credentials('jsmith')
669 delete '/issues/6.xml', {}, credentials('jsmith')
669
670
670 assert_response :ok
671 assert_response :ok
671 assert_equal '', response.body
672 assert_equal '', response.body
672 end
673 end
673
674
674 assert_nil Issue.find_by_id(6)
675 assert_nil Issue.find_by_id(6)
675 end
676 end
676 end
677 end
677
678
678 context "DELETE /issues/1.json" do
679 context "DELETE /issues/1.json" do
679 should_allow_api_authentication(:delete,
680 should_allow_api_authentication(:delete,
680 '/issues/6.json',
681 '/issues/6.json',
681 {},
682 {},
682 {:success_code => :ok})
683 {:success_code => :ok})
683
684
684 should "delete the issue" do
685 should "delete the issue" do
685 assert_difference('Issue.count', -1) do
686 assert_difference('Issue.count', -1) do
686 delete '/issues/6.json', {}, credentials('jsmith')
687 delete '/issues/6.json', {}, credentials('jsmith')
687
688
688 assert_response :ok
689 assert_response :ok
689 assert_equal '', response.body
690 assert_equal '', response.body
690 end
691 end
691
692
692 assert_nil Issue.find_by_id(6)
693 assert_nil Issue.find_by_id(6)
693 end
694 end
694 end
695 end
695
696
696 def test_create_issue_with_uploaded_file
697 def test_create_issue_with_uploaded_file
697 set_tmp_attachments_directory
698 set_tmp_attachments_directory
698
699
699 # upload the file
700 # upload the file
700 assert_difference 'Attachment.count' do
701 assert_difference 'Attachment.count' do
701 post '/uploads.xml', 'test_create_with_upload', {"CONTENT_TYPE" => 'application/octet-stream'}.merge(credentials('jsmith'))
702 post '/uploads.xml', 'test_create_with_upload', {"CONTENT_TYPE" => 'application/octet-stream'}.merge(credentials('jsmith'))
702 assert_response :created
703 assert_response :created
703 end
704 end
704 xml = Hash.from_xml(response.body)
705 xml = Hash.from_xml(response.body)
705 token = xml['upload']['token']
706 token = xml['upload']['token']
706 attachment = Attachment.first(:order => 'id DESC')
707 attachment = Attachment.first(:order => 'id DESC')
707
708
708 # create the issue with the upload's token
709 # create the issue with the upload's token
709 assert_difference 'Issue.count' do
710 assert_difference 'Issue.count' do
710 post '/issues.xml',
711 post '/issues.xml',
711 {:issue => {:project_id => 1, :subject => 'Uploaded file', :uploads => [{:token => token, :filename => 'test.txt', :content_type => 'text/plain'}]}},
712 {:issue => {:project_id => 1, :subject => 'Uploaded file', :uploads => [{:token => token, :filename => 'test.txt', :content_type => 'text/plain'}]}},
712 credentials('jsmith')
713 credentials('jsmith')
713 assert_response :created
714 assert_response :created
714 end
715 end
715 issue = Issue.first(:order => 'id DESC')
716 issue = Issue.first(:order => 'id DESC')
716 assert_equal 1, issue.attachments.count
717 assert_equal 1, issue.attachments.count
717 assert_equal attachment, issue.attachments.first
718 assert_equal attachment, issue.attachments.first
718
719
719 attachment.reload
720 attachment.reload
720 assert_equal 'test.txt', attachment.filename
721 assert_equal 'test.txt', attachment.filename
721 assert_equal 'text/plain', attachment.content_type
722 assert_equal 'text/plain', attachment.content_type
722 assert_equal 'test_create_with_upload'.size, attachment.filesize
723 assert_equal 'test_create_with_upload'.size, attachment.filesize
723 assert_equal 2, attachment.author_id
724 assert_equal 2, attachment.author_id
724
725
725 # get the issue with its attachments
726 # get the issue with its attachments
726 get "/issues/#{issue.id}.xml", :include => 'attachments'
727 get "/issues/#{issue.id}.xml", :include => 'attachments'
727 assert_response :success
728 assert_response :success
728 xml = Hash.from_xml(response.body)
729 xml = Hash.from_xml(response.body)
729 attachments = xml['issue']['attachments']
730 attachments = xml['issue']['attachments']
730 assert_kind_of Array, attachments
731 assert_kind_of Array, attachments
731 assert_equal 1, attachments.size
732 assert_equal 1, attachments.size
732 url = attachments.first['content_url']
733 url = attachments.first['content_url']
733 assert_not_nil url
734 assert_not_nil url
734
735
735 # download the attachment
736 # download the attachment
736 get url
737 get url
737 assert_response :success
738 assert_response :success
738 end
739 end
739
740
740 def test_update_issue_with_uploaded_file
741 def test_update_issue_with_uploaded_file
741 set_tmp_attachments_directory
742 set_tmp_attachments_directory
742
743
743 # upload the file
744 # upload the file
744 assert_difference 'Attachment.count' do
745 assert_difference 'Attachment.count' do
745 post '/uploads.xml', 'test_upload_with_upload', {"CONTENT_TYPE" => 'application/octet-stream'}.merge(credentials('jsmith'))
746 post '/uploads.xml', 'test_upload_with_upload', {"CONTENT_TYPE" => 'application/octet-stream'}.merge(credentials('jsmith'))
746 assert_response :created
747 assert_response :created
747 end
748 end
748 xml = Hash.from_xml(response.body)
749 xml = Hash.from_xml(response.body)
749 token = xml['upload']['token']
750 token = xml['upload']['token']
750 attachment = Attachment.first(:order => 'id DESC')
751 attachment = Attachment.first(:order => 'id DESC')
751
752
752 # update the issue with the upload's token
753 # update the issue with the upload's token
753 assert_difference 'Journal.count' do
754 assert_difference 'Journal.count' do
754 put '/issues/1.xml',
755 put '/issues/1.xml',
755 {:issue => {:notes => 'Attachment added', :uploads => [{:token => token, :filename => 'test.txt', :content_type => 'text/plain'}]}},
756 {:issue => {:notes => 'Attachment added', :uploads => [{:token => token, :filename => 'test.txt', :content_type => 'text/plain'}]}},
756 credentials('jsmith')
757 credentials('jsmith')
757 assert_response :ok
758 assert_response :ok
758 assert_equal '', @response.body
759 assert_equal '', @response.body
759 end
760 end
760
761
761 issue = Issue.find(1)
762 issue = Issue.find(1)
762 assert_include attachment, issue.attachments
763 assert_include attachment, issue.attachments
763 end
764 end
764 end
765 end
General Comments 0
You need to be logged in to leave comments. Login now