##// END OF EJS Templates
Merged r14794 (#21136)....
Jean-Philippe Lang -
r14461:8d8f612fa368
parent child
Show More
@@ -1,75 +1,75
1 api.issue do
1 api.issue do
2 api.id @issue.id
2 api.id @issue.id
3 api.project(:id => @issue.project_id, :name => @issue.project.name) unless @issue.project.nil?
3 api.project(:id => @issue.project_id, :name => @issue.project.name) unless @issue.project.nil?
4 api.tracker(:id => @issue.tracker_id, :name => @issue.tracker.name) unless @issue.tracker.nil?
4 api.tracker(:id => @issue.tracker_id, :name => @issue.tracker.name) unless @issue.tracker.nil?
5 api.status(:id => @issue.status_id, :name => @issue.status.name) unless @issue.status.nil?
5 api.status(:id => @issue.status_id, :name => @issue.status.name) unless @issue.status.nil?
6 api.priority(:id => @issue.priority_id, :name => @issue.priority.name) unless @issue.priority.nil?
6 api.priority(:id => @issue.priority_id, :name => @issue.priority.name) unless @issue.priority.nil?
7 api.author(:id => @issue.author_id, :name => @issue.author.name) unless @issue.author.nil?
7 api.author(:id => @issue.author_id, :name => @issue.author.name) unless @issue.author.nil?
8 api.assigned_to(:id => @issue.assigned_to_id, :name => @issue.assigned_to.name) unless @issue.assigned_to.nil?
8 api.assigned_to(:id => @issue.assigned_to_id, :name => @issue.assigned_to.name) unless @issue.assigned_to.nil?
9 api.category(:id => @issue.category_id, :name => @issue.category.name) unless @issue.category.nil?
9 api.category(:id => @issue.category_id, :name => @issue.category.name) unless @issue.category.nil?
10 api.fixed_version(:id => @issue.fixed_version_id, :name => @issue.fixed_version.name) unless @issue.fixed_version.nil?
10 api.fixed_version(:id => @issue.fixed_version_id, :name => @issue.fixed_version.name) unless @issue.fixed_version.nil?
11 api.parent(:id => @issue.parent_id) unless @issue.parent.nil?
11 api.parent(:id => @issue.parent_id) unless @issue.parent.nil?
12
12
13 api.subject @issue.subject
13 api.subject @issue.subject
14 api.description @issue.description
14 api.description @issue.description
15 api.start_date @issue.start_date
15 api.start_date @issue.start_date
16 api.due_date @issue.due_date
16 api.due_date @issue.due_date
17 api.done_ratio @issue.done_ratio
17 api.done_ratio @issue.done_ratio
18 api.is_private @issue.is_private
18 api.is_private @issue.is_private
19 api.estimated_hours @issue.estimated_hours
19 api.estimated_hours @issue.estimated_hours
20 api.spent_hours(@issue.spent_hours) if User.current.allowed_to?(:view_time_entries, @project)
20 api.spent_hours(@issue.spent_hours) if User.current.allowed_to?(:view_time_entries, @project)
21
21
22 render_api_custom_values @issue.visible_custom_field_values, api
22 render_api_custom_values @issue.visible_custom_field_values, api
23
23
24 api.created_on @issue.created_on
24 api.created_on @issue.created_on
25 api.updated_on @issue.updated_on
25 api.updated_on @issue.updated_on
26 api.closed_on @issue.closed_on
26 api.closed_on @issue.closed_on
27
27
28 render_api_issue_children(@issue, api) if include_in_api_response?('children')
28 render_api_issue_children(@issue, api) if include_in_api_response?('children')
29
29
30 api.array :attachments do
30 api.array :attachments do
31 @issue.attachments.each do |attachment|
31 @issue.attachments.each do |attachment|
32 render_api_attachment(attachment, api)
32 render_api_attachment(attachment, api)
33 end
33 end
34 end if include_in_api_response?('attachments')
34 end if include_in_api_response?('attachments')
35
35
36 api.array :relations do
36 api.array :relations do
37 @relations.each do |relation|
37 @relations.each do |relation|
38 api.relation(:id => relation.id, :issue_id => relation.issue_from_id, :issue_to_id => relation.issue_to_id, :relation_type => relation.relation_type, :delay => relation.delay)
38 api.relation(:id => relation.id, :issue_id => relation.issue_from_id, :issue_to_id => relation.issue_to_id, :relation_type => relation.relation_type, :delay => relation.delay)
39 end
39 end
40 end if include_in_api_response?('relations') && @relations.present?
40 end if include_in_api_response?('relations') && @relations.present?
41
41
42 api.array :changesets do
42 api.array :changesets do
43 @issue.changesets.each do |changeset|
43 @changesets.each do |changeset|
44 api.changeset :revision => changeset.revision do
44 api.changeset :revision => changeset.revision do
45 api.user(:id => changeset.user_id, :name => changeset.user.name) unless changeset.user.nil?
45 api.user(:id => changeset.user_id, :name => changeset.user.name) unless changeset.user.nil?
46 api.comments changeset.comments
46 api.comments changeset.comments
47 api.committed_on changeset.committed_on
47 api.committed_on changeset.committed_on
48 end
48 end
49 end
49 end
50 end if include_in_api_response?('changesets') && User.current.allowed_to?(:view_changesets, @project)
50 end if include_in_api_response?('changesets')
51
51
52 api.array :journals do
52 api.array :journals do
53 @journals.each do |journal|
53 @journals.each do |journal|
54 api.journal :id => journal.id do
54 api.journal :id => journal.id do
55 api.user(:id => journal.user_id, :name => journal.user.name) unless journal.user.nil?
55 api.user(:id => journal.user_id, :name => journal.user.name) unless journal.user.nil?
56 api.notes journal.notes
56 api.notes journal.notes
57 api.created_on journal.created_on
57 api.created_on journal.created_on
58 api.array :details do
58 api.array :details do
59 journal.visible_details.each do |detail|
59 journal.visible_details.each do |detail|
60 api.detail :property => detail.property, :name => detail.prop_key do
60 api.detail :property => detail.property, :name => detail.prop_key do
61 api.old_value detail.old_value
61 api.old_value detail.old_value
62 api.new_value detail.value
62 api.new_value detail.value
63 end
63 end
64 end
64 end
65 end
65 end
66 end
66 end
67 end
67 end
68 end if include_in_api_response?('journals')
68 end if include_in_api_response?('journals')
69
69
70 api.array :watchers do
70 api.array :watchers do
71 @issue.watcher_users.each do |user|
71 @issue.watcher_users.each do |user|
72 api.user :id => user.id, :name => user.name
72 api.user :id => user.id, :name => user.name
73 end
73 end
74 end if include_in_api_response?('watchers') && User.current.allowed_to?(:view_issue_watchers, @issue.project)
74 end if include_in_api_response?('watchers') && User.current.allowed_to?(:view_issue_watchers, @issue.project)
75 end
75 end
@@ -1,888 +1,902
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2015 Jean-Philippe Lang
2 # Copyright (C) 2006-2015 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 def setup
47 def setup
48 Setting.rest_api_enabled = '1'
48 Setting.rest_api_enabled = '1'
49 end
49 end
50
50
51 context "/issues" do
51 context "/issues" do
52 # 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
53 # only showing public issues.
53 # only showing public issues.
54 should_allow_api_authentication(:get, "/projects/private-child/issues.xml")
54 should_allow_api_authentication(:get, "/projects/private-child/issues.xml")
55
55
56 should "contain metadata" do
56 should "contain metadata" do
57 get '/issues.xml'
57 get '/issues.xml'
58
58
59 assert_tag :tag => 'issues',
59 assert_tag :tag => 'issues',
60 :attributes => {
60 :attributes => {
61 :type => 'array',
61 :type => 'array',
62 :total_count => assigns(:issue_count),
62 :total_count => assigns(:issue_count),
63 :limit => 25,
63 :limit => 25,
64 :offset => 0
64 :offset => 0
65 }
65 }
66 end
66 end
67
67
68 context "with offset and limit" do
68 context "with offset and limit" do
69 should "use the params" do
69 should "use the params" do
70 get '/issues.xml?offset=2&limit=3'
70 get '/issues.xml?offset=2&limit=3'
71
71
72 assert_equal 3, assigns(:limit)
72 assert_equal 3, assigns(:limit)
73 assert_equal 2, assigns(:offset)
73 assert_equal 2, assigns(:offset)
74 assert_tag :tag => 'issues', :children => {:count => 3, :only => {:tag => 'issue'}}
74 assert_tag :tag => 'issues', :children => {:count => 3, :only => {:tag => 'issue'}}
75 end
75 end
76 end
76 end
77
77
78 context "with nometa param" do
78 context "with nometa param" do
79 should "not contain metadata" do
79 should "not contain metadata" do
80 get '/issues.xml?nometa=1'
80 get '/issues.xml?nometa=1'
81
81
82 assert_tag :tag => 'issues',
82 assert_tag :tag => 'issues',
83 :attributes => {
83 :attributes => {
84 :type => 'array',
84 :type => 'array',
85 :total_count => nil,
85 :total_count => nil,
86 :limit => nil,
86 :limit => nil,
87 :offset => nil
87 :offset => nil
88 }
88 }
89 end
89 end
90 end
90 end
91
91
92 context "with nometa header" do
92 context "with nometa header" do
93 should "not contain metadata" do
93 should "not contain metadata" do
94 get '/issues.xml', {}, {'X-Redmine-Nometa' => '1'}
94 get '/issues.xml', {}, {'X-Redmine-Nometa' => '1'}
95
95
96 assert_tag :tag => 'issues',
96 assert_tag :tag => 'issues',
97 :attributes => {
97 :attributes => {
98 :type => 'array',
98 :type => 'array',
99 :total_count => nil,
99 :total_count => nil,
100 :limit => nil,
100 :limit => nil,
101 :offset => nil
101 :offset => nil
102 }
102 }
103 end
103 end
104 end
104 end
105
105
106 context "with relations" do
106 context "with relations" do
107 should "display relations" do
107 should "display relations" do
108 get '/issues.xml?include=relations'
108 get '/issues.xml?include=relations'
109
109
110 assert_response :success
110 assert_response :success
111 assert_equal 'application/xml', @response.content_type
111 assert_equal 'application/xml', @response.content_type
112 assert_tag 'relations',
112 assert_tag 'relations',
113 :parent => {:tag => 'issue', :child => {:tag => 'id', :content => '3'}},
113 :parent => {:tag => 'issue', :child => {:tag => 'id', :content => '3'}},
114 :children => {:count => 1},
114 :children => {:count => 1},
115 :child => {
115 :child => {
116 :tag => 'relation',
116 :tag => 'relation',
117 :attributes => {:id => '2', :issue_id => '2', :issue_to_id => '3',
117 :attributes => {:id => '2', :issue_id => '2', :issue_to_id => '3',
118 :relation_type => 'relates'}
118 :relation_type => 'relates'}
119 }
119 }
120 assert_tag 'relations',
120 assert_tag 'relations',
121 :parent => {:tag => 'issue', :child => {:tag => 'id', :content => '1'}},
121 :parent => {:tag => 'issue', :child => {:tag => 'id', :content => '1'}},
122 :children => {:count => 0}
122 :children => {:count => 0}
123 end
123 end
124 end
124 end
125
125
126 context "with invalid query params" do
126 context "with invalid query params" do
127 should "return errors" do
127 should "return errors" do
128 get '/issues.xml', {:f => ['start_date'], :op => {:start_date => '='}}
128 get '/issues.xml', {:f => ['start_date'], :op => {:start_date => '='}}
129
129
130 assert_response :unprocessable_entity
130 assert_response :unprocessable_entity
131 assert_equal 'application/xml', @response.content_type
131 assert_equal 'application/xml', @response.content_type
132 assert_tag 'errors', :child => {:tag => 'error', :content => "Start date can't be blank"}
132 assert_tag 'errors', :child => {:tag => 'error', :content => "Start date can't be blank"}
133 end
133 end
134 end
134 end
135
135
136 context "with custom field filter" do
136 context "with custom field filter" do
137 should "show only issues with the custom field value" do
137 should "show only issues with the custom field value" do
138 get '/issues.xml',
138 get '/issues.xml',
139 {:set_filter => 1, :f => ['cf_1'], :op => {:cf_1 => '='},
139 {:set_filter => 1, :f => ['cf_1'], :op => {:cf_1 => '='},
140 :v => {:cf_1 => ['MySQL']}}
140 :v => {:cf_1 => ['MySQL']}}
141 expected_ids = Issue.visible.
141 expected_ids = Issue.visible.
142 joins(:custom_values).
142 joins(:custom_values).
143 where(:custom_values => {:custom_field_id => 1, :value => 'MySQL'}).map(&:id)
143 where(:custom_values => {:custom_field_id => 1, :value => 'MySQL'}).map(&:id)
144 assert_select 'issues > issue > id', :count => expected_ids.count do |ids|
144 assert_select 'issues > issue > id', :count => expected_ids.count do |ids|
145 ids.each { |id| assert expected_ids.delete(id.children.first.content.to_i) }
145 ids.each { |id| assert expected_ids.delete(id.children.first.content.to_i) }
146 end
146 end
147 end
147 end
148 end
148 end
149
149
150 context "with custom field filter (shorthand method)" do
150 context "with custom field filter (shorthand method)" do
151 should "show only issues with the custom field value" do
151 should "show only issues with the custom field value" do
152 get '/issues.xml', { :cf_1 => 'MySQL' }
152 get '/issues.xml', { :cf_1 => 'MySQL' }
153
153
154 expected_ids = Issue.visible.
154 expected_ids = Issue.visible.
155 joins(:custom_values).
155 joins(:custom_values).
156 where(:custom_values => {:custom_field_id => 1, :value => 'MySQL'}).map(&:id)
156 where(:custom_values => {:custom_field_id => 1, :value => 'MySQL'}).map(&:id)
157
157
158 assert_select 'issues > issue > id', :count => expected_ids.count do |ids|
158 assert_select 'issues > issue > id', :count => expected_ids.count do |ids|
159 ids.each { |id| assert expected_ids.delete(id.children.first.content.to_i) }
159 ids.each { |id| assert expected_ids.delete(id.children.first.content.to_i) }
160 end
160 end
161 end
161 end
162 end
162 end
163 end
163 end
164
164
165 test "GET /issues/:id.xml with journals should format timestamps in ISO 8601" do
165 test "GET /issues/:id.xml with journals should format timestamps in ISO 8601" do
166 get '/issues/1.xml?include=journals'
166 get '/issues/1.xml?include=journals'
167
167
168 iso_date = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/
168 iso_date = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/
169 assert_select 'issue>created_on', :text => iso_date
169 assert_select 'issue>created_on', :text => iso_date
170 assert_select 'issue>updated_on', :text => iso_date
170 assert_select 'issue>updated_on', :text => iso_date
171 assert_select 'issue journal>created_on', :text => iso_date
171 assert_select 'issue journal>created_on', :text => iso_date
172 end
172 end
173
173
174 def test_index_should_include_issue_attributes
174 def test_index_should_include_issue_attributes
175 get '/issues.xml'
175 get '/issues.xml'
176 assert_select 'issues>issue>is_private', :text => 'false'
176 assert_select 'issues>issue>is_private', :text => 'false'
177 end
177 end
178
178
179 def test_index_should_allow_timestamp_filtering
179 def test_index_should_allow_timestamp_filtering
180 Issue.delete_all
180 Issue.delete_all
181 Issue.generate!(:subject => '1').update_column(:updated_on, Time.parse("2014-01-02T10:25:00Z"))
181 Issue.generate!(:subject => '1').update_column(:updated_on, Time.parse("2014-01-02T10:25:00Z"))
182 Issue.generate!(:subject => '2').update_column(:updated_on, Time.parse("2014-01-02T12:13:00Z"))
182 Issue.generate!(:subject => '2').update_column(:updated_on, Time.parse("2014-01-02T12:13:00Z"))
183
183
184 get '/issues.xml',
184 get '/issues.xml',
185 {:set_filter => 1, :f => ['updated_on'], :op => {:updated_on => '<='},
185 {:set_filter => 1, :f => ['updated_on'], :op => {:updated_on => '<='},
186 :v => {:updated_on => ['2014-01-02T12:00:00Z']}}
186 :v => {:updated_on => ['2014-01-02T12:00:00Z']}}
187 assert_select 'issues>issue', :count => 1
187 assert_select 'issues>issue', :count => 1
188 assert_select 'issues>issue>subject', :text => '1'
188 assert_select 'issues>issue>subject', :text => '1'
189
189
190 get '/issues.xml',
190 get '/issues.xml',
191 {:set_filter => 1, :f => ['updated_on'], :op => {:updated_on => '>='},
191 {:set_filter => 1, :f => ['updated_on'], :op => {:updated_on => '>='},
192 :v => {:updated_on => ['2014-01-02T12:00:00Z']}}
192 :v => {:updated_on => ['2014-01-02T12:00:00Z']}}
193 assert_select 'issues>issue', :count => 1
193 assert_select 'issues>issue', :count => 1
194 assert_select 'issues>issue>subject', :text => '2'
194 assert_select 'issues>issue>subject', :text => '2'
195
195
196 get '/issues.xml',
196 get '/issues.xml',
197 {:set_filter => 1, :f => ['updated_on'], :op => {:updated_on => '>='},
197 {:set_filter => 1, :f => ['updated_on'], :op => {:updated_on => '>='},
198 :v => {:updated_on => ['2014-01-02T08:00:00Z']}}
198 :v => {:updated_on => ['2014-01-02T08:00:00Z']}}
199 assert_select 'issues>issue', :count => 2
199 assert_select 'issues>issue', :count => 2
200 end
200 end
201
201
202 context "/index.json" do
202 context "/index.json" do
203 should_allow_api_authentication(:get, "/projects/private-child/issues.json")
203 should_allow_api_authentication(:get, "/projects/private-child/issues.json")
204 end
204 end
205
205
206 context "/index.xml with filter" do
206 context "/index.xml with filter" do
207 should "show only issues with the status_id" do
207 should "show only issues with the status_id" do
208 get '/issues.xml?status_id=5'
208 get '/issues.xml?status_id=5'
209
209
210 expected_ids = Issue.visible.where(:status_id => 5).map(&:id)
210 expected_ids = Issue.visible.where(:status_id => 5).map(&:id)
211
211
212 assert_select 'issues > issue > id', :count => expected_ids.count do |ids|
212 assert_select 'issues > issue > id', :count => expected_ids.count do |ids|
213 ids.each { |id| assert expected_ids.delete(id.children.first.content.to_i) }
213 ids.each { |id| assert expected_ids.delete(id.children.first.content.to_i) }
214 end
214 end
215 end
215 end
216 end
216 end
217
217
218 context "/index.json with filter" do
218 context "/index.json with filter" do
219 should "show only issues with the status_id" do
219 should "show only issues with the status_id" do
220 get '/issues.json?status_id=5'
220 get '/issues.json?status_id=5'
221
221
222 json = ActiveSupport::JSON.decode(response.body)
222 json = ActiveSupport::JSON.decode(response.body)
223 status_ids_used = json['issues'].collect {|j| j['status']['id'] }
223 status_ids_used = json['issues'].collect {|j| j['status']['id'] }
224 assert_equal 3, status_ids_used.length
224 assert_equal 3, status_ids_used.length
225 assert status_ids_used.all? {|id| id == 5 }
225 assert status_ids_used.all? {|id| id == 5 }
226 end
226 end
227
227
228 end
228 end
229
229
230 # Issue 6 is on a private project
230 # Issue 6 is on a private project
231 context "/issues/6.xml" do
231 context "/issues/6.xml" do
232 should_allow_api_authentication(:get, "/issues/6.xml")
232 should_allow_api_authentication(:get, "/issues/6.xml")
233 end
233 end
234
234
235 context "/issues/6.json" do
235 context "/issues/6.json" do
236 should_allow_api_authentication(:get, "/issues/6.json")
236 should_allow_api_authentication(:get, "/issues/6.json")
237 end
237 end
238
238
239 context "GET /issues/:id" do
239 context "GET /issues/:id" do
240 context "with journals" do
240 context "with journals" do
241 context ".xml" do
241 context ".xml" do
242 should "display journals" do
242 should "display journals" do
243 get '/issues/1.xml?include=journals'
243 get '/issues/1.xml?include=journals'
244
244
245 assert_tag :tag => 'issue',
245 assert_tag :tag => 'issue',
246 :child => {
246 :child => {
247 :tag => 'journals',
247 :tag => 'journals',
248 :attributes => { :type => 'array' },
248 :attributes => { :type => 'array' },
249 :child => {
249 :child => {
250 :tag => 'journal',
250 :tag => 'journal',
251 :attributes => { :id => '1'},
251 :attributes => { :id => '1'},
252 :child => {
252 :child => {
253 :tag => 'details',
253 :tag => 'details',
254 :attributes => { :type => 'array' },
254 :attributes => { :type => 'array' },
255 :child => {
255 :child => {
256 :tag => 'detail',
256 :tag => 'detail',
257 :attributes => { :name => 'status_id' },
257 :attributes => { :name => 'status_id' },
258 :child => {
258 :child => {
259 :tag => 'old_value',
259 :tag => 'old_value',
260 :content => '1',
260 :content => '1',
261 :sibling => {
261 :sibling => {
262 :tag => 'new_value',
262 :tag => 'new_value',
263 :content => '2'
263 :content => '2'
264 }
264 }
265 }
265 }
266 }
266 }
267 }
267 }
268 }
268 }
269 }
269 }
270 end
270 end
271 end
271 end
272 end
272 end
273
273
274 context "with custom fields" do
274 context "with custom fields" do
275 context ".xml" do
275 context ".xml" do
276 should "display custom fields" do
276 should "display custom fields" do
277 get '/issues/3.xml'
277 get '/issues/3.xml'
278
278
279 assert_tag :tag => 'issue',
279 assert_tag :tag => 'issue',
280 :child => {
280 :child => {
281 :tag => 'custom_fields',
281 :tag => 'custom_fields',
282 :attributes => { :type => 'array' },
282 :attributes => { :type => 'array' },
283 :child => {
283 :child => {
284 :tag => 'custom_field',
284 :tag => 'custom_field',
285 :attributes => { :id => '1'},
285 :attributes => { :id => '1'},
286 :child => {
286 :child => {
287 :tag => 'value',
287 :tag => 'value',
288 :content => 'MySQL'
288 :content => 'MySQL'
289 }
289 }
290 }
290 }
291 }
291 }
292
292
293 assert_nothing_raised do
293 assert_nothing_raised do
294 Hash.from_xml(response.body).to_xml
294 Hash.from_xml(response.body).to_xml
295 end
295 end
296 end
296 end
297 end
297 end
298 end
298 end
299
299
300 context "with multi custom fields" do
300 context "with multi custom fields" do
301 setup do
301 setup do
302 field = CustomField.find(1)
302 field = CustomField.find(1)
303 field.update_attribute :multiple, true
303 field.update_attribute :multiple, true
304 issue = Issue.find(3)
304 issue = Issue.find(3)
305 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
305 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
306 issue.save!
306 issue.save!
307 end
307 end
308
308
309 context ".xml" do
309 context ".xml" do
310 should "display custom fields" do
310 should "display custom fields" do
311 get '/issues/3.xml'
311 get '/issues/3.xml'
312 assert_response :success
312 assert_response :success
313 assert_tag :tag => 'issue',
313 assert_tag :tag => 'issue',
314 :child => {
314 :child => {
315 :tag => 'custom_fields',
315 :tag => 'custom_fields',
316 :attributes => { :type => 'array' },
316 :attributes => { :type => 'array' },
317 :child => {
317 :child => {
318 :tag => 'custom_field',
318 :tag => 'custom_field',
319 :attributes => { :id => '1'},
319 :attributes => { :id => '1'},
320 :child => {
320 :child => {
321 :tag => 'value',
321 :tag => 'value',
322 :attributes => { :type => 'array' },
322 :attributes => { :type => 'array' },
323 :children => { :count => 2 }
323 :children => { :count => 2 }
324 }
324 }
325 }
325 }
326 }
326 }
327
327
328 xml = Hash.from_xml(response.body)
328 xml = Hash.from_xml(response.body)
329 custom_fields = xml['issue']['custom_fields']
329 custom_fields = xml['issue']['custom_fields']
330 assert_kind_of Array, custom_fields
330 assert_kind_of Array, custom_fields
331 field = custom_fields.detect {|f| f['id'] == '1'}
331 field = custom_fields.detect {|f| f['id'] == '1'}
332 assert_kind_of Hash, field
332 assert_kind_of Hash, field
333 assert_equal ['MySQL', 'Oracle'], field['value'].sort
333 assert_equal ['MySQL', 'Oracle'], field['value'].sort
334 end
334 end
335 end
335 end
336
336
337 context ".json" do
337 context ".json" do
338 should "display custom fields" do
338 should "display custom fields" do
339 get '/issues/3.json'
339 get '/issues/3.json'
340 assert_response :success
340 assert_response :success
341 json = ActiveSupport::JSON.decode(response.body)
341 json = ActiveSupport::JSON.decode(response.body)
342 custom_fields = json['issue']['custom_fields']
342 custom_fields = json['issue']['custom_fields']
343 assert_kind_of Array, custom_fields
343 assert_kind_of Array, custom_fields
344 field = custom_fields.detect {|f| f['id'] == 1}
344 field = custom_fields.detect {|f| f['id'] == 1}
345 assert_kind_of Hash, field
345 assert_kind_of Hash, field
346 assert_equal ['MySQL', 'Oracle'], field['value'].sort
346 assert_equal ['MySQL', 'Oracle'], field['value'].sort
347 end
347 end
348 end
348 end
349 end
349 end
350
350
351 context "with empty value for multi custom field" do
351 context "with empty value for multi custom field" do
352 setup do
352 setup do
353 field = CustomField.find(1)
353 field = CustomField.find(1)
354 field.update_attribute :multiple, true
354 field.update_attribute :multiple, true
355 issue = Issue.find(3)
355 issue = Issue.find(3)
356 issue.custom_field_values = {1 => ['']}
356 issue.custom_field_values = {1 => ['']}
357 issue.save!
357 issue.save!
358 end
358 end
359
359
360 context ".xml" do
360 context ".xml" do
361 should "display custom fields" do
361 should "display custom fields" do
362 get '/issues/3.xml'
362 get '/issues/3.xml'
363 assert_response :success
363 assert_response :success
364 assert_tag :tag => 'issue',
364 assert_tag :tag => 'issue',
365 :child => {
365 :child => {
366 :tag => 'custom_fields',
366 :tag => 'custom_fields',
367 :attributes => { :type => 'array' },
367 :attributes => { :type => 'array' },
368 :child => {
368 :child => {
369 :tag => 'custom_field',
369 :tag => 'custom_field',
370 :attributes => { :id => '1'},
370 :attributes => { :id => '1'},
371 :child => {
371 :child => {
372 :tag => 'value',
372 :tag => 'value',
373 :attributes => { :type => 'array' },
373 :attributes => { :type => 'array' },
374 :children => { :count => 0 }
374 :children => { :count => 0 }
375 }
375 }
376 }
376 }
377 }
377 }
378
378
379 xml = Hash.from_xml(response.body)
379 xml = Hash.from_xml(response.body)
380 custom_fields = xml['issue']['custom_fields']
380 custom_fields = xml['issue']['custom_fields']
381 assert_kind_of Array, custom_fields
381 assert_kind_of Array, custom_fields
382 field = custom_fields.detect {|f| f['id'] == '1'}
382 field = custom_fields.detect {|f| f['id'] == '1'}
383 assert_kind_of Hash, field
383 assert_kind_of Hash, field
384 assert_equal [], field['value']
384 assert_equal [], field['value']
385 end
385 end
386 end
386 end
387
387
388 context ".json" do
388 context ".json" do
389 should "display custom fields" do
389 should "display custom fields" do
390 get '/issues/3.json'
390 get '/issues/3.json'
391 assert_response :success
391 assert_response :success
392 json = ActiveSupport::JSON.decode(response.body)
392 json = ActiveSupport::JSON.decode(response.body)
393 custom_fields = json['issue']['custom_fields']
393 custom_fields = json['issue']['custom_fields']
394 assert_kind_of Array, custom_fields
394 assert_kind_of Array, custom_fields
395 field = custom_fields.detect {|f| f['id'] == 1}
395 field = custom_fields.detect {|f| f['id'] == 1}
396 assert_kind_of Hash, field
396 assert_kind_of Hash, field
397 assert_equal [], field['value'].sort
397 assert_equal [], field['value'].sort
398 end
398 end
399 end
399 end
400 end
400 end
401
401
402 context "with attachments" do
402 context "with attachments" do
403 context ".xml" do
403 context ".xml" do
404 should "display attachments" do
404 should "display attachments" do
405 get '/issues/3.xml?include=attachments'
405 get '/issues/3.xml?include=attachments'
406
406
407 assert_tag :tag => 'issue',
407 assert_tag :tag => 'issue',
408 :child => {
408 :child => {
409 :tag => 'attachments',
409 :tag => 'attachments',
410 :children => {:count => 5},
410 :children => {:count => 5},
411 :child => {
411 :child => {
412 :tag => 'attachment',
412 :tag => 'attachment',
413 :child => {
413 :child => {
414 :tag => 'filename',
414 :tag => 'filename',
415 :content => 'source.rb',
415 :content => 'source.rb',
416 :sibling => {
416 :sibling => {
417 :tag => 'content_url',
417 :tag => 'content_url',
418 :content => 'http://www.example.com/attachments/download/4/source.rb'
418 :content => 'http://www.example.com/attachments/download/4/source.rb'
419 }
419 }
420 }
420 }
421 }
421 }
422 }
422 }
423 end
423 end
424 end
424 end
425 end
425 end
426
426
427 context "with subtasks" do
427 context "with subtasks" do
428 setup do
428 setup do
429 @c1 = Issue.create!(
429 @c1 = Issue.create!(
430 :status_id => 1, :subject => "child c1",
430 :status_id => 1, :subject => "child c1",
431 :tracker_id => 1, :project_id => 1, :author_id => 1,
431 :tracker_id => 1, :project_id => 1, :author_id => 1,
432 :parent_issue_id => 1
432 :parent_issue_id => 1
433 )
433 )
434 @c2 = Issue.create!(
434 @c2 = Issue.create!(
435 :status_id => 1, :subject => "child c2",
435 :status_id => 1, :subject => "child c2",
436 :tracker_id => 1, :project_id => 1, :author_id => 1,
436 :tracker_id => 1, :project_id => 1, :author_id => 1,
437 :parent_issue_id => 1
437 :parent_issue_id => 1
438 )
438 )
439 @c3 = Issue.create!(
439 @c3 = Issue.create!(
440 :status_id => 1, :subject => "child c3",
440 :status_id => 1, :subject => "child c3",
441 :tracker_id => 1, :project_id => 1, :author_id => 1,
441 :tracker_id => 1, :project_id => 1, :author_id => 1,
442 :parent_issue_id => @c1.id
442 :parent_issue_id => @c1.id
443 )
443 )
444 end
444 end
445
445
446 context ".xml" do
446 context ".xml" do
447 should "display children" do
447 should "display children" do
448 get '/issues/1.xml?include=children'
448 get '/issues/1.xml?include=children'
449
449
450 assert_tag :tag => 'issue',
450 assert_tag :tag => 'issue',
451 :child => {
451 :child => {
452 :tag => 'children',
452 :tag => 'children',
453 :children => {:count => 2},
453 :children => {:count => 2},
454 :child => {
454 :child => {
455 :tag => 'issue',
455 :tag => 'issue',
456 :attributes => {:id => @c1.id.to_s},
456 :attributes => {:id => @c1.id.to_s},
457 :child => {
457 :child => {
458 :tag => 'subject',
458 :tag => 'subject',
459 :content => 'child c1',
459 :content => 'child c1',
460 :sibling => {
460 :sibling => {
461 :tag => 'children',
461 :tag => 'children',
462 :children => {:count => 1},
462 :children => {:count => 1},
463 :child => {
463 :child => {
464 :tag => 'issue',
464 :tag => 'issue',
465 :attributes => {:id => @c3.id.to_s}
465 :attributes => {:id => @c3.id.to_s}
466 }
466 }
467 }
467 }
468 }
468 }
469 }
469 }
470 }
470 }
471 end
471 end
472
472
473 context ".json" do
473 context ".json" do
474 should "display children" do
474 should "display children" do
475 get '/issues/1.json?include=children'
475 get '/issues/1.json?include=children'
476
476
477 json = ActiveSupport::JSON.decode(response.body)
477 json = ActiveSupport::JSON.decode(response.body)
478 assert_equal([
478 assert_equal([
479 {
479 {
480 'id' => @c1.id, 'subject' => 'child c1', 'tracker' => {'id' => 1, 'name' => 'Bug'},
480 'id' => @c1.id, 'subject' => 'child c1', 'tracker' => {'id' => 1, 'name' => 'Bug'},
481 'children' => [{'id' => @c3.id, 'subject' => 'child c3',
481 'children' => [{'id' => @c3.id, 'subject' => 'child c3',
482 'tracker' => {'id' => 1, 'name' => 'Bug'} }]
482 'tracker' => {'id' => 1, 'name' => 'Bug'} }]
483 },
483 },
484 { 'id' => @c2.id, 'subject' => 'child c2', 'tracker' => {'id' => 1, 'name' => 'Bug'} }
484 { 'id' => @c2.id, 'subject' => 'child c2', 'tracker' => {'id' => 1, 'name' => 'Bug'} }
485 ],
485 ],
486 json['issue']['children'])
486 json['issue']['children'])
487 end
487 end
488 end
488 end
489 end
489 end
490 end
490 end
491 end
491 end
492
492
493 def test_show_should_include_issue_attributes
493 def test_show_should_include_issue_attributes
494 get '/issues/1.xml'
494 get '/issues/1.xml'
495 assert_select 'issue>is_private', :text => 'false'
495 assert_select 'issue>is_private', :text => 'false'
496 end
496 end
497
497
498 test "GET /issues/:id.xml?include=watchers should include watchers" do
498 test "GET /issues/:id.xml?include=watchers should include watchers" do
499 Watcher.create!(:user_id => 3, :watchable => Issue.find(1))
499 Watcher.create!(:user_id => 3, :watchable => Issue.find(1))
500
500
501 get '/issues/1.xml?include=watchers', {}, credentials('jsmith')
501 get '/issues/1.xml?include=watchers', {}, credentials('jsmith')
502
502
503 assert_response :ok
503 assert_response :ok
504 assert_equal 'application/xml', response.content_type
504 assert_equal 'application/xml', response.content_type
505 assert_select 'issue' do
505 assert_select 'issue' do
506 assert_select 'watchers', Issue.find(1).watchers.count
506 assert_select 'watchers', Issue.find(1).watchers.count
507 assert_select 'watchers' do
507 assert_select 'watchers' do
508 assert_select 'user[id=3]'
508 assert_select 'user[id=3]'
509 end
509 end
510 end
510 end
511 end
511 end
512
512
513 test "GET /issues/:id.xml should not disclose associated changesets from projects the user has no access to" do
514 project = Project.generate!(:is_public => false)
515 repository = Repository::Subversion.create!(:project => project, :url => "svn://localhost")
516 Issue.find(1).changesets << Changeset.generate!(:repository => repository)
517 assert Issue.find(1).changesets.any?
518
519 get '/issues/1.xml?include=changesets', {}, credentials('jsmith')
520
521 # the user jsmith has no permission to view the associated changeset
522 assert_select 'issue changesets[type=array]' do
523 assert_select 'changeset', 0
524 end
525 end
526
513 context "POST /issues.xml" do
527 context "POST /issues.xml" do
514 should_allow_api_authentication(
528 should_allow_api_authentication(
515 :post,
529 :post,
516 '/issues.xml',
530 '/issues.xml',
517 {:issue => {:project_id => 1, :subject => 'API test', :tracker_id => 2, :status_id => 3}},
531 {:issue => {:project_id => 1, :subject => 'API test', :tracker_id => 2, :status_id => 3}},
518 {:success_code => :created}
532 {:success_code => :created}
519 )
533 )
520 should "create an issue with the attributes" do
534 should "create an issue with the attributes" do
521 assert_difference('Issue.count') do
535 assert_difference('Issue.count') do
522 post '/issues.xml',
536 post '/issues.xml',
523 {:issue => {:project_id => 1, :subject => 'API test',
537 {:issue => {:project_id => 1, :subject => 'API test',
524 :tracker_id => 2, :status_id => 3}}, credentials('jsmith')
538 :tracker_id => 2, :status_id => 3}}, credentials('jsmith')
525 end
539 end
526 issue = Issue.order('id DESC').first
540 issue = Issue.order('id DESC').first
527 assert_equal 1, issue.project_id
541 assert_equal 1, issue.project_id
528 assert_equal 2, issue.tracker_id
542 assert_equal 2, issue.tracker_id
529 assert_equal 3, issue.status_id
543 assert_equal 3, issue.status_id
530 assert_equal 'API test', issue.subject
544 assert_equal 'API test', issue.subject
531
545
532 assert_response :created
546 assert_response :created
533 assert_equal 'application/xml', @response.content_type
547 assert_equal 'application/xml', @response.content_type
534 assert_tag 'issue', :child => {:tag => 'id', :content => issue.id.to_s}
548 assert_tag 'issue', :child => {:tag => 'id', :content => issue.id.to_s}
535 end
549 end
536 end
550 end
537
551
538 test "POST /issues.xml with watcher_user_ids should create issue with watchers" do
552 test "POST /issues.xml with watcher_user_ids should create issue with watchers" do
539 assert_difference('Issue.count') do
553 assert_difference('Issue.count') do
540 post '/issues.xml',
554 post '/issues.xml',
541 {:issue => {:project_id => 1, :subject => 'Watchers',
555 {:issue => {:project_id => 1, :subject => 'Watchers',
542 :tracker_id => 2, :status_id => 3, :watcher_user_ids => [3, 1]}}, credentials('jsmith')
556 :tracker_id => 2, :status_id => 3, :watcher_user_ids => [3, 1]}}, credentials('jsmith')
543 assert_response :created
557 assert_response :created
544 end
558 end
545 issue = Issue.order('id desc').first
559 issue = Issue.order('id desc').first
546 assert_equal 2, issue.watchers.size
560 assert_equal 2, issue.watchers.size
547 assert_equal [1, 3], issue.watcher_user_ids.sort
561 assert_equal [1, 3], issue.watcher_user_ids.sort
548 end
562 end
549
563
550 context "POST /issues.xml with failure" do
564 context "POST /issues.xml with failure" do
551 should "have an errors tag" do
565 should "have an errors tag" do
552 assert_no_difference('Issue.count') do
566 assert_no_difference('Issue.count') do
553 post '/issues.xml', {:issue => {:project_id => 1}}, credentials('jsmith')
567 post '/issues.xml', {:issue => {:project_id => 1}}, credentials('jsmith')
554 end
568 end
555
569
556 assert_tag :errors, :child => {:tag => 'error', :content => "Subject can't be blank"}
570 assert_tag :errors, :child => {:tag => 'error', :content => "Subject can't be blank"}
557 end
571 end
558 end
572 end
559
573
560 context "POST /issues.json" do
574 context "POST /issues.json" do
561 should_allow_api_authentication(:post,
575 should_allow_api_authentication(:post,
562 '/issues.json',
576 '/issues.json',
563 {:issue => {:project_id => 1, :subject => 'API test',
577 {:issue => {:project_id => 1, :subject => 'API test',
564 :tracker_id => 2, :status_id => 3}},
578 :tracker_id => 2, :status_id => 3}},
565 {:success_code => :created})
579 {:success_code => :created})
566
580
567 should "create an issue with the attributes" do
581 should "create an issue with the attributes" do
568 assert_difference('Issue.count') do
582 assert_difference('Issue.count') do
569 post '/issues.json',
583 post '/issues.json',
570 {:issue => {:project_id => 1, :subject => 'API test',
584 {:issue => {:project_id => 1, :subject => 'API test',
571 :tracker_id => 2, :status_id => 3}},
585 :tracker_id => 2, :status_id => 3}},
572 credentials('jsmith')
586 credentials('jsmith')
573 end
587 end
574
588
575 issue = Issue.order('id DESC').first
589 issue = Issue.order('id DESC').first
576 assert_equal 1, issue.project_id
590 assert_equal 1, issue.project_id
577 assert_equal 2, issue.tracker_id
591 assert_equal 2, issue.tracker_id
578 assert_equal 3, issue.status_id
592 assert_equal 3, issue.status_id
579 assert_equal 'API test', issue.subject
593 assert_equal 'API test', issue.subject
580 end
594 end
581
595
582 end
596 end
583
597
584 context "POST /issues.json with failure" do
598 context "POST /issues.json with failure" do
585 should "have an errors element" do
599 should "have an errors element" do
586 assert_no_difference('Issue.count') do
600 assert_no_difference('Issue.count') do
587 post '/issues.json', {:issue => {:project_id => 1}}, credentials('jsmith')
601 post '/issues.json', {:issue => {:project_id => 1}}, credentials('jsmith')
588 end
602 end
589
603
590 json = ActiveSupport::JSON.decode(response.body)
604 json = ActiveSupport::JSON.decode(response.body)
591 assert json['errors'].include?("Subject can't be blank")
605 assert json['errors'].include?("Subject can't be blank")
592 end
606 end
593 end
607 end
594
608
595 # Issue 6 is on a private project
609 # Issue 6 is on a private project
596 context "PUT /issues/6.xml" do
610 context "PUT /issues/6.xml" do
597 setup do
611 setup do
598 @parameters = {:issue => {:subject => 'API update', :notes => 'A new note'}}
612 @parameters = {:issue => {:subject => 'API update', :notes => 'A new note'}}
599 end
613 end
600
614
601 should_allow_api_authentication(:put,
615 should_allow_api_authentication(:put,
602 '/issues/6.xml',
616 '/issues/6.xml',
603 {:issue => {:subject => 'API update', :notes => 'A new note'}},
617 {:issue => {:subject => 'API update', :notes => 'A new note'}},
604 {:success_code => :ok})
618 {:success_code => :ok})
605
619
606 should "not create a new issue" do
620 should "not create a new issue" do
607 assert_no_difference('Issue.count') do
621 assert_no_difference('Issue.count') do
608 put '/issues/6.xml', @parameters, credentials('jsmith')
622 put '/issues/6.xml', @parameters, credentials('jsmith')
609 end
623 end
610 end
624 end
611
625
612 should "create a new journal" do
626 should "create a new journal" do
613 assert_difference('Journal.count') do
627 assert_difference('Journal.count') do
614 put '/issues/6.xml', @parameters, credentials('jsmith')
628 put '/issues/6.xml', @parameters, credentials('jsmith')
615 end
629 end
616 end
630 end
617
631
618 should "add the note to the journal" do
632 should "add the note to the journal" do
619 put '/issues/6.xml', @parameters, credentials('jsmith')
633 put '/issues/6.xml', @parameters, credentials('jsmith')
620
634
621 journal = Journal.last
635 journal = Journal.last
622 assert_equal "A new note", journal.notes
636 assert_equal "A new note", journal.notes
623 end
637 end
624
638
625 should "update the issue" do
639 should "update the issue" do
626 put '/issues/6.xml', @parameters, credentials('jsmith')
640 put '/issues/6.xml', @parameters, credentials('jsmith')
627
641
628 issue = Issue.find(6)
642 issue = Issue.find(6)
629 assert_equal "API update", issue.subject
643 assert_equal "API update", issue.subject
630 end
644 end
631
645
632 end
646 end
633
647
634 context "PUT /issues/3.xml with custom fields" do
648 context "PUT /issues/3.xml with custom fields" do
635 setup do
649 setup do
636 @parameters = {
650 @parameters = {
637 :issue => {:custom_fields => [{'id' => '1', 'value' => 'PostgreSQL' },
651 :issue => {:custom_fields => [{'id' => '1', 'value' => 'PostgreSQL' },
638 {'id' => '2', 'value' => '150'}]}
652 {'id' => '2', 'value' => '150'}]}
639 }
653 }
640 end
654 end
641
655
642 should "update custom fields" do
656 should "update custom fields" do
643 assert_no_difference('Issue.count') do
657 assert_no_difference('Issue.count') do
644 put '/issues/3.xml', @parameters, credentials('jsmith')
658 put '/issues/3.xml', @parameters, credentials('jsmith')
645 end
659 end
646
660
647 issue = Issue.find(3)
661 issue = Issue.find(3)
648 assert_equal '150', issue.custom_value_for(2).value
662 assert_equal '150', issue.custom_value_for(2).value
649 assert_equal 'PostgreSQL', issue.custom_value_for(1).value
663 assert_equal 'PostgreSQL', issue.custom_value_for(1).value
650 end
664 end
651 end
665 end
652
666
653 context "PUT /issues/3.xml with multi custom fields" do
667 context "PUT /issues/3.xml with multi custom fields" do
654 setup do
668 setup do
655 field = CustomField.find(1)
669 field = CustomField.find(1)
656 field.update_attribute :multiple, true
670 field.update_attribute :multiple, true
657 @parameters = {
671 @parameters = {
658 :issue => {:custom_fields => [{'id' => '1', 'value' => ['MySQL', 'PostgreSQL'] },
672 :issue => {:custom_fields => [{'id' => '1', 'value' => ['MySQL', 'PostgreSQL'] },
659 {'id' => '2', 'value' => '150'}]}
673 {'id' => '2', 'value' => '150'}]}
660 }
674 }
661 end
675 end
662
676
663 should "update custom fields" do
677 should "update custom fields" do
664 assert_no_difference('Issue.count') do
678 assert_no_difference('Issue.count') do
665 put '/issues/3.xml', @parameters, credentials('jsmith')
679 put '/issues/3.xml', @parameters, credentials('jsmith')
666 end
680 end
667
681
668 issue = Issue.find(3)
682 issue = Issue.find(3)
669 assert_equal '150', issue.custom_value_for(2).value
683 assert_equal '150', issue.custom_value_for(2).value
670 assert_equal ['MySQL', 'PostgreSQL'], issue.custom_field_value(1).sort
684 assert_equal ['MySQL', 'PostgreSQL'], issue.custom_field_value(1).sort
671 end
685 end
672 end
686 end
673
687
674 context "PUT /issues/3.xml with project change" do
688 context "PUT /issues/3.xml with project change" do
675 setup do
689 setup do
676 @parameters = {:issue => {:project_id => 2, :subject => 'Project changed'}}
690 @parameters = {:issue => {:project_id => 2, :subject => 'Project changed'}}
677 end
691 end
678
692
679 should "update project" do
693 should "update project" do
680 assert_no_difference('Issue.count') do
694 assert_no_difference('Issue.count') do
681 put '/issues/3.xml', @parameters, credentials('jsmith')
695 put '/issues/3.xml', @parameters, credentials('jsmith')
682 end
696 end
683
697
684 issue = Issue.find(3)
698 issue = Issue.find(3)
685 assert_equal 2, issue.project_id
699 assert_equal 2, issue.project_id
686 assert_equal 'Project changed', issue.subject
700 assert_equal 'Project changed', issue.subject
687 end
701 end
688 end
702 end
689
703
690 context "PUT /issues/6.xml with failed update" do
704 context "PUT /issues/6.xml with failed update" do
691 setup do
705 setup do
692 @parameters = {:issue => {:subject => ''}}
706 @parameters = {:issue => {:subject => ''}}
693 end
707 end
694
708
695 should "not create a new issue" do
709 should "not create a new issue" do
696 assert_no_difference('Issue.count') do
710 assert_no_difference('Issue.count') do
697 put '/issues/6.xml', @parameters, credentials('jsmith')
711 put '/issues/6.xml', @parameters, credentials('jsmith')
698 end
712 end
699 end
713 end
700
714
701 should "not create a new journal" do
715 should "not create a new journal" do
702 assert_no_difference('Journal.count') do
716 assert_no_difference('Journal.count') do
703 put '/issues/6.xml', @parameters, credentials('jsmith')
717 put '/issues/6.xml', @parameters, credentials('jsmith')
704 end
718 end
705 end
719 end
706
720
707 should "have an errors tag" do
721 should "have an errors tag" do
708 put '/issues/6.xml', @parameters, credentials('jsmith')
722 put '/issues/6.xml', @parameters, credentials('jsmith')
709
723
710 assert_tag :errors, :child => {:tag => 'error', :content => "Subject can't be blank"}
724 assert_tag :errors, :child => {:tag => 'error', :content => "Subject can't be blank"}
711 end
725 end
712 end
726 end
713
727
714 context "PUT /issues/6.json" do
728 context "PUT /issues/6.json" do
715 setup do
729 setup do
716 @parameters = {:issue => {:subject => 'API update', :notes => 'A new note'}}
730 @parameters = {:issue => {:subject => 'API update', :notes => 'A new note'}}
717 end
731 end
718
732
719 should_allow_api_authentication(:put,
733 should_allow_api_authentication(:put,
720 '/issues/6.json',
734 '/issues/6.json',
721 {:issue => {:subject => 'API update', :notes => 'A new note'}},
735 {:issue => {:subject => 'API update', :notes => 'A new note'}},
722 {:success_code => :ok})
736 {:success_code => :ok})
723
737
724 should "update the issue" do
738 should "update the issue" do
725 assert_no_difference('Issue.count') do
739 assert_no_difference('Issue.count') do
726 assert_difference('Journal.count') do
740 assert_difference('Journal.count') do
727 put '/issues/6.json', @parameters, credentials('jsmith')
741 put '/issues/6.json', @parameters, credentials('jsmith')
728
742
729 assert_response :ok
743 assert_response :ok
730 assert_equal '', response.body
744 assert_equal '', response.body
731 end
745 end
732 end
746 end
733
747
734 issue = Issue.find(6)
748 issue = Issue.find(6)
735 assert_equal "API update", issue.subject
749 assert_equal "API update", issue.subject
736 journal = Journal.last
750 journal = Journal.last
737 assert_equal "A new note", journal.notes
751 assert_equal "A new note", journal.notes
738 end
752 end
739 end
753 end
740
754
741 context "PUT /issues/6.json with failed update" do
755 context "PUT /issues/6.json with failed update" do
742 should "return errors" do
756 should "return errors" do
743 assert_no_difference('Issue.count') do
757 assert_no_difference('Issue.count') do
744 assert_no_difference('Journal.count') do
758 assert_no_difference('Journal.count') do
745 put '/issues/6.json', {:issue => {:subject => ''}}, credentials('jsmith')
759 put '/issues/6.json', {:issue => {:subject => ''}}, credentials('jsmith')
746
760
747 assert_response :unprocessable_entity
761 assert_response :unprocessable_entity
748 end
762 end
749 end
763 end
750
764
751 json = ActiveSupport::JSON.decode(response.body)
765 json = ActiveSupport::JSON.decode(response.body)
752 assert json['errors'].include?("Subject can't be blank")
766 assert json['errors'].include?("Subject can't be blank")
753 end
767 end
754 end
768 end
755
769
756 context "DELETE /issues/1.xml" do
770 context "DELETE /issues/1.xml" do
757 should_allow_api_authentication(:delete,
771 should_allow_api_authentication(:delete,
758 '/issues/6.xml',
772 '/issues/6.xml',
759 {},
773 {},
760 {:success_code => :ok})
774 {:success_code => :ok})
761
775
762 should "delete the issue" do
776 should "delete the issue" do
763 assert_difference('Issue.count', -1) do
777 assert_difference('Issue.count', -1) do
764 delete '/issues/6.xml', {}, credentials('jsmith')
778 delete '/issues/6.xml', {}, credentials('jsmith')
765
779
766 assert_response :ok
780 assert_response :ok
767 assert_equal '', response.body
781 assert_equal '', response.body
768 end
782 end
769
783
770 assert_nil Issue.find_by_id(6)
784 assert_nil Issue.find_by_id(6)
771 end
785 end
772 end
786 end
773
787
774 context "DELETE /issues/1.json" do
788 context "DELETE /issues/1.json" do
775 should_allow_api_authentication(:delete,
789 should_allow_api_authentication(:delete,
776 '/issues/6.json',
790 '/issues/6.json',
777 {},
791 {},
778 {:success_code => :ok})
792 {:success_code => :ok})
779
793
780 should "delete the issue" do
794 should "delete the issue" do
781 assert_difference('Issue.count', -1) do
795 assert_difference('Issue.count', -1) do
782 delete '/issues/6.json', {}, credentials('jsmith')
796 delete '/issues/6.json', {}, credentials('jsmith')
783
797
784 assert_response :ok
798 assert_response :ok
785 assert_equal '', response.body
799 assert_equal '', response.body
786 end
800 end
787
801
788 assert_nil Issue.find_by_id(6)
802 assert_nil Issue.find_by_id(6)
789 end
803 end
790 end
804 end
791
805
792 test "POST /issues/:id/watchers.xml should add watcher" do
806 test "POST /issues/:id/watchers.xml should add watcher" do
793 assert_difference 'Watcher.count' do
807 assert_difference 'Watcher.count' do
794 post '/issues/1/watchers.xml', {:user_id => 3}, credentials('jsmith')
808 post '/issues/1/watchers.xml', {:user_id => 3}, credentials('jsmith')
795
809
796 assert_response :ok
810 assert_response :ok
797 assert_equal '', response.body
811 assert_equal '', response.body
798 end
812 end
799 watcher = Watcher.order('id desc').first
813 watcher = Watcher.order('id desc').first
800 assert_equal Issue.find(1), watcher.watchable
814 assert_equal Issue.find(1), watcher.watchable
801 assert_equal User.find(3), watcher.user
815 assert_equal User.find(3), watcher.user
802 end
816 end
803
817
804 test "DELETE /issues/:id/watchers/:user_id.xml should remove watcher" do
818 test "DELETE /issues/:id/watchers/:user_id.xml should remove watcher" do
805 Watcher.create!(:user_id => 3, :watchable => Issue.find(1))
819 Watcher.create!(:user_id => 3, :watchable => Issue.find(1))
806
820
807 assert_difference 'Watcher.count', -1 do
821 assert_difference 'Watcher.count', -1 do
808 delete '/issues/1/watchers/3.xml', {}, credentials('jsmith')
822 delete '/issues/1/watchers/3.xml', {}, credentials('jsmith')
809
823
810 assert_response :ok
824 assert_response :ok
811 assert_equal '', response.body
825 assert_equal '', response.body
812 end
826 end
813 assert_equal false, Issue.find(1).watched_by?(User.find(3))
827 assert_equal false, Issue.find(1).watched_by?(User.find(3))
814 end
828 end
815
829
816 def test_create_issue_with_uploaded_file
830 def test_create_issue_with_uploaded_file
817 set_tmp_attachments_directory
831 set_tmp_attachments_directory
818 # upload the file
832 # upload the file
819 assert_difference 'Attachment.count' do
833 assert_difference 'Attachment.count' do
820 post '/uploads.xml', 'test_create_with_upload',
834 post '/uploads.xml', 'test_create_with_upload',
821 {"CONTENT_TYPE" => 'application/octet-stream'}.merge(credentials('jsmith'))
835 {"CONTENT_TYPE" => 'application/octet-stream'}.merge(credentials('jsmith'))
822 assert_response :created
836 assert_response :created
823 end
837 end
824 xml = Hash.from_xml(response.body)
838 xml = Hash.from_xml(response.body)
825 token = xml['upload']['token']
839 token = xml['upload']['token']
826 attachment = Attachment.order('id DESC').first
840 attachment = Attachment.order('id DESC').first
827
841
828 # create the issue with the upload's token
842 # create the issue with the upload's token
829 assert_difference 'Issue.count' do
843 assert_difference 'Issue.count' do
830 post '/issues.xml',
844 post '/issues.xml',
831 {:issue => {:project_id => 1, :subject => 'Uploaded file',
845 {:issue => {:project_id => 1, :subject => 'Uploaded file',
832 :uploads => [{:token => token, :filename => 'test.txt',
846 :uploads => [{:token => token, :filename => 'test.txt',
833 :content_type => 'text/plain'}]}},
847 :content_type => 'text/plain'}]}},
834 credentials('jsmith')
848 credentials('jsmith')
835 assert_response :created
849 assert_response :created
836 end
850 end
837 issue = Issue.order('id DESC').first
851 issue = Issue.order('id DESC').first
838 assert_equal 1, issue.attachments.count
852 assert_equal 1, issue.attachments.count
839 assert_equal attachment, issue.attachments.first
853 assert_equal attachment, issue.attachments.first
840
854
841 attachment.reload
855 attachment.reload
842 assert_equal 'test.txt', attachment.filename
856 assert_equal 'test.txt', attachment.filename
843 assert_equal 'text/plain', attachment.content_type
857 assert_equal 'text/plain', attachment.content_type
844 assert_equal 'test_create_with_upload'.size, attachment.filesize
858 assert_equal 'test_create_with_upload'.size, attachment.filesize
845 assert_equal 2, attachment.author_id
859 assert_equal 2, attachment.author_id
846
860
847 # get the issue with its attachments
861 # get the issue with its attachments
848 get "/issues/#{issue.id}.xml", :include => 'attachments'
862 get "/issues/#{issue.id}.xml", :include => 'attachments'
849 assert_response :success
863 assert_response :success
850 xml = Hash.from_xml(response.body)
864 xml = Hash.from_xml(response.body)
851 attachments = xml['issue']['attachments']
865 attachments = xml['issue']['attachments']
852 assert_kind_of Array, attachments
866 assert_kind_of Array, attachments
853 assert_equal 1, attachments.size
867 assert_equal 1, attachments.size
854 url = attachments.first['content_url']
868 url = attachments.first['content_url']
855 assert_not_nil url
869 assert_not_nil url
856
870
857 # download the attachment
871 # download the attachment
858 get url
872 get url
859 assert_response :success
873 assert_response :success
860 end
874 end
861
875
862 def test_update_issue_with_uploaded_file
876 def test_update_issue_with_uploaded_file
863 set_tmp_attachments_directory
877 set_tmp_attachments_directory
864 # upload the file
878 # upload the file
865 assert_difference 'Attachment.count' do
879 assert_difference 'Attachment.count' do
866 post '/uploads.xml', 'test_upload_with_upload',
880 post '/uploads.xml', 'test_upload_with_upload',
867 {"CONTENT_TYPE" => 'application/octet-stream'}.merge(credentials('jsmith'))
881 {"CONTENT_TYPE" => 'application/octet-stream'}.merge(credentials('jsmith'))
868 assert_response :created
882 assert_response :created
869 end
883 end
870 xml = Hash.from_xml(response.body)
884 xml = Hash.from_xml(response.body)
871 token = xml['upload']['token']
885 token = xml['upload']['token']
872 attachment = Attachment.order('id DESC').first
886 attachment = Attachment.order('id DESC').first
873
887
874 # update the issue with the upload's token
888 # update the issue with the upload's token
875 assert_difference 'Journal.count' do
889 assert_difference 'Journal.count' do
876 put '/issues/1.xml',
890 put '/issues/1.xml',
877 {:issue => {:notes => 'Attachment added',
891 {:issue => {:notes => 'Attachment added',
878 :uploads => [{:token => token, :filename => 'test.txt',
892 :uploads => [{:token => token, :filename => 'test.txt',
879 :content_type => 'text/plain'}]}},
893 :content_type => 'text/plain'}]}},
880 credentials('jsmith')
894 credentials('jsmith')
881 assert_response :ok
895 assert_response :ok
882 assert_equal '', @response.body
896 assert_equal '', @response.body
883 end
897 end
884
898
885 issue = Issue.find(1)
899 issue = Issue.find(1)
886 assert_include attachment, issue.attachments
900 assert_include attachment, issue.attachments
887 end
901 end
888 end
902 end
General Comments 0
You need to be logged in to leave comments. Login now