##// END OF EJS Templates
Adds subtasks to GET /issues/:id API (#5338)....
Jean-Philippe Lang -
r4351:915748204965
parent child
Show More
@@ -1,249 +1,263
1 1 # redMine - project management software
2 2 # Copyright (C) 2006 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 module IssuesHelper
19 19 include ApplicationHelper
20 20
21 21 def issue_list(issues, &block)
22 22 ancestors = []
23 23 issues.each do |issue|
24 24 while (ancestors.any? && !issue.is_descendant_of?(ancestors.last))
25 25 ancestors.pop
26 26 end
27 27 yield issue, ancestors.size
28 28 ancestors << issue unless issue.leaf?
29 29 end
30 30 end
31 31
32 32 # Renders a HTML/CSS tooltip
33 33 #
34 34 # To use, a trigger div is needed. This is a div with the class of "tooltip"
35 35 # that contains this method wrapped in a span with the class of "tip"
36 36 #
37 37 # <div class="tooltip"><%= link_to_issue(issue) %>
38 38 # <span class="tip"><%= render_issue_tooltip(issue) %></span>
39 39 # </div>
40 40 #
41 41 def render_issue_tooltip(issue)
42 42 @cached_label_status ||= l(:field_status)
43 43 @cached_label_start_date ||= l(:field_start_date)
44 44 @cached_label_due_date ||= l(:field_due_date)
45 45 @cached_label_assigned_to ||= l(:field_assigned_to)
46 46 @cached_label_priority ||= l(:field_priority)
47 47 @cached_label_project ||= l(:field_project)
48 48
49 49 link_to_issue(issue) + "<br /><br />" +
50 50 "<strong>#{@cached_label_project}</strong>: #{link_to_project(issue.project)}<br />" +
51 51 "<strong>#{@cached_label_status}</strong>: #{issue.status.name}<br />" +
52 52 "<strong>#{@cached_label_start_date}</strong>: #{format_date(issue.start_date)}<br />" +
53 53 "<strong>#{@cached_label_due_date}</strong>: #{format_date(issue.due_date)}<br />" +
54 54 "<strong>#{@cached_label_assigned_to}</strong>: #{issue.assigned_to}<br />" +
55 55 "<strong>#{@cached_label_priority}</strong>: #{issue.priority.name}"
56 56 end
57 57
58 58 def render_issue_subject_with_tree(issue)
59 59 s = ''
60 60 issue.ancestors.each do |ancestor|
61 61 s << '<div>' + content_tag('p', link_to_issue(ancestor))
62 62 end
63 63 s << '<div>' + content_tag('h3', h(issue.subject))
64 64 s << '</div>' * (issue.ancestors.size + 1)
65 65 s
66 66 end
67 67
68 68 def render_descendants_tree(issue)
69 69 s = '<form><table class="list issues">'
70 70 issue_list(issue.descendants.sort_by(&:lft)) do |child, level|
71 71 s << content_tag('tr',
72 72 content_tag('td', check_box_tag("ids[]", child.id, false, :id => nil), :class => 'checkbox') +
73 73 content_tag('td', link_to_issue(child, :truncate => 60), :class => 'subject') +
74 74 content_tag('td', h(child.status)) +
75 75 content_tag('td', link_to_user(child.assigned_to)) +
76 76 content_tag('td', progress_bar(child.done_ratio, :width => '80px')),
77 77 :class => "issue issue-#{child.id} hascontextmenu #{level > 0 ? "idnt idnt-#{level}" : nil}")
78 78 end
79 79 s << '</form></table>'
80 80 s
81 81 end
82 82
83 83 def render_custom_fields_rows(issue)
84 84 return if issue.custom_field_values.empty?
85 85 ordered_values = []
86 86 half = (issue.custom_field_values.size / 2.0).ceil
87 87 half.times do |i|
88 88 ordered_values << issue.custom_field_values[i]
89 89 ordered_values << issue.custom_field_values[i + half]
90 90 end
91 91 s = "<tr>\n"
92 92 n = 0
93 93 ordered_values.compact.each do |value|
94 94 s << "</tr>\n<tr>\n" if n > 0 && (n % 2) == 0
95 95 s << "\t<th>#{ h(value.custom_field.name) }:</th><td>#{ simple_format_without_paragraph(h(show_value(value))) }</td>\n"
96 96 n += 1
97 97 end
98 98 s << "</tr>\n"
99 99 s
100 100 end
101 101
102 102 def sidebar_queries
103 103 unless @sidebar_queries
104 104 # User can see public queries and his own queries
105 105 visible = ARCondition.new(["is_public = ? OR user_id = ?", true, (User.current.logged? ? User.current.id : 0)])
106 106 # Project specific queries and global queries
107 107 visible << (@project.nil? ? ["project_id IS NULL"] : ["project_id IS NULL OR project_id = ?", @project.id])
108 108 @sidebar_queries = Query.find(:all,
109 109 :select => 'id, name',
110 110 :order => "name ASC",
111 111 :conditions => visible.conditions)
112 112 end
113 113 @sidebar_queries
114 114 end
115 115
116 116 def show_detail(detail, no_html=false)
117 117 case detail.property
118 118 when 'attr'
119 119 field = detail.prop_key.to_s.gsub(/\_id$/, "")
120 120 label = l(("field_" + field).to_sym)
121 121 case
122 122 when ['due_date', 'start_date'].include?(detail.prop_key)
123 123 value = format_date(detail.value.to_date) if detail.value
124 124 old_value = format_date(detail.old_value.to_date) if detail.old_value
125 125
126 126 when ['project_id', 'status_id', 'tracker_id', 'assigned_to_id', 'priority_id', 'category_id', 'fixed_version_id'].include?(detail.prop_key)
127 127 value = find_name_by_reflection(field, detail.value)
128 128 old_value = find_name_by_reflection(field, detail.old_value)
129 129
130 130 when detail.prop_key == 'estimated_hours'
131 131 value = "%0.02f" % detail.value.to_f unless detail.value.blank?
132 132 old_value = "%0.02f" % detail.old_value.to_f unless detail.old_value.blank?
133 133
134 134 when detail.prop_key == 'parent_id'
135 135 label = l(:field_parent_issue)
136 136 value = "##{detail.value}" unless detail.value.blank?
137 137 old_value = "##{detail.old_value}" unless detail.old_value.blank?
138 138 end
139 139 when 'cf'
140 140 custom_field = CustomField.find_by_id(detail.prop_key)
141 141 if custom_field
142 142 label = custom_field.name
143 143 value = format_value(detail.value, custom_field.field_format) if detail.value
144 144 old_value = format_value(detail.old_value, custom_field.field_format) if detail.old_value
145 145 end
146 146 when 'attachment'
147 147 label = l(:label_attachment)
148 148 end
149 149 call_hook(:helper_issues_show_detail_after_setting, {:detail => detail, :label => label, :value => value, :old_value => old_value })
150 150
151 151 label ||= detail.prop_key
152 152 value ||= detail.value
153 153 old_value ||= detail.old_value
154 154
155 155 unless no_html
156 156 label = content_tag('strong', label)
157 157 old_value = content_tag("i", h(old_value)) if detail.old_value
158 158 old_value = content_tag("strike", old_value) if detail.old_value and (!detail.value or detail.value.empty?)
159 159 if detail.property == 'attachment' && !value.blank? && a = Attachment.find_by_id(detail.prop_key)
160 160 # Link to the attachment if it has not been removed
161 161 value = link_to_attachment(a)
162 162 else
163 163 value = content_tag("i", h(value)) if value
164 164 end
165 165 end
166 166
167 167 if !detail.value.blank?
168 168 case detail.property
169 169 when 'attr', 'cf'
170 170 if !detail.old_value.blank?
171 171 l(:text_journal_changed, :label => label, :old => old_value, :new => value)
172 172 else
173 173 l(:text_journal_set_to, :label => label, :value => value)
174 174 end
175 175 when 'attachment'
176 176 l(:text_journal_added, :label => label, :value => value)
177 177 end
178 178 else
179 179 l(:text_journal_deleted, :label => label, :old => old_value)
180 180 end
181 181 end
182 182
183 183 # Find the name of an associated record stored in the field attribute
184 184 def find_name_by_reflection(field, id)
185 185 association = Issue.reflect_on_association(field.to_sym)
186 186 if association
187 187 record = association.class_name.constantize.find_by_id(id)
188 188 return record.name if record
189 189 end
190 190 end
191 191
192 # Renders issue children recursively
193 def render_api_issue_children(issue, api)
194 return if issue.leaf?
195 api.array :children do
196 issue.children.each do |child|
197 api.issue(:id => child.id) do
198 api.tracker(:id => child.tracker_id, :name => child.tracker.name) unless child.tracker.nil?
199 api.subject child.subject
200 render_api_issue_children(child, api)
201 end
202 end
203 end
204 end
205
192 206 def issues_to_csv(issues, project = nil)
193 207 ic = Iconv.new(l(:general_csv_encoding), 'UTF-8')
194 208 decimal_separator = l(:general_csv_decimal_separator)
195 209 export = FCSV.generate(:col_sep => l(:general_csv_separator)) do |csv|
196 210 # csv header fields
197 211 headers = [ "#",
198 212 l(:field_status),
199 213 l(:field_project),
200 214 l(:field_tracker),
201 215 l(:field_priority),
202 216 l(:field_subject),
203 217 l(:field_assigned_to),
204 218 l(:field_category),
205 219 l(:field_fixed_version),
206 220 l(:field_author),
207 221 l(:field_start_date),
208 222 l(:field_due_date),
209 223 l(:field_done_ratio),
210 224 l(:field_estimated_hours),
211 225 l(:field_parent_issue),
212 226 l(:field_created_on),
213 227 l(:field_updated_on)
214 228 ]
215 229 # Export project custom fields if project is given
216 230 # otherwise export custom fields marked as "For all projects"
217 231 custom_fields = project.nil? ? IssueCustomField.for_all : project.all_issue_custom_fields
218 232 custom_fields.each {|f| headers << f.name}
219 233 # Description in the last column
220 234 headers << l(:field_description)
221 235 csv << headers.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
222 236 # csv lines
223 237 issues.each do |issue|
224 238 fields = [issue.id,
225 239 issue.status.name,
226 240 issue.project.name,
227 241 issue.tracker.name,
228 242 issue.priority.name,
229 243 issue.subject,
230 244 issue.assigned_to,
231 245 issue.category,
232 246 issue.fixed_version,
233 247 issue.author.name,
234 248 format_date(issue.start_date),
235 249 format_date(issue.due_date),
236 250 issue.done_ratio,
237 251 issue.estimated_hours.to_s.gsub('.', decimal_separator),
238 252 issue.parent_id,
239 253 format_time(issue.created_on),
240 254 format_time(issue.updated_on)
241 255 ]
242 256 custom_fields.each {|f| fields << show_value(issue.custom_value_for(f)) }
243 257 fields << issue.description
244 258 csv << fields.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
245 259 end
246 260 end
247 261 export
248 262 end
249 263 end
@@ -1,61 +1,63
1 1 api.issue do
2 2 api.id @issue.id
3 3 api.project(:id => @issue.project_id, :name => @issue.project.name) unless @issue.project.nil?
4 4 api.tracker(:id => @issue.tracker_id, :name => @issue.tracker.name) unless @issue.tracker.nil?
5 5 api.status(:id => @issue.status_id, :name => @issue.status.name) unless @issue.status.nil?
6 6 api.priority(:id => @issue.priority_id, :name => @issue.priority.name) unless @issue.priority.nil?
7 7 api.author(:id => @issue.author_id, :name => @issue.author.name) unless @issue.author.nil?
8 8 api.assigned_to(:id => @issue.assigned_to_id, :name => @issue.assigned_to.name) unless @issue.assigned_to.nil?
9 9 api.category(:id => @issue.category_id, :name => @issue.category.name) unless @issue.category.nil?
10 10 api.fixed_version(:id => @issue.fixed_version_id, :name => @issue.fixed_version.name) unless @issue.fixed_version.nil?
11 11 api.parent(:id => @issue.parent_id) unless @issue.parent.nil?
12 12
13 13 api.subject @issue.subject
14 14 api.description @issue.description
15 15 api.start_date @issue.start_date
16 16 api.due_date @issue.due_date
17 17 api.done_ratio @issue.done_ratio
18 18 api.estimated_hours @issue.estimated_hours
19 19 if User.current.allowed_to?(:view_time_entries, @project)
20 20 api.spent_hours @issue.spent_hours
21 21 end
22 22
23 23 api.array :custom_fields do
24 24 @issue.custom_field_values.each do |custom_value|
25 25 api.custom_field custom_value.value, :id => custom_value.custom_field_id, :name => custom_value.custom_field.name
26 26 end
27 27 end unless @issue.custom_field_values.empty?
28 28
29 29 api.created_on @issue.created_on
30 30 api.updated_on @issue.updated_on
31 31
32 render_api_issue_children(@issue, api)
33
32 34 api.array :relations do
33 35 @issue.relations.select {|r| r.other_issue(@issue).visible? }.each do |relation|
34 36 api.relation(:id => relation.id, :issue_id => relation.other_issue(@issue).id, :relation_type => relation.relation_type_for(@issue), :delay => relation.delay)
35 37 end
36 38 end
37 39
38 40 api.array :changesets do
39 41 @issue.changesets.each do |changeset|
40 42 api.changeset :revision => changeset.revision do
41 43 api.user(:id => changeset.user_id, :name => changeset.user.name) unless changeset.user.nil?
42 44 api.comments changeset.comments
43 45 api.committed_on changeset.committed_on
44 46 end
45 47 end
46 48 end if User.current.allowed_to?(:view_changesets, @project) && @issue.changesets.any?
47 49
48 50 api.array :journals do
49 51 @issue.journals.each do |journal|
50 52 api.journal :id => journal.id do
51 53 api.user(:id => journal.user_id, :name => journal.user.name) unless journal.user.nil?
52 54 api.notes journal.notes
53 55 api.array :details do
54 56 journal.details.each do |detail|
55 57 api.detail :property => detail.property, :name => detail.prop_key, :old => detail.old_value, :new => detail.value
56 58 end
57 59 end
58 60 end
59 61 end
60 62 end unless @issue.journals.empty?
61 63 end
@@ -1,340 +1,394
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2010 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require "#{File.dirname(__FILE__)}/../../test_helper"
19 19
20 20 class ApiTest::IssuesTest < ActionController::IntegrationTest
21 21 fixtures :projects,
22 22 :users,
23 23 :roles,
24 24 :members,
25 25 :member_roles,
26 26 :issues,
27 27 :issue_statuses,
28 28 :versions,
29 29 :trackers,
30 30 :projects_trackers,
31 31 :issue_categories,
32 32 :enabled_modules,
33 33 :enumerations,
34 34 :attachments,
35 35 :workflows,
36 36 :custom_fields,
37 37 :custom_values,
38 38 :custom_fields_projects,
39 39 :custom_fields_trackers,
40 40 :time_entries,
41 41 :journals,
42 42 :journal_details,
43 43 :queries
44 44
45 45 def setup
46 46 Setting.rest_api_enabled = '1'
47 47 end
48 48
49 49 # Use a private project to make sure auth is really working and not just
50 50 # only showing public issues.
51 51 context "/index.xml" do
52 52 should_allow_api_authentication(:get, "/projects/private-child/issues.xml")
53 53 end
54 54
55 55 context "/index.json" do
56 56 should_allow_api_authentication(:get, "/projects/private-child/issues.json")
57 57 end
58 58
59 59 context "/index.xml with filter" do
60 60 should_allow_api_authentication(:get, "/projects/private-child/issues.xml?status_id=5")
61 61
62 62 should "show only issues with the status_id" do
63 63 get '/issues.xml?status_id=5'
64 64 assert_tag :tag => 'issues',
65 65 :children => { :count => Issue.visible.count(:conditions => {:status_id => 5}),
66 66 :only => { :tag => 'issue' } }
67 67 end
68 68 end
69 69
70 70 context "/index.json with filter" do
71 71 should_allow_api_authentication(:get, "/projects/private-child/issues.json?status_id=5")
72 72
73 73 should "show only issues with the status_id" do
74 74 get '/issues.json?status_id=5'
75 75
76 76 json = ActiveSupport::JSON.decode(response.body)
77 77 status_ids_used = json['issues'].collect {|j| j['status']['id'] }
78 78 assert_equal 3, status_ids_used.length
79 79 assert status_ids_used.all? {|id| id == 5 }
80 80 end
81 81
82 82 end
83 83
84 84 # Issue 6 is on a private project
85 85 context "/issues/6.xml" do
86 86 should_allow_api_authentication(:get, "/issues/6.xml")
87 87 end
88 88
89 89 context "/issues/6.json" do
90 90 should_allow_api_authentication(:get, "/issues/6.json")
91 91 end
92
93 context "GET /issues/:id" do
94 context "with subtasks" do
95 setup do
96 @c1 = Issue.generate!(:status_id => 1, :subject => "child c1", :tracker_id => 1, :project_id => 1, :parent_issue_id => 1)
97 @c2 = Issue.generate!(:status_id => 1, :subject => "child c2", :tracker_id => 1, :project_id => 1, :parent_issue_id => 1)
98 @c3 = Issue.generate!(:status_id => 1, :subject => "child c3", :tracker_id => 1, :project_id => 1, :parent_issue_id => @c1.id)
99 end
100
101 context ".xml" do
102 should "display children" do
103 get '/issues/1.xml'
104
105 assert_tag :tag => 'issue',
106 :child => {
107 :tag => 'children',
108 :children => {:count => 2},
109 :child => {
110 :tag => 'issue',
111 :attributes => {:id => @c1.id.to_s},
112 :child => {
113 :tag => 'subject',
114 :content => 'child c1',
115 :sibling => {
116 :tag => 'children',
117 :children => {:count => 1},
118 :child => {
119 :tag => 'issue',
120 :attributes => {:id => @c3.id.to_s}
121 }
122 }
123 }
124 }
125 }
126 end
127
128 context ".json" do
129 should "display children" do
130 get '/issues/1.json'
131
132 json = ActiveSupport::JSON.decode(response.body)
133 assert_equal([
134 {
135 'id' => @c1.id, 'subject' => 'child c1', 'tracker' => {'id' => 1, 'name' => 'Bug'},
136 'children' => [{ 'id' => @c3.id, 'subject' => 'child c3', 'tracker' => {'id' => 1, 'name' => 'Bug'} }]
137 },
138 { 'id' => @c2.id, 'subject' => 'child c2', 'tracker' => {'id' => 1, 'name' => 'Bug'} }
139 ],
140 json['issue']['children'])
141 end
142 end
143 end
144 end
145 end
92 146
93 147 context "POST /issues.xml" do
94 148 should_allow_api_authentication(:post,
95 149 '/issues.xml',
96 150 {:issue => {:project_id => 1, :subject => 'API test', :tracker_id => 2, :status_id => 3}},
97 151 {:success_code => :created})
98 152
99 153 should "create an issue with the attributes" do
100 154 assert_difference('Issue.count') do
101 155 post '/issues.xml', {:issue => {:project_id => 1, :subject => 'API test', :tracker_id => 2, :status_id => 3}}, :authorization => credentials('jsmith')
102 156 end
103 157
104 158 issue = Issue.first(:order => 'id DESC')
105 159 assert_equal 1, issue.project_id
106 160 assert_equal 2, issue.tracker_id
107 161 assert_equal 3, issue.status_id
108 162 assert_equal 'API test', issue.subject
109 163
110 164 assert_response :created
111 165 assert_equal 'application/xml', @response.content_type
112 166 assert_tag 'issue', :child => {:tag => 'id', :content => issue.id.to_s}
113 167 end
114 168 end
115 169
116 170 context "POST /issues.xml with failure" do
117 171 should_allow_api_authentication(:post,
118 172 '/issues.xml',
119 173 {:issue => {:project_id => 1}},
120 174 {:success_code => :unprocessable_entity})
121 175
122 176 should "have an errors tag" do
123 177 assert_no_difference('Issue.count') do
124 178 post '/issues.xml', {:issue => {:project_id => 1}}, :authorization => credentials('jsmith')
125 179 end
126 180
127 181 assert_tag :errors, :child => {:tag => 'error', :content => "Subject can't be blank"}
128 182 end
129 183 end
130 184
131 185 context "POST /issues.json" do
132 186 should_allow_api_authentication(:post,
133 187 '/issues.json',
134 188 {:issue => {:project_id => 1, :subject => 'API test', :tracker_id => 2, :status_id => 3}},
135 189 {:success_code => :created})
136 190
137 191 should "create an issue with the attributes" do
138 192 assert_difference('Issue.count') do
139 193 post '/issues.json', {:issue => {:project_id => 1, :subject => 'API test', :tracker_id => 2, :status_id => 3}}, :authorization => credentials('jsmith')
140 194 end
141 195
142 196 issue = Issue.first(:order => 'id DESC')
143 197 assert_equal 1, issue.project_id
144 198 assert_equal 2, issue.tracker_id
145 199 assert_equal 3, issue.status_id
146 200 assert_equal 'API test', issue.subject
147 201 end
148 202
149 203 end
150 204
151 205 context "POST /issues.json with failure" do
152 206 should_allow_api_authentication(:post,
153 207 '/issues.json',
154 208 {:issue => {:project_id => 1}},
155 209 {:success_code => :unprocessable_entity})
156 210
157 211 should "have an errors element" do
158 212 assert_no_difference('Issue.count') do
159 213 post '/issues.json', {:issue => {:project_id => 1}}, :authorization => credentials('jsmith')
160 214 end
161 215
162 216 json = ActiveSupport::JSON.decode(response.body)
163 217 assert json['errors'].include?(['subject', "can't be blank"])
164 218 end
165 219 end
166 220
167 221 # Issue 6 is on a private project
168 222 context "PUT /issues/6.xml" do
169 223 setup do
170 224 @parameters = {:issue => {:subject => 'API update', :notes => 'A new note'}}
171 225 @headers = { :authorization => credentials('jsmith') }
172 226 end
173 227
174 228 should_allow_api_authentication(:put,
175 229 '/issues/6.xml',
176 230 {:issue => {:subject => 'API update', :notes => 'A new note'}},
177 231 {:success_code => :ok})
178 232
179 233 should "not create a new issue" do
180 234 assert_no_difference('Issue.count') do
181 235 put '/issues/6.xml', @parameters, @headers
182 236 end
183 237 end
184 238
185 239 should "create a new journal" do
186 240 assert_difference('Journal.count') do
187 241 put '/issues/6.xml', @parameters, @headers
188 242 end
189 243 end
190 244
191 245 should "add the note to the journal" do
192 246 put '/issues/6.xml', @parameters, @headers
193 247
194 248 journal = Journal.last
195 249 assert_equal "A new note", journal.notes
196 250 end
197 251
198 252 should "update the issue" do
199 253 put '/issues/6.xml', @parameters, @headers
200 254
201 255 issue = Issue.find(6)
202 256 assert_equal "API update", issue.subject
203 257 end
204 258
205 259 end
206 260
207 261 context "PUT /issues/6.xml with failed update" do
208 262 setup do
209 263 @parameters = {:issue => {:subject => ''}}
210 264 @headers = { :authorization => credentials('jsmith') }
211 265 end
212 266
213 267 should_allow_api_authentication(:put,
214 268 '/issues/6.xml',
215 269 {:issue => {:subject => ''}}, # Missing subject should fail
216 270 {:success_code => :unprocessable_entity})
217 271
218 272 should "not create a new issue" do
219 273 assert_no_difference('Issue.count') do
220 274 put '/issues/6.xml', @parameters, @headers
221 275 end
222 276 end
223 277
224 278 should "not create a new journal" do
225 279 assert_no_difference('Journal.count') do
226 280 put '/issues/6.xml', @parameters, @headers
227 281 end
228 282 end
229 283
230 284 should "have an errors tag" do
231 285 put '/issues/6.xml', @parameters, @headers
232 286
233 287 assert_tag :errors, :child => {:tag => 'error', :content => "Subject can't be blank"}
234 288 end
235 289 end
236 290
237 291 context "PUT /issues/6.json" do
238 292 setup do
239 293 @parameters = {:issue => {:subject => 'API update', :notes => 'A new note'}}
240 294 @headers = { :authorization => credentials('jsmith') }
241 295 end
242 296
243 297 should_allow_api_authentication(:put,
244 298 '/issues/6.json',
245 299 {:issue => {:subject => 'API update', :notes => 'A new note'}},
246 300 {:success_code => :ok})
247 301
248 302 should "not create a new issue" do
249 303 assert_no_difference('Issue.count') do
250 304 put '/issues/6.json', @parameters, @headers
251 305 end
252 306 end
253 307
254 308 should "create a new journal" do
255 309 assert_difference('Journal.count') do
256 310 put '/issues/6.json', @parameters, @headers
257 311 end
258 312 end
259 313
260 314 should "add the note to the journal" do
261 315 put '/issues/6.json', @parameters, @headers
262 316
263 317 journal = Journal.last
264 318 assert_equal "A new note", journal.notes
265 319 end
266 320
267 321 should "update the issue" do
268 322 put '/issues/6.json', @parameters, @headers
269 323
270 324 issue = Issue.find(6)
271 325 assert_equal "API update", issue.subject
272 326 end
273 327
274 328 end
275 329
276 330 context "PUT /issues/6.json with failed update" do
277 331 setup do
278 332 @parameters = {:issue => {:subject => ''}}
279 333 @headers = { :authorization => credentials('jsmith') }
280 334 end
281 335
282 336 should_allow_api_authentication(:put,
283 337 '/issues/6.json',
284 338 {:issue => {:subject => ''}}, # Missing subject should fail
285 339 {:success_code => :unprocessable_entity})
286 340
287 341 should "not create a new issue" do
288 342 assert_no_difference('Issue.count') do
289 343 put '/issues/6.json', @parameters, @headers
290 344 end
291 345 end
292 346
293 347 should "not create a new journal" do
294 348 assert_no_difference('Journal.count') do
295 349 put '/issues/6.json', @parameters, @headers
296 350 end
297 351 end
298 352
299 353 should "have an errors attribute" do
300 354 put '/issues/6.json', @parameters, @headers
301 355
302 356 json = ActiveSupport::JSON.decode(response.body)
303 357 assert json['errors'].include?(['subject', "can't be blank"])
304 358 end
305 359 end
306 360
307 361 context "DELETE /issues/1.xml" do
308 362 should_allow_api_authentication(:delete,
309 363 '/issues/6.xml',
310 364 {},
311 365 {:success_code => :ok})
312 366
313 367 should "delete the issue" do
314 368 assert_difference('Issue.count',-1) do
315 369 delete '/issues/6.xml', {}, :authorization => credentials('jsmith')
316 370 end
317 371
318 372 assert_nil Issue.find_by_id(6)
319 373 end
320 374 end
321 375
322 376 context "DELETE /issues/1.json" do
323 377 should_allow_api_authentication(:delete,
324 378 '/issues/6.json',
325 379 {},
326 380 {:success_code => :ok})
327 381
328 382 should "delete the issue" do
329 383 assert_difference('Issue.count',-1) do
330 384 delete '/issues/6.json', {}, :authorization => credentials('jsmith')
331 385 end
332 386
333 387 assert_nil Issue.find_by_id(6)
334 388 end
335 389 end
336 390
337 391 def credentials(user, password=nil)
338 392 ActionController::HttpAuthentication::Basic.encode_credentials(user, password || user)
339 393 end
340 394 end
General Comments 0
You need to be logged in to leave comments. Login now