##// END OF EJS Templates
Refactor: Convert the tests for Issues#index and #show APIs to shoulda. #6447...
Eric Davis -
r4250:c967899b1456
parent child
Show More
@@ -1,349 +1,314
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2010 Jean-Philippe Lang
2 # Copyright (C) 2006-2010 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.dirname(__FILE__)}/../../test_helper"
18 require "#{File.dirname(__FILE__)}/../../test_helper"
19
19
20 class ApiTest::IssuesTest < ActionController::IntegrationTest
20 class ApiTest::IssuesTest < ActionController::IntegrationTest
21 fixtures :projects,
21 fixtures :projects,
22 :users,
22 :users,
23 :roles,
23 :roles,
24 :members,
24 :members,
25 :member_roles,
25 :member_roles,
26 :issues,
26 :issues,
27 :issue_statuses,
27 :issue_statuses,
28 :versions,
28 :versions,
29 :trackers,
29 :trackers,
30 :projects_trackers,
30 :projects_trackers,
31 :issue_categories,
31 :issue_categories,
32 :enabled_modules,
32 :enabled_modules,
33 :enumerations,
33 :enumerations,
34 :attachments,
34 :attachments,
35 :workflows,
35 :workflows,
36 :custom_fields,
36 :custom_fields,
37 :custom_values,
37 :custom_values,
38 :custom_fields_projects,
38 :custom_fields_projects,
39 :custom_fields_trackers,
39 :custom_fields_trackers,
40 :time_entries,
40 :time_entries,
41 :journals,
41 :journals,
42 :journal_details,
42 :journal_details,
43 :queries
43 :queries
44
44
45 def setup
45 def setup
46 Setting.rest_api_enabled = '1'
46 Setting.rest_api_enabled = '1'
47 end
47 end
48
48
49 # Use a private project to make sure auth is really working and not just
50 # only showing public issues.
49 context "/index.xml" do
51 context "/index.xml" do
50 setup do
52 should_allow_api_authentication(:get, "/projects/private-child/issues.xml")
51 get '/issues.xml'
52 end
53
54 should_respond_with :success
55 should_respond_with_content_type 'application/xml'
56 end
53 end
57
54
58 context "/index.json" do
55 context "/index.json" do
59 setup do
56 should_allow_api_authentication(:get, "/projects/private-child/issues.json")
60 get '/issues.json'
61 end
62
63 should_respond_with :success
64 should_respond_with_content_type 'application/json'
65
66 should 'return a valid JSON string' do
67 assert ActiveSupport::JSON.decode(response.body)
68 end
69 end
57 end
70
58
71 context "/index.xml with filter" do
59 context "/index.xml with filter" do
72 setup do
60 should_allow_api_authentication(:get, "/projects/private-child/issues.xml?status_id=5")
73 get '/issues.xml?status_id=5'
74 end
75
61
76 should_respond_with :success
77 should_respond_with_content_type 'application/xml'
78 should "show only issues with the status_id" do
62 should "show only issues with the status_id" do
63 get '/issues.xml?status_id=5'
79 assert_tag :tag => 'issues',
64 assert_tag :tag => 'issues',
80 :children => { :count => Issue.visible.count(:conditions => {:status_id => 5}),
65 :children => { :count => Issue.visible.count(:conditions => {:status_id => 5}),
81 :only => { :tag => 'issue' } }
66 :only => { :tag => 'issue' } }
82 end
67 end
83 end
68 end
84
69
85 context "/index.json with filter" do
70 context "/index.json with filter" do
86 setup do
71 should_allow_api_authentication(:get, "/projects/private-child/issues.json?status_id=5")
87 get '/issues.json?status_id=5'
88 end
89
90 should_respond_with :success
91 should_respond_with_content_type 'application/json'
92
93 should 'return a valid JSON string' do
94 assert ActiveSupport::JSON.decode(response.body)
95 end
96
72
97 should "show only issues with the status_id" do
73 should "show only issues with the status_id" do
74 get '/issues.json?status_id=5'
75
98 json = ActiveSupport::JSON.decode(response.body)
76 json = ActiveSupport::JSON.decode(response.body)
99 status_ids_used = json.collect {|j| j['status_id'] }
77 status_ids_used = json.collect {|j| j['status_id'] }
100 assert_equal 3, status_ids_used.length
78 assert_equal 3, status_ids_used.length
101 assert status_ids_used.all? {|id| id == 5 }
79 assert status_ids_used.all? {|id| id == 5 }
102 end
80 end
103
81
104 end
82 end
105
83
106 context "/issues/1.xml" do
84 # Issue 6 is on a private project
107 setup do
85 context "/issues/6.xml" do
108 get '/issues/1.xml'
86 should_allow_api_authentication(:get, "/issues/6.xml")
109 end
87 end
110
88
111 should_respond_with :success
89 context "/issues/6.json" do
112 should_respond_with_content_type 'application/xml'
90 should_allow_api_authentication(:get, "/issues/6.json")
113 end
114
115 context "/issues/1.json" do
116 setup do
117 get '/issues/1.json'
118 end
119
120 should_respond_with :success
121 should_respond_with_content_type 'application/json'
122
123 should 'return a valid JSON string' do
124 assert ActiveSupport::JSON.decode(response.body)
125 end
126 end
91 end
127
92
128 context "POST /issues.xml" do
93 context "POST /issues.xml" do
129 setup do
94 setup do
130 @issue_count = Issue.count
95 @issue_count = Issue.count
131 @attributes = {:project_id => 1, :subject => 'API test', :tracker_id => 2, :status_id => 3}
96 @attributes = {:project_id => 1, :subject => 'API test', :tracker_id => 2, :status_id => 3}
132 post '/issues.xml', {:issue => @attributes}, :authorization => credentials('jsmith')
97 post '/issues.xml', {:issue => @attributes}, :authorization => credentials('jsmith')
133 end
98 end
134
99
135 should_respond_with :created
100 should_respond_with :created
136 should_respond_with_content_type 'application/xml'
101 should_respond_with_content_type 'application/xml'
137
102
138 should "create an issue with the attributes" do
103 should "create an issue with the attributes" do
139 assert_equal Issue.count, @issue_count + 1
104 assert_equal Issue.count, @issue_count + 1
140
105
141 issue = Issue.first(:order => 'id DESC')
106 issue = Issue.first(:order => 'id DESC')
142 @attributes.each do |attribute, value|
107 @attributes.each do |attribute, value|
143 assert_equal value, issue.send(attribute)
108 assert_equal value, issue.send(attribute)
144 end
109 end
145 end
110 end
146 end
111 end
147
112
148 context "POST /issues.xml with failure" do
113 context "POST /issues.xml with failure" do
149 setup do
114 setup do
150 @attributes = {:project_id => 1}
115 @attributes = {:project_id => 1}
151 post '/issues.xml', {:issue => @attributes}, :authorization => credentials('jsmith')
116 post '/issues.xml', {:issue => @attributes}, :authorization => credentials('jsmith')
152 end
117 end
153
118
154 should_respond_with :unprocessable_entity
119 should_respond_with :unprocessable_entity
155 should_respond_with_content_type 'application/xml'
120 should_respond_with_content_type 'application/xml'
156
121
157 should "have an errors tag" do
122 should "have an errors tag" do
158 assert_tag :errors, :child => {:tag => 'error', :content => "Subject can't be blank"}
123 assert_tag :errors, :child => {:tag => 'error', :content => "Subject can't be blank"}
159 end
124 end
160 end
125 end
161
126
162 context "POST /issues.json" do
127 context "POST /issues.json" do
163 setup do
128 setup do
164 @issue_count = Issue.count
129 @issue_count = Issue.count
165 @attributes = {:project_id => 1, :subject => 'API test', :tracker_id => 2, :status_id => 3}
130 @attributes = {:project_id => 1, :subject => 'API test', :tracker_id => 2, :status_id => 3}
166 post '/issues.json', {:issue => @attributes}, :authorization => credentials('jsmith')
131 post '/issues.json', {:issue => @attributes}, :authorization => credentials('jsmith')
167 end
132 end
168
133
169 should_respond_with :created
134 should_respond_with :created
170 should_respond_with_content_type 'application/json'
135 should_respond_with_content_type 'application/json'
171
136
172 should "create an issue with the attributes" do
137 should "create an issue with the attributes" do
173 assert_equal Issue.count, @issue_count + 1
138 assert_equal Issue.count, @issue_count + 1
174
139
175 issue = Issue.first(:order => 'id DESC')
140 issue = Issue.first(:order => 'id DESC')
176 @attributes.each do |attribute, value|
141 @attributes.each do |attribute, value|
177 assert_equal value, issue.send(attribute)
142 assert_equal value, issue.send(attribute)
178 end
143 end
179 end
144 end
180 end
145 end
181
146
182 context "POST /issues.json with failure" do
147 context "POST /issues.json with failure" do
183 setup do
148 setup do
184 @attributes = {:project_id => 1}
149 @attributes = {:project_id => 1}
185 post '/issues.json', {:issue => @attributes}, :authorization => credentials('jsmith')
150 post '/issues.json', {:issue => @attributes}, :authorization => credentials('jsmith')
186 end
151 end
187
152
188 should_respond_with :unprocessable_entity
153 should_respond_with :unprocessable_entity
189 should_respond_with_content_type 'application/json'
154 should_respond_with_content_type 'application/json'
190
155
191 should "have an errors element" do
156 should "have an errors element" do
192 json = ActiveSupport::JSON.decode(response.body)
157 json = ActiveSupport::JSON.decode(response.body)
193 assert_equal "can't be blank", json.first['subject']
158 assert_equal "can't be blank", json.first['subject']
194 end
159 end
195 end
160 end
196
161
197 context "PUT /issues/1.xml" do
162 context "PUT /issues/1.xml" do
198 setup do
163 setup do
199 @issue_count = Issue.count
164 @issue_count = Issue.count
200 @journal_count = Journal.count
165 @journal_count = Journal.count
201 @attributes = {:subject => 'API update', :notes => 'A new note'}
166 @attributes = {:subject => 'API update', :notes => 'A new note'}
202
167
203 put '/issues/1.xml', {:issue => @attributes}, :authorization => credentials('jsmith')
168 put '/issues/1.xml', {:issue => @attributes}, :authorization => credentials('jsmith')
204 end
169 end
205
170
206 should_respond_with :ok
171 should_respond_with :ok
207 should_respond_with_content_type 'application/xml'
172 should_respond_with_content_type 'application/xml'
208
173
209 should "not create a new issue" do
174 should "not create a new issue" do
210 assert_equal Issue.count, @issue_count
175 assert_equal Issue.count, @issue_count
211 end
176 end
212
177
213 should "create a new journal" do
178 should "create a new journal" do
214 assert_equal Journal.count, @journal_count + 1
179 assert_equal Journal.count, @journal_count + 1
215 end
180 end
216
181
217 should "add the note to the journal" do
182 should "add the note to the journal" do
218 journal = Journal.last
183 journal = Journal.last
219 assert_equal "A new note", journal.notes
184 assert_equal "A new note", journal.notes
220 end
185 end
221
186
222 should "update the issue" do
187 should "update the issue" do
223 issue = Issue.find(1)
188 issue = Issue.find(1)
224 @attributes.each do |attribute, value|
189 @attributes.each do |attribute, value|
225 assert_equal value, issue.send(attribute) unless attribute == :notes
190 assert_equal value, issue.send(attribute) unless attribute == :notes
226 end
191 end
227 end
192 end
228
193
229 end
194 end
230
195
231 context "PUT /issues/1.xml with failed update" do
196 context "PUT /issues/1.xml with failed update" do
232 setup do
197 setup do
233 @attributes = {:subject => ''}
198 @attributes = {:subject => ''}
234 @issue_count = Issue.count
199 @issue_count = Issue.count
235 @journal_count = Journal.count
200 @journal_count = Journal.count
236
201
237 put '/issues/1.xml', {:issue => @attributes}, :authorization => credentials('jsmith')
202 put '/issues/1.xml', {:issue => @attributes}, :authorization => credentials('jsmith')
238 end
203 end
239
204
240 should_respond_with :unprocessable_entity
205 should_respond_with :unprocessable_entity
241 should_respond_with_content_type 'application/xml'
206 should_respond_with_content_type 'application/xml'
242
207
243 should "not create a new issue" do
208 should "not create a new issue" do
244 assert_equal Issue.count, @issue_count
209 assert_equal Issue.count, @issue_count
245 end
210 end
246
211
247 should "not create a new journal" do
212 should "not create a new journal" do
248 assert_equal Journal.count, @journal_count
213 assert_equal Journal.count, @journal_count
249 end
214 end
250
215
251 should "have an errors tag" do
216 should "have an errors tag" do
252 assert_tag :errors, :child => {:tag => 'error', :content => "Subject can't be blank"}
217 assert_tag :errors, :child => {:tag => 'error', :content => "Subject can't be blank"}
253 end
218 end
254 end
219 end
255
220
256 context "PUT /issues/1.json" do
221 context "PUT /issues/1.json" do
257 setup do
222 setup do
258 @issue_count = Issue.count
223 @issue_count = Issue.count
259 @journal_count = Journal.count
224 @journal_count = Journal.count
260 @attributes = {:subject => 'API update', :notes => 'A new note'}
225 @attributes = {:subject => 'API update', :notes => 'A new note'}
261
226
262 put '/issues/1.json', {:issue => @attributes}, :authorization => credentials('jsmith')
227 put '/issues/1.json', {:issue => @attributes}, :authorization => credentials('jsmith')
263 end
228 end
264
229
265 should_respond_with :ok
230 should_respond_with :ok
266 should_respond_with_content_type 'application/json'
231 should_respond_with_content_type 'application/json'
267
232
268 should "not create a new issue" do
233 should "not create a new issue" do
269 assert_equal Issue.count, @issue_count
234 assert_equal Issue.count, @issue_count
270 end
235 end
271
236
272 should "create a new journal" do
237 should "create a new journal" do
273 assert_equal Journal.count, @journal_count + 1
238 assert_equal Journal.count, @journal_count + 1
274 end
239 end
275
240
276 should "add the note to the journal" do
241 should "add the note to the journal" do
277 journal = Journal.last
242 journal = Journal.last
278 assert_equal "A new note", journal.notes
243 assert_equal "A new note", journal.notes
279 end
244 end
280
245
281 should "update the issue" do
246 should "update the issue" do
282 issue = Issue.find(1)
247 issue = Issue.find(1)
283 @attributes.each do |attribute, value|
248 @attributes.each do |attribute, value|
284 assert_equal value, issue.send(attribute) unless attribute == :notes
249 assert_equal value, issue.send(attribute) unless attribute == :notes
285 end
250 end
286 end
251 end
287
252
288 end
253 end
289
254
290 context "PUT /issues/1.json with failed update" do
255 context "PUT /issues/1.json with failed update" do
291 setup do
256 setup do
292 @attributes = {:subject => ''}
257 @attributes = {:subject => ''}
293 @issue_count = Issue.count
258 @issue_count = Issue.count
294 @journal_count = Journal.count
259 @journal_count = Journal.count
295
260
296 put '/issues/1.json', {:issue => @attributes}, :authorization => credentials('jsmith')
261 put '/issues/1.json', {:issue => @attributes}, :authorization => credentials('jsmith')
297 end
262 end
298
263
299 should_respond_with :unprocessable_entity
264 should_respond_with :unprocessable_entity
300 should_respond_with_content_type 'application/json'
265 should_respond_with_content_type 'application/json'
301
266
302 should "not create a new issue" do
267 should "not create a new issue" do
303 assert_equal Issue.count, @issue_count
268 assert_equal Issue.count, @issue_count
304 end
269 end
305
270
306 should "not create a new journal" do
271 should "not create a new journal" do
307 assert_equal Journal.count, @journal_count
272 assert_equal Journal.count, @journal_count
308 end
273 end
309
274
310 should "have an errors attribute" do
275 should "have an errors attribute" do
311 json = ActiveSupport::JSON.decode(response.body)
276 json = ActiveSupport::JSON.decode(response.body)
312 assert_equal "can't be blank", json.first['subject']
277 assert_equal "can't be blank", json.first['subject']
313 end
278 end
314 end
279 end
315
280
316 context "DELETE /issues/1.xml" do
281 context "DELETE /issues/1.xml" do
317 setup do
282 setup do
318 @issue_count = Issue.count
283 @issue_count = Issue.count
319 delete '/issues/1.xml', {}, :authorization => credentials('jsmith')
284 delete '/issues/1.xml', {}, :authorization => credentials('jsmith')
320 end
285 end
321
286
322 should_respond_with :ok
287 should_respond_with :ok
323 should_respond_with_content_type 'application/xml'
288 should_respond_with_content_type 'application/xml'
324
289
325 should "delete the issue" do
290 should "delete the issue" do
326 assert_equal Issue.count, @issue_count -1
291 assert_equal Issue.count, @issue_count -1
327 assert_nil Issue.find_by_id(1)
292 assert_nil Issue.find_by_id(1)
328 end
293 end
329 end
294 end
330
295
331 context "DELETE /issues/1.json" do
296 context "DELETE /issues/1.json" do
332 setup do
297 setup do
333 @issue_count = Issue.count
298 @issue_count = Issue.count
334 delete '/issues/1.json', {}, :authorization => credentials('jsmith')
299 delete '/issues/1.json', {}, :authorization => credentials('jsmith')
335 end
300 end
336
301
337 should_respond_with :ok
302 should_respond_with :ok
338 should_respond_with_content_type 'application/json'
303 should_respond_with_content_type 'application/json'
339
304
340 should "delete the issue" do
305 should "delete the issue" do
341 assert_equal Issue.count, @issue_count -1
306 assert_equal Issue.count, @issue_count -1
342 assert_nil Issue.find_by_id(1)
307 assert_nil Issue.find_by_id(1)
343 end
308 end
344 end
309 end
345
310
346 def credentials(user, password=nil)
311 def credentials(user, password=nil)
347 ActionController::HttpAuthentication::Basic.encode_credentials(user, password || user)
312 ActionController::HttpAuthentication::Basic.encode_credentials(user, password || user)
348 end
313 end
349 end
314 end
@@ -1,336 +1,393
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006 Jean-Philippe Lang
2 # Copyright (C) 2006 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 ENV["RAILS_ENV"] = "test"
18 ENV["RAILS_ENV"] = "test"
19 require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
19 require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
20 require 'test_help'
20 require 'test_help'
21 require File.expand_path(File.dirname(__FILE__) + '/helper_testcase')
21 require File.expand_path(File.dirname(__FILE__) + '/helper_testcase')
22 require File.join(RAILS_ROOT,'test', 'mocks', 'open_id_authentication_mock.rb')
22 require File.join(RAILS_ROOT,'test', 'mocks', 'open_id_authentication_mock.rb')
23
23
24 require File.expand_path(File.dirname(__FILE__) + '/object_daddy_helpers')
24 require File.expand_path(File.dirname(__FILE__) + '/object_daddy_helpers')
25 include ObjectDaddyHelpers
25 include ObjectDaddyHelpers
26
26
27 class ActiveSupport::TestCase
27 class ActiveSupport::TestCase
28 # Transactional fixtures accelerate your tests by wrapping each test method
28 # Transactional fixtures accelerate your tests by wrapping each test method
29 # in a transaction that's rolled back on completion. This ensures that the
29 # in a transaction that's rolled back on completion. This ensures that the
30 # test database remains unchanged so your fixtures don't have to be reloaded
30 # test database remains unchanged so your fixtures don't have to be reloaded
31 # between every test method. Fewer database queries means faster tests.
31 # between every test method. Fewer database queries means faster tests.
32 #
32 #
33 # Read Mike Clark's excellent walkthrough at
33 # Read Mike Clark's excellent walkthrough at
34 # http://clarkware.com/cgi/blosxom/2005/10/24#Rails10FastTesting
34 # http://clarkware.com/cgi/blosxom/2005/10/24#Rails10FastTesting
35 #
35 #
36 # Every Active Record database supports transactions except MyISAM tables
36 # Every Active Record database supports transactions except MyISAM tables
37 # in MySQL. Turn off transactional fixtures in this case; however, if you
37 # in MySQL. Turn off transactional fixtures in this case; however, if you
38 # don't care one way or the other, switching from MyISAM to InnoDB tables
38 # don't care one way or the other, switching from MyISAM to InnoDB tables
39 # is recommended.
39 # is recommended.
40 self.use_transactional_fixtures = true
40 self.use_transactional_fixtures = true
41
41
42 # Instantiated fixtures are slow, but give you @david where otherwise you
42 # Instantiated fixtures are slow, but give you @david where otherwise you
43 # would need people(:david). If you don't want to migrate your existing
43 # would need people(:david). If you don't want to migrate your existing
44 # test cases which use the @david style and don't mind the speed hit (each
44 # test cases which use the @david style and don't mind the speed hit (each
45 # instantiated fixtures translates to a database query per test method),
45 # instantiated fixtures translates to a database query per test method),
46 # then set this back to true.
46 # then set this back to true.
47 self.use_instantiated_fixtures = false
47 self.use_instantiated_fixtures = false
48
48
49 # Add more helper methods to be used by all tests here...
49 # Add more helper methods to be used by all tests here...
50
50
51 def log_user(login, password)
51 def log_user(login, password)
52 User.anonymous
52 User.anonymous
53 get "/login"
53 get "/login"
54 assert_equal nil, session[:user_id]
54 assert_equal nil, session[:user_id]
55 assert_response :success
55 assert_response :success
56 assert_template "account/login"
56 assert_template "account/login"
57 post "/login", :username => login, :password => password
57 post "/login", :username => login, :password => password
58 assert_equal login, User.find(session[:user_id]).login
58 assert_equal login, User.find(session[:user_id]).login
59 end
59 end
60
60
61 def uploaded_test_file(name, mime)
61 def uploaded_test_file(name, mime)
62 ActionController::TestUploadedFile.new(ActiveSupport::TestCase.fixture_path + "/files/#{name}", mime)
62 ActionController::TestUploadedFile.new(ActiveSupport::TestCase.fixture_path + "/files/#{name}", mime)
63 end
63 end
64
64
65 # Mock out a file
65 # Mock out a file
66 def self.mock_file
66 def self.mock_file
67 file = 'a_file.png'
67 file = 'a_file.png'
68 file.stubs(:size).returns(32)
68 file.stubs(:size).returns(32)
69 file.stubs(:original_filename).returns('a_file.png')
69 file.stubs(:original_filename).returns('a_file.png')
70 file.stubs(:content_type).returns('image/png')
70 file.stubs(:content_type).returns('image/png')
71 file.stubs(:read).returns(false)
71 file.stubs(:read).returns(false)
72 file
72 file
73 end
73 end
74
74
75 def mock_file
75 def mock_file
76 self.class.mock_file
76 self.class.mock_file
77 end
77 end
78
78
79 # Use a temporary directory for attachment related tests
79 # Use a temporary directory for attachment related tests
80 def set_tmp_attachments_directory
80 def set_tmp_attachments_directory
81 Dir.mkdir "#{RAILS_ROOT}/tmp/test" unless File.directory?("#{RAILS_ROOT}/tmp/test")
81 Dir.mkdir "#{RAILS_ROOT}/tmp/test" unless File.directory?("#{RAILS_ROOT}/tmp/test")
82 Dir.mkdir "#{RAILS_ROOT}/tmp/test/attachments" unless File.directory?("#{RAILS_ROOT}/tmp/test/attachments")
82 Dir.mkdir "#{RAILS_ROOT}/tmp/test/attachments" unless File.directory?("#{RAILS_ROOT}/tmp/test/attachments")
83 Attachment.storage_path = "#{RAILS_ROOT}/tmp/test/attachments"
83 Attachment.storage_path = "#{RAILS_ROOT}/tmp/test/attachments"
84 end
84 end
85
85
86 def with_settings(options, &block)
86 def with_settings(options, &block)
87 saved_settings = options.keys.inject({}) {|h, k| h[k] = Setting[k].dup; h}
87 saved_settings = options.keys.inject({}) {|h, k| h[k] = Setting[k].dup; h}
88 options.each {|k, v| Setting[k] = v}
88 options.each {|k, v| Setting[k] = v}
89 yield
89 yield
90 saved_settings.each {|k, v| Setting[k] = v}
90 saved_settings.each {|k, v| Setting[k] = v}
91 end
91 end
92
92
93 def change_user_password(login, new_password)
93 def change_user_password(login, new_password)
94 user = User.first(:conditions => {:login => login})
94 user = User.first(:conditions => {:login => login})
95 user.password, user.password_confirmation = new_password, new_password
95 user.password, user.password_confirmation = new_password, new_password
96 user.save!
96 user.save!
97 end
97 end
98
98
99 def self.ldap_configured?
99 def self.ldap_configured?
100 @test_ldap = Net::LDAP.new(:host => '127.0.0.1', :port => 389)
100 @test_ldap = Net::LDAP.new(:host => '127.0.0.1', :port => 389)
101 return @test_ldap.bind
101 return @test_ldap.bind
102 rescue Exception => e
102 rescue Exception => e
103 # LDAP is not listening
103 # LDAP is not listening
104 return nil
104 return nil
105 end
105 end
106
106
107 # Returns the path to the test +vendor+ repository
107 # Returns the path to the test +vendor+ repository
108 def self.repository_path(vendor)
108 def self.repository_path(vendor)
109 File.join(RAILS_ROOT.gsub(%r{config\/\.\.}, ''), "/tmp/test/#{vendor.downcase}_repository")
109 File.join(RAILS_ROOT.gsub(%r{config\/\.\.}, ''), "/tmp/test/#{vendor.downcase}_repository")
110 end
110 end
111
111
112 # Returns true if the +vendor+ test repository is configured
112 # Returns true if the +vendor+ test repository is configured
113 def self.repository_configured?(vendor)
113 def self.repository_configured?(vendor)
114 File.directory?(repository_path(vendor))
114 File.directory?(repository_path(vendor))
115 end
115 end
116
116
117 def assert_error_tag(options={})
117 def assert_error_tag(options={})
118 assert_tag({:tag => 'p', :attributes => { :id => 'errorExplanation' }}.merge(options))
118 assert_tag({:tag => 'p', :attributes => { :id => 'errorExplanation' }}.merge(options))
119 end
119 end
120
120
121 # Shoulda macros
121 # Shoulda macros
122 def self.should_render_404
122 def self.should_render_404
123 should_respond_with :not_found
123 should_respond_with :not_found
124 should_render_template 'common/error'
124 should_render_template 'common/error'
125 end
125 end
126
126
127 def self.should_have_before_filter(expected_method, options = {})
127 def self.should_have_before_filter(expected_method, options = {})
128 should_have_filter('before', expected_method, options)
128 should_have_filter('before', expected_method, options)
129 end
129 end
130
130
131 def self.should_have_after_filter(expected_method, options = {})
131 def self.should_have_after_filter(expected_method, options = {})
132 should_have_filter('after', expected_method, options)
132 should_have_filter('after', expected_method, options)
133 end
133 end
134
134
135 def self.should_have_filter(filter_type, expected_method, options)
135 def self.should_have_filter(filter_type, expected_method, options)
136 description = "have #{filter_type}_filter :#{expected_method}"
136 description = "have #{filter_type}_filter :#{expected_method}"
137 description << " with #{options.inspect}" unless options.empty?
137 description << " with #{options.inspect}" unless options.empty?
138
138
139 should description do
139 should description do
140 klass = "action_controller/filters/#{filter_type}_filter".classify.constantize
140 klass = "action_controller/filters/#{filter_type}_filter".classify.constantize
141 expected = klass.new(:filter, expected_method.to_sym, options)
141 expected = klass.new(:filter, expected_method.to_sym, options)
142 assert_equal 1, @controller.class.filter_chain.select { |filter|
142 assert_equal 1, @controller.class.filter_chain.select { |filter|
143 filter.method == expected.method && filter.kind == expected.kind &&
143 filter.method == expected.method && filter.kind == expected.kind &&
144 filter.options == expected.options && filter.class == expected.class
144 filter.options == expected.options && filter.class == expected.class
145 }.size
145 }.size
146 end
146 end
147 end
147 end
148
148
149 def self.should_show_the_old_and_new_values_for(prop_key, model, &block)
149 def self.should_show_the_old_and_new_values_for(prop_key, model, &block)
150 context "" do
150 context "" do
151 setup do
151 setup do
152 if block_given?
152 if block_given?
153 instance_eval &block
153 instance_eval &block
154 else
154 else
155 @old_value = model.generate!
155 @old_value = model.generate!
156 @new_value = model.generate!
156 @new_value = model.generate!
157 end
157 end
158 end
158 end
159
159
160 should "use the new value's name" do
160 should "use the new value's name" do
161 @detail = JournalDetail.generate!(:property => 'attr',
161 @detail = JournalDetail.generate!(:property => 'attr',
162 :old_value => @old_value.id,
162 :old_value => @old_value.id,
163 :value => @new_value.id,
163 :value => @new_value.id,
164 :prop_key => prop_key)
164 :prop_key => prop_key)
165
165
166 assert_match @new_value.name, show_detail(@detail, true)
166 assert_match @new_value.name, show_detail(@detail, true)
167 end
167 end
168
168
169 should "use the old value's name" do
169 should "use the old value's name" do
170 @detail = JournalDetail.generate!(:property => 'attr',
170 @detail = JournalDetail.generate!(:property => 'attr',
171 :old_value => @old_value.id,
171 :old_value => @old_value.id,
172 :value => @new_value.id,
172 :value => @new_value.id,
173 :prop_key => prop_key)
173 :prop_key => prop_key)
174
174
175 assert_match @old_value.name, show_detail(@detail, true)
175 assert_match @old_value.name, show_detail(@detail, true)
176 end
176 end
177 end
177 end
178 end
178 end
179
179
180 def self.should_create_a_new_user(&block)
180 def self.should_create_a_new_user(&block)
181 should "create a new user" do
181 should "create a new user" do
182 user = instance_eval &block
182 user = instance_eval &block
183 assert user
183 assert user
184 assert_kind_of User, user
184 assert_kind_of User, user
185 assert !user.new_record?
185 assert !user.new_record?
186 end
186 end
187 end
187 end
188
188
189 # Test that a request allows the three types of API authentication
190 #
191 # * HTTP Basic with username and password
192 # * HTTP Basic with an api key for the username
193 # * Key based with the key=X parameter
194 #
195 # @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete)
196 # @param [String] url the request url
197 # @param [optional, Hash] parameters additional request parameters
198 def self.should_allow_api_authentication(http_method, url, parameters={})
199 should_allow_http_basic_auth_with_username_and_password(http_method, url, parameters)
200 should_allow_http_basic_auth_with_key(http_method, url, parameters)
201 should_allow_key_based_auth(http_method, url, parameters)
202 end
203
189 # Test that a request allows the username and password for HTTP BASIC
204 # Test that a request allows the username and password for HTTP BASIC
190 #
205 #
191 # @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete)
206 # @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete)
192 # @param [String] url the request url
207 # @param [String] url the request url
193 # @param [optional, Hash] parameters additional request parameters
208 # @param [optional, Hash] parameters additional request parameters
194 def self.should_allow_http_basic_auth_with_username_and_password(http_method, url, parameters={})
209 def self.should_allow_http_basic_auth_with_username_and_password(http_method, url, parameters={})
195 context "should allow http basic auth using a username and password for #{http_method} #{url}" do
210 context "should allow http basic auth using a username and password for #{http_method} #{url}" do
196 context "with a valid HTTP authentication" do
211 context "with a valid HTTP authentication" do
197 setup do
212 setup do
198 @user = User.generate_with_protected!(:password => 'my_password', :password_confirmation => 'my_password', :admin => true) # Admin so they can access the project
213 @user = User.generate_with_protected!(:password => 'my_password', :password_confirmation => 'my_password', :admin => true) # Admin so they can access the project
199 @authorization = ActionController::HttpAuthentication::Basic.encode_credentials(@user.login, 'my_password')
214 @authorization = ActionController::HttpAuthentication::Basic.encode_credentials(@user.login, 'my_password')
200 send(http_method, url, parameters, {:authorization => @authorization})
215 send(http_method, url, parameters, {:authorization => @authorization})
201 end
216 end
202
217
203 should_respond_with :success
218 should_respond_with :success
204 should_respond_with_content_type_based_on_url(url)
219 should_respond_with_content_type_based_on_url(url)
205 should "login as the user" do
220 should "login as the user" do
206 assert_equal @user, User.current
221 assert_equal @user, User.current
207 end
222 end
208 end
223 end
209
224
210 context "with an invalid HTTP authentication" do
225 context "with an invalid HTTP authentication" do
211 setup do
226 setup do
212 @user = User.generate_with_protected!
227 @user = User.generate_with_protected!
213 @authorization = ActionController::HttpAuthentication::Basic.encode_credentials(@user.login, 'wrong_password')
228 @authorization = ActionController::HttpAuthentication::Basic.encode_credentials(@user.login, 'wrong_password')
214 send(http_method, url, parameters, {:authorization => @authorization})
229 send(http_method, url, parameters, {:authorization => @authorization})
215 end
230 end
216
231
217 should_respond_with :unauthorized
232 should_respond_with :unauthorized
218 should_respond_with_content_type_based_on_url(url)
233 should_respond_with_content_type_based_on_url(url)
219 should "not login as the user" do
234 should "not login as the user" do
220 assert_equal User.anonymous, User.current
235 assert_equal User.anonymous, User.current
221 end
236 end
222 end
237 end
223
238
224 context "without credentials" do
239 context "without credentials" do
225 setup do
240 setup do
226 send(http_method, url, parameters, {:authorization => ''})
241 send(http_method, url, parameters, {:authorization => ''})
227 end
242 end
228
243
229 should_respond_with :unauthorized
244 should_respond_with :unauthorized
230 should_respond_with_content_type_based_on_url(url)
245 should_respond_with_content_type_based_on_url(url)
231 should "include_www_authenticate_header" do
246 should "include_www_authenticate_header" do
232 assert @controller.response.headers.has_key?('WWW-Authenticate')
247 assert @controller.response.headers.has_key?('WWW-Authenticate')
233 end
248 end
234 end
249 end
235 end
250 end
236
251
237 end
252 end
238
253
239 # Test that a request allows the API key with HTTP BASIC
254 # Test that a request allows the API key with HTTP BASIC
240 #
255 #
241 # @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete)
256 # @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete)
242 # @param [String] url the request url
257 # @param [String] url the request url
243 # @param [optional, Hash] parameters additional request parameters
258 # @param [optional, Hash] parameters additional request parameters
244 def self.should_allow_http_basic_auth_with_key(http_method, url, parameters={})
259 def self.should_allow_http_basic_auth_with_key(http_method, url, parameters={})
245 context "should allow http basic auth with a key for #{http_method} #{url}" do
260 context "should allow http basic auth with a key for #{http_method} #{url}" do
246 context "with a valid HTTP authentication using the API token" do
261 context "with a valid HTTP authentication using the API token" do
247 setup do
262 setup do
248 @user = User.generate_with_protected!
263 @user = User.generate_with_protected!(:admin => true)
249 @token = Token.generate!(:user => @user, :action => 'api')
264 @token = Token.generate!(:user => @user, :action => 'api')
250 @authorization = ActionController::HttpAuthentication::Basic.encode_credentials(@token.value, 'X')
265 @authorization = ActionController::HttpAuthentication::Basic.encode_credentials(@token.value, 'X')
251 send(http_method, url, parameters, {:authorization => @authorization})
266 send(http_method, url, parameters, {:authorization => @authorization})
252 end
267 end
253
268
254 should_respond_with :success
269 should_respond_with :success
255 should_respond_with_content_type_based_on_url(url)
270 should_respond_with_content_type_based_on_url(url)
271 should_be_a_valid_response_string_based_on_url(url)
256 should "login as the user" do
272 should "login as the user" do
257 assert_equal @user, User.current
273 assert_equal @user, User.current
258 end
274 end
259 end
275 end
260
276
261 context "with an invalid HTTP authentication" do
277 context "with an invalid HTTP authentication" do
262 setup do
278 setup do
263 @user = User.generate_with_protected!
279 @user = User.generate_with_protected!
264 @token = Token.generate!(:user => @user, :action => 'feeds')
280 @token = Token.generate!(:user => @user, :action => 'feeds')
265 @authorization = ActionController::HttpAuthentication::Basic.encode_credentials(@token.value, 'X')
281 @authorization = ActionController::HttpAuthentication::Basic.encode_credentials(@token.value, 'X')
266 send(http_method, url, parameters, {:authorization => @authorization})
282 send(http_method, url, parameters, {:authorization => @authorization})
267 end
283 end
268
284
269 should_respond_with :unauthorized
285 should_respond_with :unauthorized
270 should_respond_with_content_type_based_on_url(url)
286 should_respond_with_content_type_based_on_url(url)
271 should "not login as the user" do
287 should "not login as the user" do
272 assert_equal User.anonymous, User.current
288 assert_equal User.anonymous, User.current
273 end
289 end
274 end
290 end
275 end
291 end
276 end
292 end
277
293
278 # Test that a request allows full key authentication
294 # Test that a request allows full key authentication
279 #
295 #
280 # @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete)
296 # @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete)
281 # @param [String] url the request url, without the key=ZXY parameter
297 # @param [String] url the request url, without the key=ZXY parameter
282 def self.should_allow_key_based_auth(http_method, url)
298 # @param [optional, Hash] parameters additional request parameters
299 def self.should_allow_key_based_auth(http_method, url, parameters={})
283 context "should allow key based auth using key=X for #{http_method} #{url}" do
300 context "should allow key based auth using key=X for #{http_method} #{url}" do
284 context "with a valid api token" do
301 context "with a valid api token" do
285 setup do
302 setup do
286 @user = User.generate_with_protected!
303 @user = User.generate_with_protected!(:admin => true)
287 @token = Token.generate!(:user => @user, :action => 'api')
304 @token = Token.generate!(:user => @user, :action => 'api')
288 send(http_method, url + "?key=#{@token.value}")
305 # Simple url parse to add on ?key= or &key=
306 request_url = if url.match(/\?/)
307 url + "&key=#{@token.value}"
308 else
309 url + "?key=#{@token.value}"
310 end
311 send(http_method, request_url, parameters)
289 end
312 end
290
313
291 should_respond_with :success
314 should_respond_with :success
292 should_respond_with_content_type_based_on_url(url)
315 should_respond_with_content_type_based_on_url(url)
316 should_be_a_valid_response_string_based_on_url(url)
293 should "login as the user" do
317 should "login as the user" do
294 assert_equal @user, User.current
318 assert_equal @user, User.current
295 end
319 end
296 end
320 end
297
321
298 context "with an invalid api token" do
322 context "with an invalid api token" do
299 setup do
323 setup do
300 @user = User.generate_with_protected!
324 @user = User.generate_with_protected!
301 @token = Token.generate!(:user => @user, :action => 'feeds')
325 @token = Token.generate!(:user => @user, :action => 'feeds')
302 send(http_method, url + "?key=#{@token.value}")
326 send(http_method, url + "?key=#{@token.value}")
303 end
327 end
304
328
305 should_respond_with :unauthorized
329 should_respond_with :unauthorized
306 should_respond_with_content_type_based_on_url(url)
330 should_respond_with_content_type_based_on_url(url)
307 should "not login as the user" do
331 should "not login as the user" do
308 assert_equal User.anonymous, User.current
332 assert_equal User.anonymous, User.current
309 end
333 end
310 end
334 end
311 end
335 end
312
336
313 end
337 end
314
338
315 # Uses should_respond_with_content_type based on what's in the url:
339 # Uses should_respond_with_content_type based on what's in the url:
316 #
340 #
317 # '/project/issues.xml' => should_respond_with_content_type :xml
341 # '/project/issues.xml' => should_respond_with_content_type :xml
318 # '/project/issues.json' => should_respond_with_content_type :json
342 # '/project/issues.json' => should_respond_with_content_type :json
319 #
343 #
320 # @param [String] url Request
344 # @param [String] url Request
321 def self.should_respond_with_content_type_based_on_url(url)
345 def self.should_respond_with_content_type_based_on_url(url)
322 case
346 case
323 when url.match(/xml/i)
347 when url.match(/xml/i)
324 should_respond_with_content_type :xml
348 should_respond_with_content_type :xml
325 when url.match(/json/i)
349 when url.match(/json/i)
326 should_respond_with_content_type :json
350 should_respond_with_content_type :json
327 else
351 else
328 raise "Unknown content type for should_respond_with_content_type_based_on_url: #{url}"
352 raise "Unknown content type for should_respond_with_content_type_based_on_url: #{url}"
329 end
353 end
330
354
331 end
355 end
356
357 # Uses the url to assert which format the response should be in
358 #
359 # '/project/issues.xml' => should_be_a_valid_xml_string
360 # '/project/issues.json' => should_be_a_valid_json_string
361 #
362 # @param [String] url Request
363 def self.should_be_a_valid_response_string_based_on_url(url)
364 case
365 when url.match(/xml/i)
366 should_be_a_valid_xml_string
367 when url.match(/json/i)
368 should_be_a_valid_json_string
369 else
370 raise "Unknown content type for should_be_a_valid_response_based_on_url: #{url}"
371 end
372
373 end
374
375 # Checks that the response is a valid JSON string
376 def self.should_be_a_valid_json_string
377 should "be a valid JSON string" do
378 assert ActiveSupport::JSON.decode(response.body)
379 end
380 end
381
382 # Checks that the response is a valid XML string
383 def self.should_be_a_valid_xml_string
384 should "be a valid XML string" do
385 assert REXML::Document.new(response.body)
386 end
387 end
388
332 end
389 end
333
390
334 # Simple module to "namespace" all of the API tests
391 # Simple module to "namespace" all of the API tests
335 module ApiTest
392 module ApiTest
336 end
393 end
General Comments 0
You need to be logged in to leave comments. Login now