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