4 class ChangesetsControllerTest < ActionDispatch::IntegrationTest
6 # test all routes which lead to this controller
9 { :path => "/api/0.6/changesets", :method => :get },
10 { :controller => "api/changesets", :action => "index" }
13 { :path => "/api/0.6/changesets.json", :method => :get },
14 { :controller => "api/changesets", :action => "index", :format => "json" }
17 { :path => "/api/0.6/changesets", :method => :post },
18 { :controller => "api/changesets", :action => "create" }
21 { :path => "/api/0.6/changeset/1", :method => :get },
22 { :controller => "api/changesets", :action => "show", :id => "1" }
25 { :path => "/api/0.6/changeset/1.json", :method => :get },
26 { :controller => "api/changesets", :action => "show", :id => "1", :format => "json" }
29 { :path => "/api/0.6/changeset/1", :method => :put },
30 { :controller => "api/changesets", :action => "update", :id => "1" }
33 { :path => "/api/0.6/changeset/1/upload", :method => :post },
34 { :controller => "api/changesets", :action => "upload", :id => "1" }
37 { :path => "/api/0.6/changeset/1/close", :method => :put },
38 { :controller => "api/changesets", :action => "close", :id => "1" }
42 { :controller => "api/changesets", :action => "create" },
43 { :path => "/api/0.6/changeset/create", :method => :put }
48 # test the query functionality of changesets
50 private_user = create(:user, :data_public => false)
51 private_user_changeset = create(:changeset, :user => private_user)
52 private_user_closed_changeset = create(:changeset, :closed, :user => private_user)
54 changeset = create(:changeset, :user => user)
55 closed_changeset = create(:changeset, :closed, :user => user, :created_at => Time.utc(2008, 1, 1, 0, 0, 0), :closed_at => Time.utc(2008, 1, 2, 0, 0, 0))
56 changeset2 = create(:changeset, :min_lat => (5 * GeoRecord::SCALE).round, :min_lon => (5 * GeoRecord::SCALE).round, :max_lat => (15 * GeoRecord::SCALE).round, :max_lon => (15 * GeoRecord::SCALE).round)
57 changeset3 = create(:changeset, :min_lat => (4.5 * GeoRecord::SCALE).round, :min_lon => (4.5 * GeoRecord::SCALE).round, :max_lat => (5 * GeoRecord::SCALE).round, :max_lon => (5 * GeoRecord::SCALE).round)
59 get api_changesets_path(:bbox => "-10,-10, 10, 10")
60 assert_response :success, "can't get changesets in bbox"
61 assert_changesets_in_order [changeset3, changeset2]
63 get api_changesets_path(:bbox => "4.5,4.5,4.6,4.6")
64 assert_response :success, "can't get changesets in bbox"
65 assert_changesets_in_order [changeset3]
67 # not found when looking for changesets of non-existing users
68 get api_changesets_path(:user => User.maximum(:id) + 1)
69 assert_response :not_found
70 assert_equal "text/plain", @response.media_type
71 get api_changesets_path(:display_name => " ")
72 assert_response :not_found
73 assert_equal "text/plain", @response.media_type
75 # can't get changesets of user 1 without authenticating
76 get api_changesets_path(:user => private_user.id)
77 assert_response :not_found, "shouldn't be able to get changesets by non-public user (ID)"
78 get api_changesets_path(:display_name => private_user.display_name)
79 assert_response :not_found, "shouldn't be able to get changesets by non-public user (name)"
81 # but this should work
82 auth_header = bearer_authorization_header private_user
83 get api_changesets_path(:user => private_user.id), :headers => auth_header
84 assert_response :success, "can't get changesets by user ID"
85 assert_changesets_in_order [private_user_changeset, private_user_closed_changeset]
87 get api_changesets_path(:display_name => private_user.display_name), :headers => auth_header
88 assert_response :success, "can't get changesets by user name"
89 assert_changesets_in_order [private_user_changeset, private_user_closed_changeset]
92 get api_changesets_path(:display_name => private_user.display_name), :headers => auth_header, :params => { :format => "json" }
93 assert_response :success, "can't get changesets by user name"
95 js = ActiveSupport::JSON.decode(@response.body)
98 assert_equal Settings.api_version, js["version"]
99 assert_equal Settings.generator, js["generator"]
100 assert_equal 2, js["changesets"].count
102 # check that the correct error is given when we provide both UID and name
103 get api_changesets_path(:user => private_user.id,
104 :display_name => private_user.display_name), :headers => auth_header
105 assert_response :bad_request, "should be a bad request to have both ID and name specified"
107 get api_changesets_path(:user => private_user.id, :open => true), :headers => auth_header
108 assert_response :success, "can't get changesets by user and open"
109 assert_changesets_in_order [private_user_changeset]
111 get api_changesets_path(:time => "2007-12-31"), :headers => auth_header
112 assert_response :success, "can't get changesets by time-since"
113 assert_changesets_in_order [changeset3, changeset2, changeset, private_user_changeset, private_user_closed_changeset, closed_changeset]
115 get api_changesets_path(:time => "2008-01-01T12:34Z"), :headers => auth_header
116 assert_response :success, "can't get changesets by time-since with hour"
117 assert_changesets_in_order [changeset3, changeset2, changeset, private_user_changeset, private_user_closed_changeset, closed_changeset]
119 get api_changesets_path(:time => "2007-12-31T23:59Z,2008-01-02T00:01Z"), :headers => auth_header
120 assert_response :success, "can't get changesets by time-range"
121 assert_changesets_in_order [closed_changeset]
123 get api_changesets_path(:open => "true"), :headers => auth_header
124 assert_response :success, "can't get changesets by open-ness"
125 assert_changesets_in_order [changeset3, changeset2, changeset, private_user_changeset]
127 get api_changesets_path(:closed => "true"), :headers => auth_header
128 assert_response :success, "can't get changesets by closed-ness"
129 assert_changesets_in_order [private_user_closed_changeset, closed_changeset]
131 get api_changesets_path(:closed => "true", :user => private_user.id), :headers => auth_header
132 assert_response :success, "can't get changesets by closed-ness and user"
133 assert_changesets_in_order [private_user_closed_changeset]
135 get api_changesets_path(:closed => "true", :user => user.id), :headers => auth_header
136 assert_response :success, "can't get changesets by closed-ness and user"
137 assert_changesets_in_order [closed_changeset]
139 get api_changesets_path(:changesets => "#{private_user_changeset.id},#{changeset.id},#{closed_changeset.id}"), :headers => auth_header
140 assert_response :success, "can't get changesets by id (as comma-separated string)"
141 assert_changesets_in_order [changeset, private_user_changeset, closed_changeset]
143 get api_changesets_path(:changesets => ""), :headers => auth_header
144 assert_response :bad_request, "should be a bad request since changesets is empty"
148 # test the query functionality of changesets with the limit parameter
151 changeset1 = create(:changeset, :closed, :user => user, :created_at => Time.utc(2008, 1, 1, 0, 0, 0), :closed_at => Time.utc(2008, 1, 2, 0, 0, 0))
152 changeset2 = create(:changeset, :closed, :user => user, :created_at => Time.utc(2008, 2, 1, 0, 0, 0), :closed_at => Time.utc(2008, 2, 2, 0, 0, 0))
153 changeset3 = create(:changeset, :closed, :user => user, :created_at => Time.utc(2008, 3, 1, 0, 0, 0), :closed_at => Time.utc(2008, 3, 2, 0, 0, 0))
154 changeset4 = create(:changeset, :closed, :user => user, :created_at => Time.utc(2008, 4, 1, 0, 0, 0), :closed_at => Time.utc(2008, 4, 2, 0, 0, 0))
155 changeset5 = create(:changeset, :closed, :user => user, :created_at => Time.utc(2008, 5, 1, 0, 0, 0), :closed_at => Time.utc(2008, 5, 2, 0, 0, 0))
157 get api_changesets_path
158 assert_response :success
159 assert_changesets_in_order [changeset5, changeset4, changeset3, changeset2, changeset1]
161 get api_changesets_path(:limit => "3")
162 assert_response :success
163 assert_changesets_in_order [changeset5, changeset4, changeset3]
165 get api_changesets_path(:limit => "0")
166 assert_response :bad_request
168 get api_changesets_path(:limit => Settings.max_changeset_query_limit)
169 assert_response :success
170 assert_changesets_in_order [changeset5, changeset4, changeset3, changeset2, changeset1]
172 get api_changesets_path(:limit => Settings.max_changeset_query_limit + 1)
173 assert_response :bad_request
177 # test the query functionality of sequential changesets with order and time parameters
180 changeset1 = create(:changeset, :closed, :user => user, :created_at => Time.utc(2008, 1, 1, 0, 0, 0), :closed_at => Time.utc(2008, 1, 2, 0, 0, 0))
181 changeset2 = create(:changeset, :closed, :user => user, :created_at => Time.utc(2008, 2, 1, 0, 0, 0), :closed_at => Time.utc(2008, 2, 2, 0, 0, 0))
182 changeset3 = create(:changeset, :closed, :user => user, :created_at => Time.utc(2008, 3, 1, 0, 0, 0), :closed_at => Time.utc(2008, 3, 2, 0, 0, 0))
183 changeset4 = create(:changeset, :closed, :user => user, :created_at => Time.utc(2008, 4, 1, 0, 0, 0), :closed_at => Time.utc(2008, 4, 2, 0, 0, 0))
184 changeset5 = create(:changeset, :closed, :user => user, :created_at => Time.utc(2008, 5, 1, 0, 0, 0), :closed_at => Time.utc(2008, 5, 2, 0, 0, 0))
185 changeset6 = create(:changeset, :closed, :user => user, :created_at => Time.utc(2008, 6, 1, 0, 0, 0), :closed_at => Time.utc(2008, 6, 2, 0, 0, 0))
187 get api_changesets_path
188 assert_response :success
189 assert_changesets_in_order [changeset6, changeset5, changeset4, changeset3, changeset2, changeset1]
191 get api_changesets_path(:order => "oldest")
192 assert_response :success
193 assert_changesets_in_order [changeset1, changeset2, changeset3, changeset4, changeset5, changeset6]
196 # lower time bound at the opening time of a changeset
197 ["2008-02-01T00:00:00Z", "2008-05-15T00:00:00Z", [changeset5, changeset4, changeset3, changeset2], [changeset5, changeset4, changeset3, changeset2]],
198 # lower time bound in the middle of a changeset
199 ["2008-02-01T12:00:00Z", "2008-05-15T00:00:00Z", [changeset5, changeset4, changeset3, changeset2], [changeset5, changeset4, changeset3]],
200 # lower time bound at the closing time of a changeset
201 ["2008-02-02T00:00:00Z", "2008-05-15T00:00:00Z", [changeset5, changeset4, changeset3, changeset2], [changeset5, changeset4, changeset3]],
202 # lower time bound after the closing time of a changeset
203 ["2008-02-02T00:00:01Z", "2008-05-15T00:00:00Z", [changeset5, changeset4, changeset3], [changeset5, changeset4, changeset3]],
204 # upper time bound in the middle of a changeset
205 ["2007-09-09T12:00:00Z", "2008-04-01T12:00:00Z", [changeset4, changeset3, changeset2, changeset1], [changeset4, changeset3, changeset2, changeset1]],
207 ["2009-02-02T00:00:01Z", "2018-05-15T00:00:00Z", [], []]
208 ].each do |from, to, interval_changesets, point_changesets|
209 get api_changesets_path(:time => "#{from},#{to}")
210 assert_response :success
211 assert_changesets_in_order interval_changesets
213 get api_changesets_path(:from => from, :to => to)
214 assert_response :success
215 assert_changesets_in_order point_changesets
217 get api_changesets_path(:from => from, :to => to, :order => "oldest")
218 assert_response :success
219 assert_changesets_in_order point_changesets.reverse
224 # test the query functionality of overlapping changesets with order and time parameters
225 def test_index_order_overlapping
227 changeset1 = create(:changeset, :closed, :user => user, :created_at => Time.utc(2015, 6, 4, 17, 0, 0), :closed_at => Time.utc(2015, 6, 4, 17, 0, 0))
228 changeset2 = create(:changeset, :closed, :user => user, :created_at => Time.utc(2015, 6, 4, 16, 0, 0), :closed_at => Time.utc(2015, 6, 4, 18, 0, 0))
229 changeset3 = create(:changeset, :closed, :user => user, :created_at => Time.utc(2015, 6, 4, 14, 0, 0), :closed_at => Time.utc(2015, 6, 4, 20, 0, 0))
230 changeset4 = create(:changeset, :closed, :user => user, :created_at => Time.utc(2015, 6, 3, 23, 0, 0), :closed_at => Time.utc(2015, 6, 4, 23, 0, 0))
231 create(:changeset, :closed, :user => user, :created_at => Time.utc(2015, 6, 2, 23, 0, 0), :closed_at => Time.utc(2015, 6, 3, 23, 0, 0))
233 get api_changesets_path(:time => "2015-06-04T00:00:00Z")
234 assert_response :success
235 assert_changesets_in_order [changeset1, changeset2, changeset3, changeset4]
237 get api_changesets_path(:from => "2015-06-04T00:00:00Z")
238 assert_response :success
239 assert_changesets_in_order [changeset1, changeset2, changeset3]
241 get api_changesets_path(:from => "2015-06-04T00:00:00Z", :order => "oldest")
242 assert_response :success
243 assert_changesets_in_order [changeset3, changeset2, changeset1]
245 get api_changesets_path(:time => "2015-06-04T16:00:00Z,2015-06-04T17:30:00Z")
246 assert_response :success
247 assert_changesets_in_order [changeset1, changeset2, changeset3, changeset4]
249 get api_changesets_path(:from => "2015-06-04T16:00:00Z", :to => "2015-06-04T17:30:00Z")
250 assert_response :success
251 assert_changesets_in_order [changeset1, changeset2]
253 get api_changesets_path(:from => "2015-06-04T16:00:00Z", :to => "2015-06-04T17:30:00Z", :order => "oldest")
254 assert_response :success
255 assert_changesets_in_order [changeset2, changeset1]
259 # check that errors are returned if garbage is inserted
261 def test_index_invalid
264 ";drop table users;"].each do |bbox|
265 get api_changesets_path(:bbox => bbox)
266 assert_response :bad_request, "'#{bbox}' isn't a bbox"
271 ";drop table users;",
273 "-,-"].each do |time|
274 get api_changesets_path(:time => time)
275 assert_response :bad_request, "'#{time}' isn't a valid time range"
282 get api_changesets_path(:user => uid)
283 assert_response :bad_request, "'#{uid}' isn't a valid user ID"
286 get api_changesets_path(:order => "oldest", :time => "2008-01-01T00:00Z,2018-01-01T00:00Z")
287 assert_response :bad_request, "cannot use order=oldest with time"
290 # -----------------------
291 # Test simple changeset creation
292 # -----------------------
295 auth_header = bearer_authorization_header create(:user, :data_public => false)
296 # Create the first user's changeset
297 xml = "<osm><changeset>" \
298 "<tag k='created_by' v='osm test suite checking changesets'/>" \
300 post api_changesets_path, :params => xml, :headers => auth_header
301 assert_require_public_data
303 auth_header = bearer_authorization_header
304 # Create the first user's changeset
305 xml = "<osm><changeset>" \
306 "<tag k='created_by' v='osm test suite checking changesets'/>" \
308 post api_changesets_path, :params => xml, :headers => auth_header
310 assert_response :success, "Creation of changeset did not return success status"
311 newid = @response.body.to_i
313 # check end time, should be an hour ahead of creation time
314 cs = Changeset.find(newid)
315 duration = cs.closed_at - cs.created_at
316 # the difference can either be a rational, or a floating point number
317 # of seconds, depending on the code path taken :-(
318 if duration.instance_of?(Rational)
319 assert_equal Rational(1, 24), duration, "initial idle timeout should be an hour (#{cs.created_at} -> #{cs.closed_at})"
321 # must be number of seconds...
322 assert_equal 3600, duration.round, "initial idle timeout should be an hour (#{cs.created_at} -> #{cs.closed_at})"
325 # checks if uploader was subscribed
326 assert_equal 1, cs.subscribers.length
329 def test_create_invalid
330 auth_header = bearer_authorization_header create(:user, :data_public => false)
331 xml = "<osm><changeset></osm>"
332 post api_changesets_path, :params => xml, :headers => auth_header
333 assert_require_public_data
335 ## Try the public user
336 auth_header = bearer_authorization_header
337 xml = "<osm><changeset></osm>"
338 post api_changesets_path, :params => xml, :headers => auth_header
339 assert_response :bad_request, "creating a invalid changeset should fail"
342 def test_create_invalid_no_content
343 ## First check with no auth
344 post api_changesets_path
345 assert_response :unauthorized, "shouldn't be able to create a changeset with no auth"
347 ## Now try to with a non-public user
348 auth_header = bearer_authorization_header create(:user, :data_public => false)
349 post api_changesets_path, :headers => auth_header
350 assert_require_public_data
352 ## Try an inactive user
353 auth_header = bearer_authorization_header create(:user, :pending)
354 post api_changesets_path, :headers => auth_header
357 ## Now try to use a normal user
358 auth_header = bearer_authorization_header
359 post api_changesets_path, :headers => auth_header
360 assert_response :bad_request, "creating a changeset with no content should fail"
363 def test_create_wrong_method
364 auth_header = bearer_authorization_header
366 put api_changesets_path, :headers => auth_header
367 assert_response :not_found
368 assert_template "rescues/routing_error"
371 def test_create_legacy_path
372 auth_header = bearer_authorization_header
373 xml = "<osm><changeset></changeset></osm>"
375 assert_difference "Changeset.count", 1 do
376 put "/api/0.6/changeset/create", :params => xml, :headers => auth_header
379 assert_response :success, "Creation of changeset did not return success status"
380 assert_equal Changeset.last.id, @response.body.to_i
384 # check that the changeset can be shown and returns the correct
385 # document structure.
387 changeset = create(:changeset)
389 get api_changeset_path(changeset)
390 assert_response :success, "cannot get first changeset"
392 assert_dom "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
393 assert_single_changeset changeset do
394 assert_dom "> discussion", 0
397 get api_changeset_path(changeset, :include_discussion => true)
398 assert_response :success, "cannot get first changeset with comments"
400 assert_dom "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
401 assert_single_changeset changeset do
402 assert_dom "> discussion", 1
403 assert_dom "> discussion > comment", 0
407 def test_show_comments
408 # all comments visible
409 changeset = create(:changeset, :closed)
410 comment1, comment2, comment3 = create_list(:changeset_comment, 3, :changeset_id => changeset.id)
412 get api_changeset_path(changeset, :include_discussion => true)
413 assert_response :success, "cannot get closed changeset with comments"
415 assert_dom "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1 do
416 assert_single_changeset changeset do
417 assert_dom "> discussion", 1 do
418 assert_dom "> comment", 3 do |dom_comments|
419 assert_dom dom_comments[0], "> @id", comment1.id.to_s
420 assert_dom dom_comments[0], "> @visible", "true"
421 assert_dom dom_comments[1], "> @id", comment2.id.to_s
422 assert_dom dom_comments[1], "> @visible", "true"
423 assert_dom dom_comments[2], "> @id", comment3.id.to_s
424 assert_dom dom_comments[2], "> @visible", "true"
430 # one hidden comment not included because not asked for
431 comment2.update(:visible => false)
434 get api_changeset_path(changeset, :include_discussion => true)
435 assert_response :success, "cannot get closed changeset with comments"
437 assert_dom "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
438 assert_single_changeset changeset do
439 assert_dom "> discussion", 1 do
440 assert_dom "> comment", 2 do |dom_comments|
441 assert_dom dom_comments[0], "> @id", comment1.id.to_s
442 assert_dom dom_comments[0], "> @visible", "true"
443 assert_dom dom_comments[1], "> @id", comment3.id.to_s
444 assert_dom dom_comments[1], "> @visible", "true"
449 # one hidden comment not included because no permissions
450 get api_changeset_path(changeset, :include_discussion => true, :show_hidden_comments => true)
451 assert_response :success, "cannot get closed changeset with comments"
453 assert_dom "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
454 assert_single_changeset changeset do
455 assert_dom "> discussion", 1 do
456 assert_dom "> comment", 2 do |dom_comments|
457 assert_dom dom_comments[0], "> @id", comment1.id.to_s
458 assert_dom dom_comments[0], "> @visible", "true"
459 # maybe will show an empty comment element with visible=false in the future
460 assert_dom dom_comments[1], "> @id", comment3.id.to_s
461 assert_dom dom_comments[1], "> @visible", "true"
466 # one hidden comment shown to moderators
467 moderator_user = create(:moderator_user)
468 auth_header = bearer_authorization_header moderator_user
469 get api_changeset_path(changeset, :include_discussion => true, :show_hidden_comments => true), :headers => auth_header
470 assert_response :success, "cannot get closed changeset with comments"
472 assert_dom "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
473 assert_single_changeset changeset do
474 assert_dom "> discussion", 1 do
475 assert_dom "> comment", 3 do |dom_comments|
476 assert_dom dom_comments[0], "> @id", comment1.id.to_s
477 assert_dom dom_comments[0], "> @visible", "true"
478 assert_dom dom_comments[1], "> @id", comment2.id.to_s
479 assert_dom dom_comments[1], "> @visible", "false"
480 assert_dom dom_comments[2], "> @id", comment3.id.to_s
481 assert_dom dom_comments[2], "> @visible", "true"
488 changeset = create(:changeset, :closed)
489 create(:changeset_tag, :changeset => changeset, :k => "created_by", :v => "JOSM/1.5 (18364)")
490 create(:changeset_tag, :changeset => changeset, :k => "comment", :v => "changeset comment")
492 get api_changeset_path(changeset)
494 assert_response :success
495 assert_dom "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
496 assert_single_changeset changeset do
497 assert_dom "> tag", 2
498 assert_dom "> tag[k='created_by'][v='JOSM/1.5 (18364)']", 1
499 assert_dom "> tag[k='comment'][v='changeset comment']", 1
504 changeset = create(:changeset)
506 get api_changeset_path(changeset, :format => "json")
507 assert_response :success, "cannot get first changeset"
509 js = ActiveSupport::JSON.decode(@response.body)
512 assert_equal Settings.api_version, js["version"]
513 assert_equal Settings.generator, js["generator"]
514 assert_single_changeset_json changeset, js
515 assert_nil js["changeset"]["tags"]
516 assert_nil js["changeset"]["comments"]
517 assert_equal changeset.user.id, js["changeset"]["uid"]
518 assert_equal changeset.user.display_name, js["changeset"]["user"]
520 get api_changeset_path(changeset, :format => "json", :include_discussion => true)
521 assert_response :success, "cannot get first changeset with comments"
523 js = ActiveSupport::JSON.decode(@response.body)
525 assert_equal Settings.api_version, js["version"]
526 assert_equal Settings.generator, js["generator"]
527 assert_single_changeset_json changeset, js
528 assert_nil js["changeset"]["tags"]
529 assert_nil js["changeset"]["min_lat"]
530 assert_nil js["changeset"]["min_lon"]
531 assert_nil js["changeset"]["max_lat"]
532 assert_nil js["changeset"]["max_lon"]
533 assert_equal 0, js["changeset"]["comments"].count
536 def test_show_comments_json
537 # all comments visible
538 changeset = create(:changeset, :closed)
539 comment0, comment1, comment2 = create_list(:changeset_comment, 3, :changeset_id => changeset.id)
541 get api_changeset_path(changeset, :format => "json", :include_discussion => true)
542 assert_response :success, "cannot get closed changeset with comments"
544 js = ActiveSupport::JSON.decode(@response.body)
546 assert_equal Settings.api_version, js["version"]
547 assert_equal Settings.generator, js["generator"]
548 assert_single_changeset_json changeset, js
549 assert_equal 3, js["changeset"]["comments"].count
550 assert_equal comment0.id, js["changeset"]["comments"][0]["id"]
551 assert js["changeset"]["comments"][0]["visible"]
552 assert_equal comment1.id, js["changeset"]["comments"][1]["id"]
553 assert js["changeset"]["comments"][1]["visible"]
554 assert_equal comment2.id, js["changeset"]["comments"][2]["id"]
555 assert js["changeset"]["comments"][2]["visible"]
557 # one hidden comment not included because not asked for
558 comment1.update(:visible => false)
561 get api_changeset_path(changeset, :format => "json", :include_discussion => true)
562 assert_response :success, "cannot get closed changeset with comments"
564 js = ActiveSupport::JSON.decode(@response.body)
566 assert_equal Settings.api_version, js["version"]
567 assert_equal Settings.generator, js["generator"]
568 assert_single_changeset_json changeset, js
569 assert_equal 2, js["changeset"]["comments"].count
570 assert_equal comment0.id, js["changeset"]["comments"][0]["id"]
571 assert js["changeset"]["comments"][0]["visible"]
572 assert_equal comment2.id, js["changeset"]["comments"][1]["id"]
573 assert js["changeset"]["comments"][1]["visible"]
575 # one hidden comment not included because no permissions
576 get api_changeset_path(changeset, :format => "json", :include_discussion => true, :show_hidden_comments => true)
577 assert_response :success, "cannot get closed changeset with comments"
579 js = ActiveSupport::JSON.decode(@response.body)
581 assert_equal Settings.api_version, js["version"]
582 assert_equal Settings.generator, js["generator"]
583 assert_single_changeset_json changeset, js
584 assert_equal 2, js["changeset"]["comments"].count
585 assert_equal comment0.id, js["changeset"]["comments"][0]["id"]
586 assert js["changeset"]["comments"][0]["visible"]
587 # maybe will show an empty comment element with visible=false in the future
588 assert_equal comment2.id, js["changeset"]["comments"][1]["id"]
589 assert js["changeset"]["comments"][1]["visible"]
591 # one hidden comment shown to moderators
592 moderator_user = create(:moderator_user)
593 auth_header = bearer_authorization_header moderator_user
594 get api_changeset_path(changeset, :format => "json", :include_discussion => true, :show_hidden_comments => true), :headers => auth_header
595 assert_response :success, "cannot get closed changeset with comments"
597 js = ActiveSupport::JSON.decode(@response.body)
599 assert_equal Settings.api_version, js["version"]
600 assert_equal Settings.generator, js["generator"]
601 assert_single_changeset_json changeset, js
602 assert_equal 3, js["changeset"]["comments"].count
603 assert_equal comment0.id, js["changeset"]["comments"][0]["id"]
604 assert js["changeset"]["comments"][0]["visible"]
605 assert_equal comment1.id, js["changeset"]["comments"][1]["id"]
606 assert_not js["changeset"]["comments"][1]["visible"]
607 assert_equal comment2.id, js["changeset"]["comments"][2]["id"]
608 assert js["changeset"]["comments"][2]["visible"]
611 def test_show_tags_json
612 changeset = create(:changeset, :closed)
613 create(:changeset_tag, :changeset => changeset, :k => "created_by", :v => "JOSM/1.5 (18364)")
614 create(:changeset_tag, :changeset => changeset, :k => "comment", :v => "changeset comment")
616 get api_changeset_path(changeset, :format => "json")
618 assert_response :success
619 js = ActiveSupport::JSON.decode(@response.body)
621 assert_equal Settings.api_version, js["version"]
622 assert_equal Settings.generator, js["generator"]
623 assert_single_changeset_json changeset, js
624 assert_equal 2, js["changeset"]["tags"].count
625 assert_equal "JOSM/1.5 (18364)", js["changeset"]["tags"]["created_by"]
626 assert_equal "changeset comment", js["changeset"]["tags"]["comment"]
629 def test_show_bbox_json
630 # test bbox attribute
631 changeset = create(:changeset, :min_lat => (-5 * GeoRecord::SCALE).round, :min_lon => (5 * GeoRecord::SCALE).round,
632 :max_lat => (15 * GeoRecord::SCALE).round, :max_lon => (12 * GeoRecord::SCALE).round)
634 get api_changeset_path(changeset, :format => "json")
635 assert_response :success, "cannot get first changeset"
637 js = ActiveSupport::JSON.decode(@response.body)
639 assert_equal(-5, js["changeset"]["min_lat"])
640 assert_equal 5, js["changeset"]["min_lon"]
641 assert_equal 15, js["changeset"]["max_lat"]
642 assert_equal 12, js["changeset"]["max_lon"]
646 # check that a changeset that doesn't exist returns an appropriate message
647 def test_show_not_found
648 [0, -32, 233455644, "afg", "213"].each do |id|
649 get api_changeset_path(id)
650 assert_response :not_found, "should get a not found"
651 rescue ActionController::UrlGenerationError => e
652 assert_match(/No route matches/, e.to_s)
657 # test that the user who opened a change can close it
659 private_user = create(:user, :data_public => false)
660 private_changeset = create(:changeset, :user => private_user)
662 changeset = create(:changeset, :user => user)
664 ## Try without authentication
665 put changeset_close_path(changeset)
666 assert_response :unauthorized
668 ## Try using the non-public user
669 auth_header = bearer_authorization_header private_user
670 put changeset_close_path(private_changeset), :headers => auth_header
671 assert_require_public_data
673 ## The try with the public user
674 auth_header = bearer_authorization_header user
677 put changeset_close_path(cs_id), :headers => auth_header
678 assert_response :success
680 # test that it really is closed now
681 cs = Changeset.find(changeset.id)
683 "changeset should be closed now (#{cs.closed_at} > #{Time.now.utc}.")
687 # test that a different user can't close another user's changeset
688 def test_close_invalid
690 changeset = create(:changeset)
692 auth_header = bearer_authorization_header user
694 put changeset_close_path(changeset), :headers => auth_header
695 assert_response :conflict
696 assert_equal "The user doesn't own that changeset", @response.body
700 # test that you can't close using another method
701 def test_close_method_invalid
703 changeset = create(:changeset, :user => user)
705 auth_header = bearer_authorization_header user
707 get changeset_close_path(changeset), :headers => auth_header
708 assert_response :not_found
709 assert_template "rescues/routing_error"
711 post changeset_close_path(changeset), :headers => auth_header
712 assert_response :not_found
713 assert_template "rescues/routing_error"
717 # check that you can't close a changeset that isn't found
718 def test_close_not_found
719 cs_ids = [0, -132, "123"]
721 # First try to do it with no auth
723 put changeset_close_path(id)
724 assert_response :unauthorized, "Shouldn't be able close the non-existant changeset #{id}, when not authorized"
725 rescue ActionController::UrlGenerationError => e
726 assert_match(/No route matches/, e.to_s)
730 auth_header = bearer_authorization_header
732 put changeset_close_path(id), :headers => auth_header
733 assert_response :not_found, "The changeset #{id} doesn't exist, so can't be closed"
734 rescue ActionController::UrlGenerationError => e
735 assert_match(/No route matches/, e.to_s)
740 # upload something simple, but valid and check that it can
742 # Also try without auth and another user.
743 def test_upload_simple_valid
744 private_user = create(:user, :data_public => false)
745 private_changeset = create(:changeset, :user => private_user)
747 changeset = create(:changeset, :user => user)
751 relation = create(:relation)
752 other_relation = create(:relation)
753 # create some tags, since we test that they are removed later
754 create(:node_tag, :node => node)
755 create(:way_tag, :way => way)
756 create(:relation_tag, :relation => relation)
759 changeset_id = changeset.id
761 # simple diff to change a node, way and relation by removing
766 <node id='#{node.id}' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
767 <way id='#{way.id}' changeset='#{changeset_id}' version='1'>
768 <nd ref='#{node.id}'/>
772 <relation id='#{relation.id}' changeset='#{changeset_id}' version='1'>
773 <member type='way' role='some' ref='#{way.id}'/>
774 <member type='node' role='some' ref='#{node.id}'/>
775 <member type='relation' role='some' ref='#{other_relation.id}'/>
782 post changeset_upload_path(changeset), :params => diff
783 assert_response :unauthorized,
784 "shouldn't be able to upload a simple valid diff to changeset: #{@response.body}"
786 ## Now try with a private user
787 auth_header = bearer_authorization_header private_user
788 changeset_id = private_changeset.id
790 # simple diff to change a node, way and relation by removing
795 <node id='#{node.id}' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
796 <way id='#{way.id}' changeset='#{changeset_id}' version='1'>
797 <nd ref='#{node.id}'/>
801 <relation id='#{relation.id}' changeset='#{changeset_id}' version='1'>
802 <member type='way' role='some' ref='#{way.id}'/>
803 <member type='node' role='some' ref='#{node.id}'/>
804 <member type='relation' role='some' ref='#{other_relation.id}'/>
811 post changeset_upload_path(private_changeset), :params => diff, :headers => auth_header
812 assert_response :forbidden,
813 "can't upload a simple valid diff to changeset: #{@response.body}"
815 ## Now try with the public user
816 auth_header = bearer_authorization_header user
817 changeset_id = changeset.id
819 # simple diff to change a node, way and relation by removing
824 <node id='#{node.id}' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
825 <way id='#{way.id}' changeset='#{changeset_id}' version='1'>
826 <nd ref='#{node.id}'/>
830 <relation id='#{relation.id}' changeset='#{changeset_id}' version='1'>
831 <member type='way' role='some' ref='#{way.id}'/>
832 <member type='node' role='some' ref='#{node.id}'/>
833 <member type='relation' role='some' ref='#{other_relation.id}'/>
840 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
841 assert_response :success,
842 "can't upload a simple valid diff to changeset: #{@response.body}"
844 # check that the changes made it into the database
845 assert_equal 0, Node.find(node.id).tags.size, "node #{node.id} should now have no tags"
846 assert_equal 0, Way.find(way.id).tags.size, "way #{way.id} should now have no tags"
847 assert_equal 0, Relation.find(relation.id).tags.size, "relation #{relation.id} should now have no tags"
851 # upload something which creates new objects using placeholders
852 def test_upload_create_valid
854 changeset = create(:changeset, :user => user)
856 way = create(:way_with_nodes, :nodes_count => 2)
857 relation = create(:relation)
859 auth_header = bearer_authorization_header user
861 # simple diff to create a node way and relation using placeholders
865 <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
866 <tag k='foo' v='bar'/>
867 <tag k='baz' v='bat'/>
869 <way id='-1' changeset='#{changeset.id}'>
870 <nd ref='#{node.id}'/>
874 <relation id='-1' changeset='#{changeset.id}'>
875 <member type='way' role='some' ref='#{way.id}'/>
876 <member type='node' role='some' ref='#{node.id}'/>
877 <member type='relation' role='some' ref='#{relation.id}'/>
884 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
885 assert_response :success,
886 "can't upload a simple valid creation to changeset: #{@response.body}"
888 # check the returned payload
889 new_node_id, new_way_id, new_rel_id = nil
890 assert_dom "diffResult[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1 do
891 # inspect the response to find out what the new element IDs are
892 # check the old IDs are all present and negative one
893 # check the versions are present and equal one
894 assert_dom "> node", 1 do |(node_el)|
895 new_node_id = node_el["new_id"].to_i
896 assert_dom "> @old_id", "-1"
897 assert_dom "> @new_version", "1"
899 assert_dom "> way", 1 do |(way_el)|
900 new_way_id = way_el["new_id"].to_i
901 assert_dom "> @old_id", "-1"
902 assert_dom "> @new_version", "1"
904 assert_dom "> relation", 1 do |(rel_el)|
905 new_rel_id = rel_el["new_id"].to_i
906 assert_dom "> @old_id", "-1"
907 assert_dom "> @new_version", "1"
911 # check that the changes made it into the database
912 assert_equal 2, Node.find(new_node_id).tags.size, "new node should have two tags"
913 assert_equal 0, Way.find(new_way_id).tags.size, "new way should have no tags"
914 assert_equal 0, Relation.find(new_rel_id).tags.size, "new relation should have no tags"
918 # test a complex delete where we delete elements which rely on eachother
919 # in the same transaction.
920 def test_upload_delete
921 changeset = create(:changeset)
922 super_relation = create(:relation)
923 used_relation = create(:relation)
924 used_way = create(:way)
925 used_node = create(:node)
926 create(:relation_member, :relation => super_relation, :member => used_relation)
927 create(:relation_member, :relation => super_relation, :member => used_way)
928 create(:relation_member, :relation => super_relation, :member => used_node)
930 auth_header = bearer_authorization_header changeset.user
932 diff = XML::Document.new
933 diff.root = XML::Node.new "osmChange"
934 delete = XML::Node.new "delete"
936 delete << xml_node_for_relation(super_relation)
937 delete << xml_node_for_relation(used_relation)
938 delete << xml_node_for_way(used_way)
939 delete << xml_node_for_node(used_node)
941 # update the changeset to one that this user owns
942 %w[node way relation].each do |type|
943 delete.find("//osmChange/delete/#{type}").each do |n|
944 n["changeset"] = changeset.id.to_s
949 post changeset_upload_path(changeset), :params => diff.to_s, :headers => auth_header
950 assert_response :success,
951 "can't upload a deletion diff to changeset: #{@response.body}"
953 # check the response is well-formed
954 assert_select "diffResult>node", 1
955 assert_select "diffResult>way", 1
956 assert_select "diffResult>relation", 2
958 # check that everything was deleted
959 assert_not Node.find(used_node.id).visible
960 assert_not Way.find(used_way.id).visible
961 assert_not Relation.find(super_relation.id).visible
962 assert_not Relation.find(used_relation.id).visible
966 # test uploading a delete with no lat/lon, as they are optional in
967 # the osmChange spec.
968 def test_upload_nolatlon_delete
970 changeset = create(:changeset)
972 auth_header = bearer_authorization_header changeset.user
973 diff = "<osmChange><delete><node id='#{node.id}' version='#{node.version}' changeset='#{changeset.id}'/></delete></osmChange>"
976 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
977 assert_response :success,
978 "can't upload a deletion diff to changeset: #{@response.body}"
980 # check the response is well-formed
981 assert_select "diffResult>node", 1
983 # check that everything was deleted
984 assert_not Node.find(node.id).visible
987 def test_repeated_changeset_create
989 auth_header = bearer_authorization_header
991 # create a temporary changeset
992 xml = "<osm><changeset>" \
993 "<tag k='created_by' v='osm test suite checking changesets'/>" \
995 assert_difference "Changeset.count", 1 do
996 post api_changesets_path, :params => xml, :headers => auth_header
998 assert_response :success
1002 def test_upload_large_changeset
1003 user = create(:user)
1004 auth_header = bearer_authorization_header user
1006 # create an old changeset to ensure we have the maximum rate limit
1007 create(:changeset, :user => user, :created_at => Time.now.utc - 28.days)
1009 # create a changeset
1010 post api_changesets_path, :params => "<osm><changeset/></osm>", :headers => auth_header
1011 assert_response :success, "Should be able to create a changeset: #{@response.body}"
1012 changeset_id = @response.body.to_i
1014 # upload some widely-spaced nodes, spiralling positive and negative
1018 <node id='-1' lon='-20' lat='-10' changeset='#{changeset_id}'/>
1019 <node id='-10' lon='20' lat='10' changeset='#{changeset_id}'/>
1020 <node id='-2' lon='-40' lat='-20' changeset='#{changeset_id}'/>
1021 <node id='-11' lon='40' lat='20' changeset='#{changeset_id}'/>
1022 <node id='-3' lon='-60' lat='-30' changeset='#{changeset_id}'/>
1023 <node id='-12' lon='60' lat='30' changeset='#{changeset_id}'/>
1024 <node id='-4' lon='-80' lat='-40' changeset='#{changeset_id}'/>
1025 <node id='-13' lon='80' lat='40' changeset='#{changeset_id}'/>
1026 <node id='-5' lon='-100' lat='-50' changeset='#{changeset_id}'/>
1027 <node id='-14' lon='100' lat='50' changeset='#{changeset_id}'/>
1028 <node id='-6' lon='-120' lat='-60' changeset='#{changeset_id}'/>
1029 <node id='-15' lon='120' lat='60' changeset='#{changeset_id}'/>
1030 <node id='-7' lon='-140' lat='-70' changeset='#{changeset_id}'/>
1031 <node id='-16' lon='140' lat='70' changeset='#{changeset_id}'/>
1032 <node id='-8' lon='-160' lat='-80' changeset='#{changeset_id}'/>
1033 <node id='-17' lon='160' lat='80' changeset='#{changeset_id}'/>
1034 <node id='-9' lon='-179.9' lat='-89.9' changeset='#{changeset_id}'/>
1035 <node id='-18' lon='179.9' lat='89.9' changeset='#{changeset_id}'/>
1040 # upload it, which used to cause an error like "PGError: ERROR:
1041 # integer out of range" (bug #2152). but shouldn't any more.
1042 post changeset_upload_path(changeset_id), :params => diff, :headers => auth_header
1043 assert_response :success,
1044 "can't upload a spatially-large diff to changeset: #{@response.body}"
1046 # check that the changeset bbox is within bounds
1047 cs = Changeset.find(changeset_id)
1048 assert_operator cs.min_lon, :>=, -180 * GeoRecord::SCALE, "Minimum longitude (#{cs.min_lon / GeoRecord::SCALE}) should be >= -180 to be valid."
1049 assert_operator cs.max_lon, :<=, 180 * GeoRecord::SCALE, "Maximum longitude (#{cs.max_lon / GeoRecord::SCALE}) should be <= 180 to be valid."
1050 assert_operator cs.min_lat, :>=, -90 * GeoRecord::SCALE, "Minimum latitude (#{cs.min_lat / GeoRecord::SCALE}) should be >= -90 to be valid."
1051 assert_operator cs.max_lat, :<=, 90 * GeoRecord::SCALE, "Maximum latitude (#{cs.max_lat / GeoRecord::SCALE}) should be <= 90 to be valid."
1055 # test that deleting stuff in a transaction doesn't bypass the checks
1056 # to ensure that used elements are not deleted.
1057 def test_upload_delete_invalid
1058 changeset = create(:changeset)
1059 relation = create(:relation)
1060 other_relation = create(:relation)
1061 used_way = create(:way)
1062 used_node = create(:node)
1063 create(:relation_member, :relation => relation, :member => used_way)
1064 create(:relation_member, :relation => relation, :member => used_node)
1066 auth_header = bearer_authorization_header changeset.user
1068 diff = XML::Document.new
1069 diff.root = XML::Node.new "osmChange"
1070 delete = XML::Node.new "delete"
1072 delete << xml_node_for_relation(other_relation)
1073 delete << xml_node_for_way(used_way)
1074 delete << xml_node_for_node(used_node)
1076 # update the changeset to one that this user owns
1077 %w[node way relation].each do |type|
1078 delete.find("//osmChange/delete/#{type}").each do |n|
1079 n["changeset"] = changeset.id.to_s
1084 post changeset_upload_path(changeset), :params => diff.to_s, :headers => auth_header
1085 assert_response :precondition_failed,
1086 "shouldn't be able to upload a invalid deletion diff: #{@response.body}"
1087 assert_equal "Precondition failed: Way #{used_way.id} is still used by relations #{relation.id}.", @response.body
1089 # check that nothing was, in fact, deleted
1090 assert Node.find(used_node.id).visible
1091 assert Way.find(used_way.id).visible
1092 assert Relation.find(relation.id).visible
1093 assert Relation.find(other_relation.id).visible
1097 # test that a conditional delete of an in use object works.
1098 def test_upload_delete_if_unused
1099 changeset = create(:changeset)
1100 super_relation = create(:relation)
1101 used_relation = create(:relation)
1102 used_way = create(:way)
1103 used_node = create(:node)
1104 create(:relation_member, :relation => super_relation, :member => used_relation)
1105 create(:relation_member, :relation => super_relation, :member => used_way)
1106 create(:relation_member, :relation => super_relation, :member => used_node)
1108 auth_header = bearer_authorization_header changeset.user
1110 diff = XML::Document.new
1111 diff.root = XML::Node.new "osmChange"
1112 delete = XML::Node.new "delete"
1114 delete["if-unused"] = ""
1115 delete << xml_node_for_relation(used_relation)
1116 delete << xml_node_for_way(used_way)
1117 delete << xml_node_for_node(used_node)
1119 # update the changeset to one that this user owns
1120 %w[node way relation].each do |type|
1121 delete.find("//osmChange/delete/#{type}").each do |n|
1122 n["changeset"] = changeset.id.to_s
1127 post changeset_upload_path(changeset), :params => diff.to_s, :headers => auth_header
1128 assert_response :success,
1129 "can't do a conditional delete of in use objects: #{@response.body}"
1131 # check the returned payload
1132 assert_dom "diffResult[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1 do
1133 # check the old IDs are all present and what we expect
1134 # check the new IDs are all present and unchanged
1135 # check the new versions are all present and unchanged
1136 assert_dom "> node", 1 do
1137 assert_dom "> @old_id", used_node.id.to_s
1138 assert_dom "> @new_id", used_node.id.to_s
1139 assert_dom "> @new_version", used_node.version.to_s
1141 assert_dom "> way", 1 do
1142 assert_dom "> @old_id", used_way.id.to_s
1143 assert_dom "> @new_id", used_way.id.to_s
1144 assert_dom "> @new_version", used_way.version.to_s
1146 assert_dom "> relation", 1 do
1147 assert_dom "> @old_id", used_relation.id.to_s
1148 assert_dom "> @new_id", used_relation.id.to_s
1149 assert_dom "> @new_version", used_relation.version.to_s
1153 # check that nothing was, in fact, deleted
1154 assert Node.find(used_node.id).visible
1155 assert Way.find(used_way.id).visible
1156 assert Relation.find(used_relation.id).visible
1160 # upload an element with a really long tag value
1161 def test_upload_invalid_too_long_tag
1162 changeset = create(:changeset)
1164 auth_header = bearer_authorization_header changeset.user
1166 # simple diff to create a node way and relation using placeholders
1170 <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
1171 <tag k='foo' v='#{'x' * 256}'/>
1178 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1179 assert_response :bad_request,
1180 "shouldn't be able to upload too long a tag to changeset: #{@response.body}"
1184 # upload something which creates new objects and inserts them into
1185 # existing containers using placeholders.
1186 def test_upload_complex
1188 node = create(:node)
1189 relation = create(:relation)
1190 create(:way_node, :way => way, :node => node)
1192 changeset = create(:changeset)
1194 auth_header = bearer_authorization_header changeset.user
1196 # simple diff to create a node way and relation using placeholders
1200 <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
1201 <tag k='foo' v='bar'/>
1202 <tag k='baz' v='bat'/>
1206 <way id='#{way.id}' changeset='#{changeset.id}' version='1'>
1208 <nd ref='#{node.id}'/>
1210 <relation id='#{relation.id}' changeset='#{changeset.id}' version='1'>
1211 <member type='way' role='some' ref='#{way.id}'/>
1212 <member type='node' role='some' ref='-1'/>
1213 <member type='relation' role='some' ref='#{relation.id}'/>
1220 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1221 assert_response :success,
1222 "can't upload a complex diff to changeset: #{@response.body}"
1224 # check the returned payload
1226 assert_dom "diffResult[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1 do
1227 assert_dom "> node", 1 do |(node_el)|
1228 new_node_id = node_el["new_id"].to_i
1230 assert_dom "> way", 1
1231 assert_dom "> relation", 1
1234 # check that the changes made it into the database
1235 assert_equal 2, Node.find(new_node_id).tags.size, "new node should have two tags"
1236 assert_equal [new_node_id, node.id], Way.find(way.id).nds, "way nodes should match"
1237 Relation.find(relation.id).members.each do |type, id, _role|
1238 assert_equal new_node_id, id, "relation should contain new node" if type == "node"
1243 # create a diff which references several changesets, which should cause
1244 # a rollback and none of the diff gets committed
1245 def test_upload_invalid_changesets
1246 changeset = create(:changeset)
1247 other_changeset = create(:changeset, :user => changeset.user)
1248 node = create(:node)
1250 relation = create(:relation)
1251 other_relation = create(:relation)
1253 auth_header = bearer_authorization_header changeset.user
1255 # simple diff to create a node way and relation using placeholders
1259 <node id='#{node.id}' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
1260 <way id='#{way.id}' changeset='#{changeset.id}' version='1'>
1261 <nd ref='#{node.id}'/>
1265 <relation id='#{relation.id}' changeset='#{changeset.id}' version='1'>
1266 <member type='way' role='some' ref='#{way.id}'/>
1267 <member type='node' role='some' ref='#{node.id}'/>
1268 <member type='relation' role='some' ref='#{other_relation.id}'/>
1272 <node id='-1' lon='0' lat='0' changeset='#{other_changeset.id}'>
1273 <tag k='foo' v='bar'/>
1274 <tag k='baz' v='bat'/>
1281 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1282 assert_response :conflict,
1283 "uploading a diff with multiple changesets should have failed"
1285 # check that objects are unmodified
1286 assert_nodes_are_equal(node, Node.find(node.id))
1287 assert_ways_are_equal(way, Way.find(way.id))
1288 assert_relations_are_equal(relation, Relation.find(relation.id))
1292 # upload multiple versions of the same element in the same diff.
1293 def test_upload_multiple_valid
1294 node = create(:node)
1295 changeset = create(:changeset)
1296 auth_header = bearer_authorization_header changeset.user
1298 # change the location of a node multiple times, each time referencing
1299 # the last version. doesn't this depend on version numbers being
1304 <node id='#{node.id}' lon='0.0' lat='0.0' changeset='#{changeset.id}' version='1'/>
1305 <node id='#{node.id}' lon='0.1' lat='0.0' changeset='#{changeset.id}' version='2'/>
1306 <node id='#{node.id}' lon='0.1' lat='0.1' changeset='#{changeset.id}' version='3'/>
1307 <node id='#{node.id}' lon='0.1' lat='0.2' changeset='#{changeset.id}' version='4'/>
1308 <node id='#{node.id}' lon='0.2' lat='0.2' changeset='#{changeset.id}' version='5'/>
1309 <node id='#{node.id}' lon='0.3' lat='0.2' changeset='#{changeset.id}' version='6'/>
1310 <node id='#{node.id}' lon='0.3' lat='0.3' changeset='#{changeset.id}' version='7'/>
1311 <node id='#{node.id}' lon='0.9' lat='0.9' changeset='#{changeset.id}' version='8'/>
1317 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1318 assert_response :success,
1319 "can't upload multiple versions of an element in a diff: #{@response.body}"
1321 # check the response is well-formed. its counter-intuitive, but the
1322 # API will return multiple elements with the same ID and different
1323 # version numbers for each change we made.
1324 assert_select "diffResult>node", 8
1328 # upload multiple versions of the same element in the same diff, but
1329 # keep the version numbers the same.
1330 def test_upload_multiple_duplicate
1331 node = create(:node)
1332 changeset = create(:changeset)
1334 auth_header = bearer_authorization_header changeset.user
1339 <node id='#{node.id}' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
1340 <node id='#{node.id}' lon='1' lat='1' changeset='#{changeset.id}' version='1'/>
1346 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1347 assert_response :conflict,
1348 "shouldn't be able to upload the same element twice in a diff: #{@response.body}"
1352 # try to upload some elements without specifying the version
1353 def test_upload_missing_version
1354 changeset = create(:changeset)
1356 auth_header = bearer_authorization_header changeset.user
1361 <node id='1' lon='1' lat='1' changeset='#{changeset.id}'/>
1367 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1368 assert_response :bad_request,
1369 "shouldn't be able to upload an element without version: #{@response.body}"
1373 # try to upload with commands other than create, modify, or delete
1374 def test_action_upload_invalid
1375 changeset = create(:changeset)
1377 auth_header = bearer_authorization_header changeset.user
1382 <node id='1' lon='1' lat='1' changeset='#{changeset.id}' />
1386 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1387 assert_response :bad_request, "Shouldn't be able to upload a diff with the action ping"
1388 assert_equal("Unknown action ping, choices are create, modify, delete", @response.body)
1392 # upload a valid changeset which has a mixture of whitespace
1393 # to check a bug reported by ivansanchez (#1565).
1394 def test_upload_whitespace_valid
1395 changeset = create(:changeset)
1396 node = create(:node)
1397 way = create(:way_with_nodes, :nodes_count => 2)
1398 relation = create(:relation)
1399 other_relation = create(:relation)
1400 create(:relation_tag, :relation => relation)
1402 auth_header = bearer_authorization_header changeset.user
1406 <modify><node id='#{node.id}' lon='0' lat='0' changeset='#{changeset.id}'
1408 <node id='#{node.id}' lon='1' lat='1' changeset='#{changeset.id}' version='2'><tag k='k' v='v'/></node></modify>
1410 <relation id='#{relation.id}' changeset='#{changeset.id}' version='1'><member
1411 type='way' role='some' ref='#{way.id}'/><member
1412 type='node' role='some' ref='#{node.id}'/>
1413 <member type='relation' role='some' ref='#{other_relation.id}'/>
1415 </modify></osmChange>
1419 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1420 assert_response :success,
1421 "can't upload a valid diff with whitespace variations to changeset: #{@response.body}"
1423 # check the response is well-formed
1424 assert_select "diffResult>node", 2
1425 assert_select "diffResult>relation", 1
1427 # check that the changes made it into the database
1428 assert_equal 1, Node.find(node.id).tags.size, "node #{node.id} should now have one tag"
1429 assert_equal 0, Relation.find(relation.id).tags.size, "relation #{relation.id} should now have no tags"
1433 # test that a placeholder can be reused within the same upload.
1434 def test_upload_reuse_placeholder_valid
1435 changeset = create(:changeset)
1437 auth_header = bearer_authorization_header changeset.user
1442 <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
1443 <tag k="foo" v="bar"/>
1447 <node id='-1' lon='1' lat='1' changeset='#{changeset.id}' version='1'/>
1450 <node id='-1' lon='2' lat='2' changeset='#{changeset.id}' version='2'/>
1456 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1457 assert_response :success,
1458 "can't upload a valid diff with re-used placeholders to changeset: #{@response.body}"
1460 # check the response is well-formed
1461 assert_select "diffResult>node", 3
1462 assert_select "diffResult>node[old_id='-1']", 3
1466 # test what happens if a diff upload re-uses placeholder IDs in an
1468 def test_upload_placeholder_invalid
1469 changeset = create(:changeset)
1471 auth_header = bearer_authorization_header changeset.user
1476 <node id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
1477 <node id='-1' lon='1' lat='1' changeset='#{changeset.id}' version='1'/>
1478 <node id='-1' lon='2' lat='2' changeset='#{changeset.id}' version='2'/>
1484 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1485 assert_response :bad_request,
1486 "shouldn't be able to re-use placeholder IDs"
1488 # placeholder_ids must be unique across all action blocks
1492 <node id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
1495 <node id='-1' lon='1' lat='1' changeset='#{changeset.id}' version='1'/>
1501 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1502 assert_response :bad_request,
1503 "shouldn't be able to re-use placeholder IDs"
1506 def test_upload_process_order
1507 changeset = create(:changeset)
1509 auth_header = bearer_authorization_header changeset.user
1514 <node id="-1" lat="1" lon="2" changeset="#{changeset.id}"/>
1515 <way id="-1" changeset="#{changeset.id}">
1519 <node id="-2" lat="1" lon="2" changeset="#{changeset.id}"/>
1525 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1526 assert_response :bad_request,
1527 "shouldn't refer elements behind it"
1530 def test_upload_duplicate_delete
1531 changeset = create(:changeset)
1533 auth_header = bearer_authorization_header changeset.user
1538 <node id="-1" lat="39" lon="116" changeset="#{changeset.id}" />
1541 <node id="-1" version="1" changeset="#{changeset.id}" />
1542 <node id="-1" version="1" changeset="#{changeset.id}" />
1548 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1549 assert_response :gone,
1550 "transaction should be cancelled by second deletion"
1555 <node id="-1" lat="39" lon="116" changeset="#{changeset.id}" />
1557 <delete if-unused="true">
1558 <node id="-1" version="1" changeset="#{changeset.id}" />
1559 <node id="-1" version="1" changeset="#{changeset.id}" />
1565 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1567 assert_select "diffResult>node", 3
1568 assert_select "diffResult>node[old_id='-1']", 3
1569 assert_select "diffResult>node[new_version='1']", 1
1570 assert_select "diffResult>node[new_version='2']", 1
1574 # test that uploading a way referencing invalid placeholders gives a
1575 # proper error, not a 500.
1576 def test_upload_placeholder_invalid_way
1577 changeset = create(:changeset)
1580 auth_header = bearer_authorization_header changeset.user
1585 <node id="-1" lon="0.0" lat="0.0" changeset="#{changeset.id}" version="1"/>
1586 <node id="-2" lon="0.1" lat="0.1" changeset="#{changeset.id}" version="1"/>
1587 <node id="-3" lon="0.2" lat="0.2" changeset="#{changeset.id}" version="1"/>
1588 <way id="-1" changeset="#{changeset.id}" version="1">
1599 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1600 assert_response :bad_request,
1601 "shouldn't be able to use invalid placeholder IDs"
1602 assert_equal "Placeholder node not found for reference -4 in way -1", @response.body
1604 # the same again, but this time use an existing way
1608 <node id="-1" lon="0.0" lat="0.0" changeset="#{changeset.id}" version="1"/>
1609 <node id="-2" lon="0.1" lat="0.1" changeset="#{changeset.id}" version="1"/>
1610 <node id="-3" lon="0.2" lat="0.2" changeset="#{changeset.id}" version="1"/>
1611 <way id="#{way.id}" changeset="#{changeset.id}" version="1">
1622 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1623 assert_response :bad_request,
1624 "shouldn't be able to use invalid placeholder IDs"
1625 assert_equal "Placeholder node not found for reference -4 in way #{way.id}", @response.body
1629 # test that uploading a relation referencing invalid placeholders gives a
1630 # proper error, not a 500.
1631 def test_upload_placeholder_invalid_relation
1632 changeset = create(:changeset)
1633 relation = create(:relation)
1635 auth_header = bearer_authorization_header changeset.user
1640 <node id="-1" lon="0.0" lat="0.0" changeset="#{changeset.id}" version="1"/>
1641 <node id="-2" lon="0.1" lat="0.1" changeset="#{changeset.id}" version="1"/>
1642 <node id="-3" lon="0.2" lat="0.2" changeset="#{changeset.id}" version="1"/>
1643 <relation id="-1" changeset="#{changeset.id}" version="1">
1644 <member type="node" role="foo" ref="-1"/>
1645 <member type="node" role="foo" ref="-2"/>
1646 <member type="node" role="foo" ref="-3"/>
1647 <member type="node" role="foo" ref="-4"/>
1654 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1655 assert_response :bad_request,
1656 "shouldn't be able to use invalid placeholder IDs"
1657 assert_equal "Placeholder Node not found for reference -4 in relation -1.", @response.body
1659 # the same again, but this time use an existing relation
1663 <node id="-1" lon="0.0" lat="0.0" changeset="#{changeset.id}" version="1"/>
1664 <node id="-2" lon="0.1" lat="0.1" changeset="#{changeset.id}" version="1"/>
1665 <node id="-3" lon="0.2" lat="0.2" changeset="#{changeset.id}" version="1"/>
1666 <relation id="#{relation.id}" changeset="#{changeset.id}" version="1">
1667 <member type="node" role="foo" ref="-1"/>
1668 <member type="node" role="foo" ref="-2"/>
1669 <member type="node" role="foo" ref="-3"/>
1670 <member type="way" role="bar" ref="-1"/>
1677 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1678 assert_response :bad_request,
1679 "shouldn't be able to use invalid placeholder IDs"
1680 assert_equal "Placeholder Way not found for reference -1 in relation #{relation.id}.", @response.body
1684 # test what happens if a diff is uploaded containing only a node
1686 def test_upload_node_move
1687 auth_header = bearer_authorization_header
1689 xml = "<osm><changeset>" \
1690 "<tag k='created_by' v='osm test suite checking changesets'/>" \
1691 "</changeset></osm>"
1692 post api_changesets_path, :params => xml, :headers => auth_header
1693 assert_response :success
1694 changeset_id = @response.body.to_i
1696 old_node = create(:node, :lat => 1, :lon => 1)
1698 diff = XML::Document.new
1699 diff.root = XML::Node.new "osmChange"
1700 modify = XML::Node.new "modify"
1701 xml_old_node = xml_node_for_node(old_node)
1702 xml_old_node["lat"] = 2.0.to_s
1703 xml_old_node["lon"] = 2.0.to_s
1704 xml_old_node["changeset"] = changeset_id.to_s
1705 modify << xml_old_node
1709 post changeset_upload_path(changeset_id), :params => diff.to_s, :headers => auth_header
1710 assert_response :success,
1711 "diff should have uploaded OK"
1714 changeset = Changeset.find(changeset_id)
1715 assert_equal 1 * GeoRecord::SCALE, changeset.min_lon, "min_lon should be 1 degree"
1716 assert_equal 2 * GeoRecord::SCALE, changeset.max_lon, "max_lon should be 2 degrees"
1717 assert_equal 1 * GeoRecord::SCALE, changeset.min_lat, "min_lat should be 1 degree"
1718 assert_equal 2 * GeoRecord::SCALE, changeset.max_lat, "max_lat should be 2 degrees"
1722 # test what happens if a diff is uploaded adding a node to a way.
1723 def test_upload_way_extend
1724 auth_header = bearer_authorization_header
1726 xml = "<osm><changeset>" \
1727 "<tag k='created_by' v='osm test suite checking changesets'/>" \
1728 "</changeset></osm>"
1729 post api_changesets_path, :params => xml, :headers => auth_header
1730 assert_response :success
1731 changeset_id = @response.body.to_i
1733 old_way = create(:way)
1734 create(:way_node, :way => old_way, :node => create(:node, :lat => 0.1, :lon => 0.1))
1736 diff = XML::Document.new
1737 diff.root = XML::Node.new "osmChange"
1738 modify = XML::Node.new "modify"
1739 xml_old_way = xml_node_for_way(old_way)
1740 nd_ref = XML::Node.new "nd"
1741 nd_ref["ref"] = create(:node, :lat => 0.3, :lon => 0.3).id.to_s
1742 xml_old_way << nd_ref
1743 xml_old_way["changeset"] = changeset_id.to_s
1744 modify << xml_old_way
1748 post changeset_upload_path(changeset_id), :params => diff.to_s, :headers => auth_header
1749 assert_response :success,
1750 "diff should have uploaded OK"
1753 changeset = Changeset.find(changeset_id)
1754 assert_equal 0.1 * GeoRecord::SCALE, changeset.min_lon, "min_lon should be 0.1 degree"
1755 assert_equal 0.3 * GeoRecord::SCALE, changeset.max_lon, "max_lon should be 0.3 degrees"
1756 assert_equal 0.1 * GeoRecord::SCALE, changeset.min_lat, "min_lat should be 0.1 degree"
1757 assert_equal 0.3 * GeoRecord::SCALE, changeset.max_lat, "max_lat should be 0.3 degrees"
1761 # test for more issues in #1568
1762 def test_upload_empty_invalid
1763 changeset = create(:changeset)
1765 auth_header = bearer_authorization_header changeset.user
1768 "<osmChange></osmChange>",
1769 "<osmChange><modify/></osmChange>",
1770 "<osmChange><modify></modify></osmChange>"].each do |diff|
1772 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1773 assert_response(:success, "should be able to upload " \
1774 "empty changeset: " + diff)
1779 # test that the X-Error-Format header works to request XML errors
1780 def test_upload_xml_errors
1781 changeset = create(:changeset)
1782 node = create(:node)
1783 create(:relation_member, :member => node)
1785 auth_header = bearer_authorization_header changeset.user
1787 # try and delete a node that is in use
1788 diff = XML::Document.new
1789 diff.root = XML::Node.new "osmChange"
1790 delete = XML::Node.new "delete"
1792 delete << xml_node_for_node(node)
1795 error_header = error_format_header "xml"
1796 post changeset_upload_path(changeset), :params => diff.to_s, :headers => auth_header.merge(error_header)
1797 assert_response :success,
1798 "failed to return error in XML format"
1800 # check the returned payload
1801 assert_select "osmError[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
1802 assert_select "osmError>status", 1
1803 assert_select "osmError>message", 1
1806 def test_upload_not_found
1807 changeset = create(:changeset)
1809 auth_header = bearer_authorization_header changeset.user
1815 <node id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
1821 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1822 assert_response :not_found, "Node should not be found"
1828 <way id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
1834 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1835 assert_response :not_found, "Way should not be found"
1841 <relation id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
1847 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1848 assert_response :not_found, "Relation should not be found"
1854 <node id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
1860 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1861 assert_response :not_found, "Node should not be deleted"
1867 <way id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
1873 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1874 assert_response :not_found, "Way should not be deleted"
1880 <relation id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
1886 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1887 assert_response :not_found, "Relation should not be deleted"
1890 def test_upload_relation_placeholder_not_fix
1891 changeset = create(:changeset)
1893 auth_header = bearer_authorization_header changeset.user
1897 <osmChange version='0.6'>
1899 <relation id='-2' version='0' changeset='#{changeset.id}'>
1900 <member type='relation' role='' ref='-4' />
1901 <tag k='type' v='route' />
1902 <tag k='name' v='AtoB' />
1904 <relation id='-3' version='0' changeset='#{changeset.id}'>
1905 <tag k='type' v='route' />
1906 <tag k='name' v='BtoA' />
1908 <relation id='-4' version='0' changeset='#{changeset.id}'>
1909 <member type='relation' role='' ref='-2' />
1910 <member type='relation' role='' ref='-3' />
1911 <tag k='type' v='route_master' />
1912 <tag k='name' v='master' />
1919 post changeset_upload_path(changeset), :params => diff.to_s, :headers => auth_header
1920 assert_response :bad_request, "shouldn't be able to use reference -4 in relation -2: #{@response.body}"
1921 assert_equal "Placeholder Relation not found for reference -4 in relation -2.", @response.body
1924 def test_upload_multiple_delete_block
1925 changeset = create(:changeset)
1927 auth_header = bearer_authorization_header changeset.user
1929 node = create(:node)
1931 create(:way_node, :way => way, :node => node)
1932 alone_node = create(:node)
1936 <osmChange version='0.6'>
1937 <delete version="0.6">
1938 <node id="#{node.id}" version="#{node.version}" changeset="#{changeset.id}"/>
1940 <delete version="0.6" if-unused="true">
1941 <node id="#{alone_node.id}" version="#{alone_node.version}" changeset="#{changeset.id}"/>
1947 post changeset_upload_path(changeset), :params => diff.to_s, :headers => auth_header
1948 assert_response :precondition_failed,
1949 "shouldn't be able to upload a invalid deletion diff: #{@response.body}"
1950 assert_equal "Precondition failed: Node #{node.id} is still used by ways #{way.id}.", @response.body
1954 # test initial rate limit
1955 def test_upload_initial_rate_limit
1957 user = create(:user)
1959 # create some objects to use
1960 node = create(:node)
1961 way = create(:way_with_nodes, :nodes_count => 2)
1962 relation = create(:relation)
1964 # create a changeset that puts us near the initial rate limit
1965 changeset = create(:changeset, :user => user,
1966 :created_at => Time.now.utc - 5.minutes,
1967 :num_changes => Settings.initial_changes_per_hour - 2)
1969 # create authentication header
1970 auth_header = bearer_authorization_header user
1972 # simple diff to create a node way and relation using placeholders
1976 <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
1977 <tag k='foo' v='bar'/>
1978 <tag k='baz' v='bat'/>
1980 <way id='-1' changeset='#{changeset.id}'>
1981 <nd ref='#{node.id}'/>
1985 <relation id='-1' changeset='#{changeset.id}'>
1986 <member type='way' role='some' ref='#{way.id}'/>
1987 <member type='node' role='some' ref='#{node.id}'/>
1988 <member type='relation' role='some' ref='#{relation.id}'/>
1995 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1996 assert_response :too_many_requests, "upload did not hit rate limit"
2000 # test maximum rate limit
2001 def test_upload_maximum_rate_limit
2003 user = create(:user)
2005 # create some objects to use
2006 node = create(:node)
2007 way = create(:way_with_nodes, :nodes_count => 2)
2008 relation = create(:relation)
2010 # create a changeset to establish our initial edit time
2011 changeset = create(:changeset, :user => user,
2012 :created_at => Time.now.utc - 28.days)
2014 # create changeset to put us near the maximum rate limit
2015 total_changes = Settings.max_changes_per_hour - 2
2016 while total_changes.positive?
2017 changes = [total_changes, Changeset::MAX_ELEMENTS].min
2018 changeset = create(:changeset, :user => user,
2019 :created_at => Time.now.utc - 5.minutes,
2020 :num_changes => changes)
2021 total_changes -= changes
2024 # create authentication header
2025 auth_header = bearer_authorization_header user
2027 # simple diff to create a node way and relation using placeholders
2031 <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
2032 <tag k='foo' v='bar'/>
2033 <tag k='baz' v='bat'/>
2035 <way id='-1' changeset='#{changeset.id}'>
2036 <nd ref='#{node.id}'/>
2040 <relation id='-1' changeset='#{changeset.id}'>
2041 <member type='way' role='some' ref='#{way.id}'/>
2042 <member type='node' role='some' ref='#{node.id}'/>
2043 <member type='relation' role='some' ref='#{relation.id}'/>
2050 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
2051 assert_response :too_many_requests, "upload did not hit rate limit"
2055 # test initial size limit
2056 def test_upload_initial_size_limit
2058 user = create(:user)
2060 # create a changeset that puts us near the initial size limit
2061 changeset = create(:changeset, :user => user,
2062 :min_lat => (-0.5 * GeoRecord::SCALE).round, :min_lon => (0.5 * GeoRecord::SCALE).round,
2063 :max_lat => (0.5 * GeoRecord::SCALE).round, :max_lon => (2.5 * GeoRecord::SCALE).round)
2065 # create authentication header
2066 auth_header = bearer_authorization_header user
2068 # simple diff to create a node
2072 <node id='-1' lon='0.9' lat='2.9' changeset='#{changeset.id}'>
2073 <tag k='foo' v='bar'/>
2074 <tag k='baz' v='bat'/>
2081 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
2082 assert_response :payload_too_large, "upload did not hit size limit"
2086 # test size limit after one week
2087 def test_upload_week_size_limit
2089 user = create(:user)
2091 # create a changeset to establish our initial edit time
2092 create(:changeset, :user => user, :created_at => Time.now.utc - 7.days)
2094 # create a changeset that puts us near the initial size limit
2095 changeset = create(:changeset, :user => user,
2096 :min_lat => (-0.5 * GeoRecord::SCALE).round, :min_lon => (0.5 * GeoRecord::SCALE).round,
2097 :max_lat => (0.5 * GeoRecord::SCALE).round, :max_lon => (2.5 * GeoRecord::SCALE).round)
2099 # create authentication header
2100 auth_header = bearer_authorization_header user
2102 # simple diff to create a node way and relation using placeholders
2106 <node id='-1' lon='35' lat='35' changeset='#{changeset.id}'>
2107 <tag k='foo' v='bar'/>
2108 <tag k='baz' v='bat'/>
2115 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
2116 assert_response :payload_too_large, "upload did not hit size limit"
2120 # when we make some simple changes we get the same changes back from the
2122 def test_diff_download_simple
2123 node = create(:node)
2125 ## First try with a non-public user, which should get a forbidden
2126 auth_header = bearer_authorization_header create(:user, :data_public => false)
2128 # create a temporary changeset
2129 xml = "<osm><changeset>" \
2130 "<tag k='created_by' v='osm test suite checking changesets'/>" \
2131 "</changeset></osm>"
2132 post api_changesets_path, :params => xml, :headers => auth_header
2133 assert_response :forbidden
2135 ## Now try with a normal user
2136 auth_header = bearer_authorization_header
2138 # create a temporary changeset
2139 xml = "<osm><changeset>" \
2140 "<tag k='created_by' v='osm test suite checking changesets'/>" \
2141 "</changeset></osm>"
2142 post api_changesets_path, :params => xml, :headers => auth_header
2143 assert_response :success
2144 changeset_id = @response.body.to_i
2150 <node id='#{node.id}' lon='0.0' lat='0.0' changeset='#{changeset_id}' version='1'/>
2151 <node id='#{node.id}' lon='0.1' lat='0.0' changeset='#{changeset_id}' version='2'/>
2152 <node id='#{node.id}' lon='0.1' lat='0.1' changeset='#{changeset_id}' version='3'/>
2153 <node id='#{node.id}' lon='0.1' lat='0.2' changeset='#{changeset_id}' version='4'/>
2154 <node id='#{node.id}' lon='0.2' lat='0.2' changeset='#{changeset_id}' version='5'/>
2155 <node id='#{node.id}' lon='0.3' lat='0.2' changeset='#{changeset_id}' version='6'/>
2156 <node id='#{node.id}' lon='0.3' lat='0.3' changeset='#{changeset_id}' version='7'/>
2157 <node id='#{node.id}' lon='0.9' lat='0.9' changeset='#{changeset_id}' version='8'/>
2163 post changeset_upload_path(changeset_id), :params => diff, :headers => auth_header
2164 assert_response :success,
2165 "can't upload multiple versions of an element in a diff: #{@response.body}"
2167 get api_changeset_download_path(changeset_id)
2168 assert_response :success
2170 assert_select "osmChange", 1
2171 assert_select "osmChange>modify", 8
2172 assert_select "osmChange>modify>node", 8
2176 # culled this from josm to ensure that nothing in the way that josm
2177 # is formatting the request is causing it to fail.
2179 # NOTE: the error turned out to be something else completely!
2180 def test_josm_upload
2181 auth_header = bearer_authorization_header
2183 # create a temporary changeset
2184 xml = "<osm><changeset>" \
2185 "<tag k='created_by' v='osm test suite checking changesets'/>" \
2186 "</changeset></osm>"
2187 post api_changesets_path, :params => xml, :headers => auth_header
2188 assert_response :success
2189 changeset_id = @response.body.to_i
2192 <osmChange version="0.6" generator="JOSM">
2193 <create version="0.6" generator="JOSM">
2194 <node id='-1' visible='true' changeset='#{changeset_id}' lat='51.49619982187321' lon='-0.18722061869438314' />
2195 <node id='-2' visible='true' changeset='#{changeset_id}' lat='51.496359883909605' lon='-0.18653093576241928' />
2196 <node id='-3' visible='true' changeset='#{changeset_id}' lat='51.49598132358285' lon='-0.18719613290981638' />
2197 <node id='-4' visible='true' changeset='#{changeset_id}' lat='51.4961591711078' lon='-0.18629015888084607' />
2198 <node id='-5' visible='true' changeset='#{changeset_id}' lat='51.49582126021711' lon='-0.18708186591517145' />
2199 <node id='-6' visible='true' changeset='#{changeset_id}' lat='51.49591018437858' lon='-0.1861432441734455' />
2200 <node id='-7' visible='true' changeset='#{changeset_id}' lat='51.49560784152179' lon='-0.18694719410005425' />
2201 <node id='-8' visible='true' changeset='#{changeset_id}' lat='51.49567389979617' lon='-0.1860289771788006' />
2202 <node id='-9' visible='true' changeset='#{changeset_id}' lat='51.49543761398892' lon='-0.186820684213126' />
2203 <way id='-10' action='modify' visible='true' changeset='#{changeset_id}'>
2213 <tag k='highway' v='residential' />
2214 <tag k='name' v='Foobar Street' />
2221 post changeset_upload_path(changeset_id), :params => diff, :headers => auth_header
2222 assert_response :success,
2223 "can't upload a diff from JOSM: #{@response.body}"
2225 get api_changeset_download_path(changeset_id)
2226 assert_response :success
2228 assert_select "osmChange", 1
2229 assert_select "osmChange>create>node", 9
2230 assert_select "osmChange>create>way", 1
2231 assert_select "osmChange>create>way>nd", 9
2232 assert_select "osmChange>create>way>tag", 2
2236 # when we make some complex changes we get the same changes back from the
2238 def test_diff_download_complex
2239 node = create(:node)
2240 node2 = create(:node)
2242 auth_header = bearer_authorization_header
2244 # create a temporary changeset
2245 xml = "<osm><changeset>" \
2246 "<tag k='created_by' v='osm test suite checking changesets'/>" \
2247 "</changeset></osm>"
2248 post api_changesets_path, :params => xml, :headers => auth_header
2249 assert_response :success
2250 changeset_id = @response.body.to_i
2256 <node id='#{node.id}' lon='0.0' lat='0.0' changeset='#{changeset_id}' version='1'/>
2259 <node id='-1' lon='0.9' lat='0.9' changeset='#{changeset_id}' version='0'/>
2260 <node id='-2' lon='0.8' lat='0.9' changeset='#{changeset_id}' version='0'/>
2261 <node id='-3' lon='0.7' lat='0.9' changeset='#{changeset_id}' version='0'/>
2264 <node id='#{node2.id}' lon='2.0' lat='1.5' changeset='#{changeset_id}' version='1'/>
2265 <way id='#{way.id}' changeset='#{changeset_id}' version='1'>
2266 <nd ref='#{node2.id}'/>
2276 post changeset_upload_path(changeset_id), :params => diff, :headers => auth_header
2277 assert_response :success,
2278 "can't upload multiple versions of an element in a diff: #{@response.body}"
2280 get api_changeset_download_path(changeset_id)
2281 assert_response :success
2283 assert_select "osmChange", 1
2284 assert_select "osmChange>create", 3
2285 assert_select "osmChange>delete", 1
2286 assert_select "osmChange>modify", 2
2287 assert_select "osmChange>create>node", 3
2288 assert_select "osmChange>delete>node", 1
2289 assert_select "osmChange>modify>node", 1
2290 assert_select "osmChange>modify>way", 1
2294 # check that the bounding box of a changeset gets updated correctly
2295 # FIXME: This should really be moded to a integration test due to the with_controller
2296 def test_changeset_bbox
2298 create(:way_node, :way => way, :node => create(:node, :lat => 0.3, :lon => 0.3))
2300 auth_header = bearer_authorization_header
2302 # create a new changeset
2303 xml = "<osm><changeset/></osm>"
2304 post api_changesets_path, :params => xml, :headers => auth_header
2305 assert_response :success, "Creating of changeset failed."
2306 changeset_id = @response.body.to_i
2308 # add a single node to it
2309 with_controller(NodesController.new) do
2310 xml = "<osm><node lon='0.1' lat='0.2' changeset='#{changeset_id}'/></osm>"
2311 post api_nodes_path, :params => xml, :headers => auth_header
2312 assert_response :success, "Couldn't create node."
2315 # get the bounding box back from the changeset
2316 get api_changeset_path(changeset_id)
2317 assert_response :success, "Couldn't read back changeset."
2318 assert_select "osm>changeset[min_lon='0.1000000']", 1
2319 assert_select "osm>changeset[max_lon='0.1000000']", 1
2320 assert_select "osm>changeset[min_lat='0.2000000']", 1
2321 assert_select "osm>changeset[max_lat='0.2000000']", 1
2323 # add another node to it
2324 with_controller(NodesController.new) do
2325 xml = "<osm><node lon='0.2' lat='0.1' changeset='#{changeset_id}'/></osm>"
2326 post api_nodes_path, :params => xml, :headers => auth_header
2327 assert_response :success, "Couldn't create second node."
2330 # get the bounding box back from the changeset
2331 get api_changeset_path(changeset_id)
2332 assert_response :success, "Couldn't read back changeset for the second time."
2333 assert_select "osm>changeset[min_lon='0.1000000']", 1
2334 assert_select "osm>changeset[max_lon='0.2000000']", 1
2335 assert_select "osm>changeset[min_lat='0.1000000']", 1
2336 assert_select "osm>changeset[max_lat='0.2000000']", 1
2338 # add (delete) a way to it, which contains a point at (3,3)
2339 with_controller(WaysController.new) do
2340 xml = update_changeset(xml_for_way(way), changeset_id)
2341 delete api_way_path(way), :params => xml.to_s, :headers => auth_header
2342 assert_response :success, "Couldn't delete a way."
2345 # get the bounding box back from the changeset
2346 get api_changeset_path(changeset_id)
2347 assert_response :success, "Couldn't read back changeset for the third time."
2348 assert_select "osm>changeset[min_lon='0.1000000']", 1
2349 assert_select "osm>changeset[max_lon='0.3000000']", 1
2350 assert_select "osm>changeset[min_lat='0.1000000']", 1
2351 assert_select "osm>changeset[max_lat='0.3000000']", 1
2355 # check updating tags on a changeset
2356 def test_changeset_update
2357 private_user = create(:user, :data_public => false)
2358 private_changeset = create(:changeset, :user => private_user)
2359 user = create(:user)
2360 changeset = create(:changeset, :user => user)
2362 ## First try with a non-public user
2363 new_changeset = create_changeset_xml(:user => private_user)
2364 new_tag = XML::Node.new "tag"
2365 new_tag["k"] = "tagtesting"
2366 new_tag["v"] = "valuetesting"
2367 new_changeset.find("//osm/changeset").first << new_tag
2369 # try without any authorization
2370 put api_changeset_path(private_changeset), :params => new_changeset.to_s
2371 assert_response :unauthorized
2373 # try with the wrong authorization
2374 auth_header = bearer_authorization_header
2375 put api_changeset_path(private_changeset), :params => new_changeset.to_s, :headers => auth_header
2376 assert_response :conflict
2378 # now this should get an unauthorized
2379 auth_header = bearer_authorization_header private_user
2380 put api_changeset_path(private_changeset), :params => new_changeset.to_s, :headers => auth_header
2381 assert_require_public_data "user with their data non-public, shouldn't be able to edit their changeset"
2383 ## Now try with the public user
2384 new_changeset = create_changeset_xml(:id => 1)
2385 new_tag = XML::Node.new "tag"
2386 new_tag["k"] = "tagtesting"
2387 new_tag["v"] = "valuetesting"
2388 new_changeset.find("//osm/changeset").first << new_tag
2390 # try without any authorization
2391 put api_changeset_path(changeset), :params => new_changeset.to_s
2392 assert_response :unauthorized
2394 # try with the wrong authorization
2395 auth_header = bearer_authorization_header
2396 put api_changeset_path(changeset), :params => new_changeset.to_s, :headers => auth_header
2397 assert_response :conflict
2399 # now this should work...
2400 auth_header = bearer_authorization_header user
2401 put api_changeset_path(changeset), :params => new_changeset.to_s, :headers => auth_header
2402 assert_response :success
2404 assert_select "osm>changeset[id='#{changeset.id}']", 1
2405 assert_select "osm>changeset>tag", 1
2406 assert_select "osm>changeset>tag[k='tagtesting'][v='valuetesting']", 1
2410 # check that a user different from the one who opened the changeset
2412 def test_changeset_update_invalid
2413 auth_header = bearer_authorization_header
2415 changeset = create(:changeset)
2416 new_changeset = create_changeset_xml(:user => changeset.user, :id => changeset.id)
2417 new_tag = XML::Node.new "tag"
2418 new_tag["k"] = "testing"
2419 new_tag["v"] = "testing"
2420 new_changeset.find("//osm/changeset").first << new_tag
2422 put api_changeset_path(changeset), :params => new_changeset.to_s, :headers => auth_header
2423 assert_response :conflict
2427 # check that a changeset can contain a certain max number of changes.
2428 ## FIXME should be changed to an integration test due to the with_controller
2429 def test_changeset_limits
2430 user = create(:user)
2431 auth_header = bearer_authorization_header user
2433 # create an old changeset to ensure we have the maximum rate limit
2434 create(:changeset, :user => user, :created_at => Time.now.utc - 28.days)
2436 # open a new changeset
2437 xml = "<osm><changeset/></osm>"
2438 post api_changesets_path, :params => xml, :headers => auth_header
2439 assert_response :success, "can't create a new changeset"
2440 cs_id = @response.body.to_i
2442 # start the counter just short of where the changeset should finish.
2444 # alter the database to set the counter on the changeset directly,
2445 # otherwise it takes about 6 minutes to fill all of them.
2446 changeset = Changeset.find(cs_id)
2447 changeset.num_changes = Changeset::MAX_ELEMENTS - offset
2450 with_controller(NodesController.new) do
2452 xml = "<osm><node changeset='#{cs_id}' lat='0.0' lon='0.0'/></osm>"
2453 post api_nodes_path, :params => xml, :headers => auth_header
2454 assert_response :success, "can't create a new node"
2455 node_id = @response.body.to_i
2457 get api_node_path(node_id)
2458 assert_response :success, "can't read back new node"
2459 node_doc = XML::Parser.string(@response.body).parse
2460 node_xml = node_doc.find("//osm/node").first
2462 # loop until we fill the changeset with nodes
2464 node_xml["lat"] = rand.to_s
2465 node_xml["lon"] = rand.to_s
2466 node_xml["version"] = (i + 1).to_s
2468 put api_node_path(node_id), :params => node_doc.to_s, :headers => auth_header
2469 assert_response :success, "attempt #{i} should have succeeded"
2472 # trying again should fail
2473 node_xml["lat"] = rand.to_s
2474 node_xml["lon"] = rand.to_s
2475 node_xml["version"] = offset.to_s
2477 put api_node_path(node_id), :params => node_doc.to_s, :headers => auth_header
2478 assert_response :conflict, "final attempt should have failed"
2481 changeset = Changeset.find(cs_id)
2482 assert_equal Changeset::MAX_ELEMENTS + 1, changeset.num_changes
2484 # check that the changeset is now closed as well
2485 assert_not(changeset.open?,
2486 "changeset should have been auto-closed by exceeding " \
2493 # check that the output consists of one specific changeset
2494 def assert_single_changeset(changeset, &)
2495 assert_dom "> changeset", 1 do
2496 assert_dom "> @id", changeset.id.to_s
2497 assert_dom "> @created_at", changeset.created_at.xmlschema
2499 assert_dom "> @open", "true"
2500 assert_dom "> @closed_at", 0
2502 assert_dom "> @open", "false"
2503 assert_dom "> @closed_at", changeset.closed_at.xmlschema
2505 assert_dom "> @comments_count", changeset.comments.length.to_s
2506 assert_dom "> @changes_count", changeset.num_changes.to_s
2507 yield if block_given?
2511 def assert_single_changeset_json(changeset, js)
2512 assert_equal changeset.id, js["changeset"]["id"]
2513 assert_equal changeset.created_at.xmlschema, js["changeset"]["created_at"]
2515 assert js["changeset"]["open"]
2516 assert_nil js["changeset"]["closed_at"]
2518 assert_not js["changeset"]["open"]
2519 assert_equal changeset.closed_at.xmlschema, js["changeset"]["closed_at"]
2521 assert_equal changeset.comments.length, js["changeset"]["comments_count"]
2522 assert_equal changeset.num_changes, js["changeset"]["changes_count"]
2526 # check that certain changesets exist in the output in the specified order
2527 def assert_changesets_in_order(changesets)
2528 assert_select "osm>changeset", changesets.size
2529 changesets.each_with_index do |changeset, index|
2530 assert_select "osm>changeset:nth-child(#{index + 1})[id='#{changeset.id}']", 1
2535 # update the changeset_id of a way element
2536 def update_changeset(xml, changeset_id)
2537 xml_attr_rewrite(xml, "changeset", changeset_id)
2541 # update an attribute in a way element
2542 def xml_attr_rewrite(xml, name, value)
2543 xml.find("//osm/way").first[name] = value.to_s
2548 # build XML for changesets
2549 def create_changeset_xml(user: nil, id: nil)
2550 root = XML::Document.new
2551 root.root = XML::Node.new "osm"
2552 cs = XML::Node.new "changeset"
2554 cs["user"] = user.display_name
2555 cs["uid"] = user.id.to_s
2557 cs["id"] = id.to_s if id