##// END OF EJS Templates
Merged r10571 from trunk...
Toshi MARUYAMA -
r10367:2c3c1d2a0286
parent child
Show More
@@ -1,778 +1,779
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class 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 "not create a new issue" do
629 should "not create a new issue" do
629 assert_no_difference('Issue.count') do
630 assert_no_difference('Issue.count') do
630 put '/issues/6.json', @parameters, credentials('jsmith')
631 put '/issues/6.json', @parameters, credentials('jsmith')
631 end
632 end
632 end
633 end
633
634
634 should "create a new journal" do
635 should "create a new journal" do
635 assert_difference('Journal.count') do
636 assert_difference('Journal.count') do
636 put '/issues/6.json', @parameters, credentials('jsmith')
637 put '/issues/6.json', @parameters, credentials('jsmith')
637 end
638 end
638 end
639 end
639
640
640 should "add the note to the journal" do
641 should "add the note to the journal" do
641 put '/issues/6.json', @parameters, credentials('jsmith')
642 put '/issues/6.json', @parameters, credentials('jsmith')
642
643
643 journal = Journal.last
644 journal = Journal.last
644 assert_equal "A new note", journal.notes
645 assert_equal "A new note", journal.notes
645 end
646 end
646
647
647 should "update the issue" do
648 should "update the issue" do
648 put '/issues/6.json', @parameters, credentials('jsmith')
649 put '/issues/6.json', @parameters, credentials('jsmith')
649
650
650 issue = Issue.find(6)
651 issue = Issue.find(6)
651 assert_equal "API update", issue.subject
652 assert_equal "API update", issue.subject
652 end
653 end
653
654
654 end
655 end
655
656
656 context "PUT /issues/6.json with failed update" do
657 context "PUT /issues/6.json with failed update" do
657 setup do
658 setup do
658 @parameters = {:issue => {:subject => ''}}
659 @parameters = {:issue => {:subject => ''}}
659 end
660 end
660
661
661 should "not create a new issue" do
662 should "not create a new issue" do
662 assert_no_difference('Issue.count') do
663 assert_no_difference('Issue.count') do
663 put '/issues/6.json', @parameters, credentials('jsmith')
664 put '/issues/6.json', @parameters, credentials('jsmith')
664 end
665 end
665 end
666 end
666
667
667 should "not create a new journal" do
668 should "not create a new journal" do
668 assert_no_difference('Journal.count') do
669 assert_no_difference('Journal.count') do
669 put '/issues/6.json', @parameters, credentials('jsmith')
670 put '/issues/6.json', @parameters, credentials('jsmith')
670 end
671 end
671 end
672 end
672
673
673 should "have an errors attribute" do
674 should "have an errors attribute" do
674 put '/issues/6.json', @parameters, credentials('jsmith')
675 put '/issues/6.json', @parameters, credentials('jsmith')
675
676
676 json = ActiveSupport::JSON.decode(response.body)
677 json = ActiveSupport::JSON.decode(response.body)
677 assert json['errors'].include?("Subject can't be blank")
678 assert json['errors'].include?("Subject can't be blank")
678 end
679 end
679 end
680 end
680
681
681 context "DELETE /issues/1.xml" do
682 context "DELETE /issues/1.xml" do
682 should_allow_api_authentication(:delete,
683 should_allow_api_authentication(:delete,
683 '/issues/6.xml',
684 '/issues/6.xml',
684 {},
685 {},
685 {:success_code => :ok})
686 {:success_code => :ok})
686
687
687 should "delete the issue" do
688 should "delete the issue" do
688 assert_difference('Issue.count',-1) do
689 assert_difference('Issue.count',-1) do
689 delete '/issues/6.xml', {}, credentials('jsmith')
690 delete '/issues/6.xml', {}, credentials('jsmith')
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 context "DELETE /issues/1.json" do
697 context "DELETE /issues/1.json" do
697 should_allow_api_authentication(:delete,
698 should_allow_api_authentication(:delete,
698 '/issues/6.json',
699 '/issues/6.json',
699 {},
700 {},
700 {:success_code => :ok})
701 {:success_code => :ok})
701
702
702 should "delete the issue" do
703 should "delete the issue" do
703 assert_difference('Issue.count',-1) do
704 assert_difference('Issue.count',-1) do
704 delete '/issues/6.json', {}, credentials('jsmith')
705 delete '/issues/6.json', {}, credentials('jsmith')
705 end
706 end
706
707
707 assert_nil Issue.find_by_id(6)
708 assert_nil Issue.find_by_id(6)
708 end
709 end
709 end
710 end
710
711
711 def test_create_issue_with_uploaded_file
712 def test_create_issue_with_uploaded_file
712 set_tmp_attachments_directory
713 set_tmp_attachments_directory
713
714
714 # upload the file
715 # upload the file
715 assert_difference 'Attachment.count' do
716 assert_difference 'Attachment.count' do
716 post '/uploads.xml', 'test_create_with_upload', {"CONTENT_TYPE" => 'application/octet-stream'}.merge(credentials('jsmith'))
717 post '/uploads.xml', 'test_create_with_upload', {"CONTENT_TYPE" => 'application/octet-stream'}.merge(credentials('jsmith'))
717 assert_response :created
718 assert_response :created
718 end
719 end
719 xml = Hash.from_xml(response.body)
720 xml = Hash.from_xml(response.body)
720 token = xml['upload']['token']
721 token = xml['upload']['token']
721 attachment = Attachment.first(:order => 'id DESC')
722 attachment = Attachment.first(:order => 'id DESC')
722
723
723 # create the issue with the upload's token
724 # create the issue with the upload's token
724 assert_difference 'Issue.count' do
725 assert_difference 'Issue.count' do
725 post '/issues.xml',
726 post '/issues.xml',
726 {:issue => {:project_id => 1, :subject => 'Uploaded file', :uploads => [{:token => token, :filename => 'test.txt', :content_type => 'text/plain'}]}},
727 {:issue => {:project_id => 1, :subject => 'Uploaded file', :uploads => [{:token => token, :filename => 'test.txt', :content_type => 'text/plain'}]}},
727 credentials('jsmith')
728 credentials('jsmith')
728 assert_response :created
729 assert_response :created
729 end
730 end
730 issue = Issue.first(:order => 'id DESC')
731 issue = Issue.first(:order => 'id DESC')
731 assert_equal 1, issue.attachments.count
732 assert_equal 1, issue.attachments.count
732 assert_equal attachment, issue.attachments.first
733 assert_equal attachment, issue.attachments.first
733
734
734 attachment.reload
735 attachment.reload
735 assert_equal 'test.txt', attachment.filename
736 assert_equal 'test.txt', attachment.filename
736 assert_equal 'text/plain', attachment.content_type
737 assert_equal 'text/plain', attachment.content_type
737 assert_equal 'test_create_with_upload'.size, attachment.filesize
738 assert_equal 'test_create_with_upload'.size, attachment.filesize
738 assert_equal 2, attachment.author_id
739 assert_equal 2, attachment.author_id
739
740
740 # get the issue with its attachments
741 # get the issue with its attachments
741 get "/issues/#{issue.id}.xml", :include => 'attachments'
742 get "/issues/#{issue.id}.xml", :include => 'attachments'
742 assert_response :success
743 assert_response :success
743 xml = Hash.from_xml(response.body)
744 xml = Hash.from_xml(response.body)
744 attachments = xml['issue']['attachments']
745 attachments = xml['issue']['attachments']
745 assert_kind_of Array, attachments
746 assert_kind_of Array, attachments
746 assert_equal 1, attachments.size
747 assert_equal 1, attachments.size
747 url = attachments.first['content_url']
748 url = attachments.first['content_url']
748 assert_not_nil url
749 assert_not_nil url
749
750
750 # download the attachment
751 # download the attachment
751 get url
752 get url
752 assert_response :success
753 assert_response :success
753 end
754 end
754
755
755 def test_update_issue_with_uploaded_file
756 def test_update_issue_with_uploaded_file
756 set_tmp_attachments_directory
757 set_tmp_attachments_directory
757
758
758 # upload the file
759 # upload the file
759 assert_difference 'Attachment.count' do
760 assert_difference 'Attachment.count' do
760 post '/uploads.xml', 'test_upload_with_upload', {"CONTENT_TYPE" => 'application/octet-stream'}.merge(credentials('jsmith'))
761 post '/uploads.xml', 'test_upload_with_upload', {"CONTENT_TYPE" => 'application/octet-stream'}.merge(credentials('jsmith'))
761 assert_response :created
762 assert_response :created
762 end
763 end
763 xml = Hash.from_xml(response.body)
764 xml = Hash.from_xml(response.body)
764 token = xml['upload']['token']
765 token = xml['upload']['token']
765 attachment = Attachment.first(:order => 'id DESC')
766 attachment = Attachment.first(:order => 'id DESC')
766
767
767 # update the issue with the upload's token
768 # update the issue with the upload's token
768 assert_difference 'Journal.count' do
769 assert_difference 'Journal.count' do
769 put '/issues/1.xml',
770 put '/issues/1.xml',
770 {:issue => {:notes => 'Attachment added', :uploads => [{:token => token, :filename => 'test.txt', :content_type => 'text/plain'}]}},
771 {:issue => {:notes => 'Attachment added', :uploads => [{:token => token, :filename => 'test.txt', :content_type => 'text/plain'}]}},
771 credentials('jsmith')
772 credentials('jsmith')
772 assert_response :ok
773 assert_response :ok
773 end
774 end
774
775
775 issue = Issue.find(1)
776 issue = Issue.find(1)
776 assert_include attachment, issue.attachments
777 assert_include attachment, issue.attachments
777 end
778 end
778 end
779 end
General Comments 0
You need to be logged in to leave comments. Login now