4 class ChangesetsControllerTest < ActionDispatch::IntegrationTest
6 # test all routes which lead to this controller
9 { :path => "/api/0.6/changeset/create", :method => :put },
10 { :controller => "api/changesets", :action => "create" }
13 { :path => "/api/0.6/changeset/1/upload", :method => :post },
14 { :controller => "api/changesets", :action => "upload", :id => "1" }
17 { :path => "/api/0.6/changeset/1/download", :method => :get },
18 { :controller => "api/changesets", :action => "download", :id => "1" }
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/subscribe", :method => :post },
30 { :controller => "api/changesets", :action => "subscribe", :id => "1" }
33 { :path => "/api/0.6/changeset/1/subscribe.json", :method => :post },
34 { :controller => "api/changesets", :action => "subscribe", :id => "1", :format => "json" }
37 { :path => "/api/0.6/changeset/1/unsubscribe", :method => :post },
38 { :controller => "api/changesets", :action => "unsubscribe", :id => "1" }
41 { :path => "/api/0.6/changeset/1/unsubscribe.json", :method => :post },
42 { :controller => "api/changesets", :action => "unsubscribe", :id => "1", :format => "json" }
45 { :path => "/api/0.6/changeset/1", :method => :put },
46 { :controller => "api/changesets", :action => "update", :id => "1" }
49 { :path => "/api/0.6/changeset/1/close", :method => :put },
50 { :controller => "api/changesets", :action => "close", :id => "1" }
53 { :path => "/api/0.6/changesets", :method => :get },
54 { :controller => "api/changesets", :action => "index" }
57 { :path => "/api/0.6/changesets.json", :method => :get },
58 { :controller => "api/changesets", :action => "index", :format => "json" }
62 # -----------------------
63 # Test simple changeset creation
64 # -----------------------
67 auth_header = bearer_authorization_header create(:user, :data_public => false)
68 # Create the first user's changeset
69 xml = "<osm><changeset>" \
70 "<tag k='created_by' v='osm test suite checking changesets'/>" \
72 put changeset_create_path, :params => xml, :headers => auth_header
73 assert_require_public_data
75 auth_header = bearer_authorization_header
76 # Create the first user's changeset
77 xml = "<osm><changeset>" \
78 "<tag k='created_by' v='osm test suite checking changesets'/>" \
80 put changeset_create_path, :params => xml, :headers => auth_header
82 assert_response :success, "Creation of changeset did not return success status"
83 newid = @response.body.to_i
85 # check end time, should be an hour ahead of creation time
86 cs = Changeset.find(newid)
87 duration = cs.closed_at - cs.created_at
88 # the difference can either be a rational, or a floating point number
89 # of seconds, depending on the code path taken :-(
90 if duration.instance_of?(Rational)
91 assert_equal Rational(1, 24), duration, "initial idle timeout should be an hour (#{cs.created_at} -> #{cs.closed_at})"
93 # must be number of seconds...
94 assert_equal 3600, duration.round, "initial idle timeout should be an hour (#{cs.created_at} -> #{cs.closed_at})"
97 # checks if uploader was subscribed
98 assert_equal 1, cs.subscribers.length
101 def test_create_invalid
102 auth_header = bearer_authorization_header create(:user, :data_public => false)
103 xml = "<osm><changeset></osm>"
104 put changeset_create_path, :params => xml, :headers => auth_header
105 assert_require_public_data
107 ## Try the public user
108 auth_header = bearer_authorization_header
109 xml = "<osm><changeset></osm>"
110 put changeset_create_path, :params => xml, :headers => auth_header
111 assert_response :bad_request, "creating a invalid changeset should fail"
114 def test_create_invalid_no_content
115 ## First check with no auth
116 put changeset_create_path
117 assert_response :unauthorized, "shouldn't be able to create a changeset with no auth"
119 ## Now try to with a non-public user
120 auth_header = bearer_authorization_header create(:user, :data_public => false)
121 put changeset_create_path, :headers => auth_header
122 assert_require_public_data
124 ## Try an inactive user
125 auth_header = bearer_authorization_header create(:user, :pending)
126 put changeset_create_path, :headers => auth_header
129 ## Now try to use a normal user
130 auth_header = bearer_authorization_header
131 put changeset_create_path, :headers => auth_header
132 assert_response :bad_request, "creating a changeset with no content should fail"
135 def test_create_wrong_method
136 auth_header = bearer_authorization_header
138 get changeset_create_path, :headers => auth_header
139 assert_response :not_found
140 assert_template "rescues/routing_error"
142 post changeset_create_path, :headers => auth_header
143 assert_response :not_found
144 assert_template "rescues/routing_error"
148 # check that the changeset can be shown and returns the correct
149 # document structure.
151 changeset = create(:changeset)
153 get changeset_show_path(changeset)
154 assert_response :success, "cannot get first changeset"
156 assert_dom "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
157 assert_single_changeset changeset do
158 assert_dom "> discussion", 0
161 get changeset_show_path(changeset), :params => { :include_discussion => true }
162 assert_response :success, "cannot get first changeset with comments"
164 assert_dom "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
165 assert_single_changeset changeset do
166 assert_dom "> discussion", 1
167 assert_dom "> discussion > comment", 0
171 def test_show_comments
172 # all comments visible
173 changeset = create(:changeset, :closed)
174 comment1, comment2, comment3 = create_list(:changeset_comment, 3, :changeset_id => changeset.id)
176 get changeset_show_path(changeset), :params => { :include_discussion => true }
177 assert_response :success, "cannot get closed changeset with comments"
179 assert_dom "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1 do
180 assert_single_changeset changeset do
181 assert_dom "> discussion", 1 do
182 assert_dom "> comment", 3 do |dom_comments|
183 assert_dom dom_comments[0], "> @id", comment1.id.to_s
184 assert_dom dom_comments[0], "> @visible", "true"
185 assert_dom dom_comments[1], "> @id", comment2.id.to_s
186 assert_dom dom_comments[1], "> @visible", "true"
187 assert_dom dom_comments[2], "> @id", comment3.id.to_s
188 assert_dom dom_comments[2], "> @visible", "true"
194 # one hidden comment not included because not asked for
195 comment2.update(:visible => false)
198 get changeset_show_path(changeset), :params => { :include_discussion => true }
199 assert_response :success, "cannot get closed changeset with comments"
201 assert_dom "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
202 assert_single_changeset changeset do
203 assert_dom "> discussion", 1 do
204 assert_dom "> comment", 2 do |dom_comments|
205 assert_dom dom_comments[0], "> @id", comment1.id.to_s
206 assert_dom dom_comments[0], "> @visible", "true"
207 assert_dom dom_comments[1], "> @id", comment3.id.to_s
208 assert_dom dom_comments[1], "> @visible", "true"
213 # one hidden comment not included because no permissions
214 get changeset_show_path(changeset), :params => { :include_discussion => true, :show_hidden_comments => true }
215 assert_response :success, "cannot get closed changeset with comments"
217 assert_dom "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
218 assert_single_changeset changeset do
219 assert_dom "> discussion", 1 do
220 assert_dom "> comment", 2 do |dom_comments|
221 assert_dom dom_comments[0], "> @id", comment1.id.to_s
222 assert_dom dom_comments[0], "> @visible", "true"
223 # maybe will show an empty comment element with visible=false in the future
224 assert_dom dom_comments[1], "> @id", comment3.id.to_s
225 assert_dom dom_comments[1], "> @visible", "true"
230 # one hidden comment shown to moderators
231 moderator_user = create(:moderator_user)
232 auth_header = bearer_authorization_header moderator_user
233 get changeset_show_path(changeset), :params => { :include_discussion => true, :show_hidden_comments => true },
234 :headers => auth_header
235 assert_response :success, "cannot get closed changeset with comments"
237 assert_dom "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
238 assert_single_changeset changeset do
239 assert_dom "> discussion", 1 do
240 assert_dom "> comment", 3 do |dom_comments|
241 assert_dom dom_comments[0], "> @id", comment1.id.to_s
242 assert_dom dom_comments[0], "> @visible", "true"
243 assert_dom dom_comments[1], "> @id", comment2.id.to_s
244 assert_dom dom_comments[1], "> @visible", "false"
245 assert_dom dom_comments[2], "> @id", comment3.id.to_s
246 assert_dom dom_comments[2], "> @visible", "true"
253 changeset = create(:changeset, :closed)
254 create(:changeset_tag, :changeset => changeset, :k => "created_by", :v => "JOSM/1.5 (18364)")
255 create(:changeset_tag, :changeset => changeset, :k => "comment", :v => "changeset comment")
257 get changeset_show_path(changeset)
259 assert_response :success
260 assert_dom "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
261 assert_single_changeset changeset do
262 assert_dom "> tag", 2
263 assert_dom "> tag[k='created_by'][v='JOSM/1.5 (18364)']", 1
264 assert_dom "> tag[k='comment'][v='changeset comment']", 1
269 changeset = create(:changeset)
271 get changeset_show_path(changeset), :params => { :format => "json" }
272 assert_response :success, "cannot get first changeset"
274 js = ActiveSupport::JSON.decode(@response.body)
277 assert_equal Settings.api_version, js["version"]
278 assert_equal Settings.generator, js["generator"]
279 assert_single_changeset_json changeset, js
280 assert_nil js["changeset"]["tags"]
281 assert_nil js["changeset"]["comments"]
282 assert_equal changeset.user.id, js["changeset"]["uid"]
283 assert_equal changeset.user.display_name, js["changeset"]["user"]
285 get changeset_show_path(changeset), :params => { :format => "json", :include_discussion => true }
286 assert_response :success, "cannot get first changeset with comments"
288 js = ActiveSupport::JSON.decode(@response.body)
290 assert_equal Settings.api_version, js["version"]
291 assert_equal Settings.generator, js["generator"]
292 assert_single_changeset_json changeset, js
293 assert_nil js["changeset"]["tags"]
294 assert_nil js["changeset"]["min_lat"]
295 assert_nil js["changeset"]["min_lon"]
296 assert_nil js["changeset"]["max_lat"]
297 assert_nil js["changeset"]["max_lon"]
298 assert_equal 0, js["changeset"]["comments"].count
301 def test_show_comments_json
302 # all comments visible
303 changeset = create(:changeset, :closed)
304 comment0, comment1, comment2 = create_list(:changeset_comment, 3, :changeset_id => changeset.id)
306 get changeset_show_path(changeset), :params => { :format => "json", :include_discussion => true }
307 assert_response :success, "cannot get closed changeset with comments"
309 js = ActiveSupport::JSON.decode(@response.body)
311 assert_equal Settings.api_version, js["version"]
312 assert_equal Settings.generator, js["generator"]
313 assert_single_changeset_json changeset, js
314 assert_equal 3, js["changeset"]["comments"].count
315 assert_equal comment0.id, js["changeset"]["comments"][0]["id"]
316 assert js["changeset"]["comments"][0]["visible"]
317 assert_equal comment1.id, js["changeset"]["comments"][1]["id"]
318 assert js["changeset"]["comments"][1]["visible"]
319 assert_equal comment2.id, js["changeset"]["comments"][2]["id"]
320 assert js["changeset"]["comments"][2]["visible"]
322 # one hidden comment not included because not asked for
323 comment1.update(:visible => false)
326 get changeset_show_path(changeset), :params => { :format => "json", :include_discussion => true }
327 assert_response :success, "cannot get closed changeset with comments"
329 js = ActiveSupport::JSON.decode(@response.body)
331 assert_equal Settings.api_version, js["version"]
332 assert_equal Settings.generator, js["generator"]
333 assert_single_changeset_json changeset, js
334 assert_equal 2, js["changeset"]["comments"].count
335 assert_equal comment0.id, js["changeset"]["comments"][0]["id"]
336 assert js["changeset"]["comments"][0]["visible"]
337 assert_equal comment2.id, js["changeset"]["comments"][1]["id"]
338 assert js["changeset"]["comments"][1]["visible"]
340 # one hidden comment not included because no permissions
341 get changeset_show_path(changeset), :params => { :format => "json", :include_discussion => true, :show_hidden_comments => true }
342 assert_response :success, "cannot get closed changeset with comments"
344 js = ActiveSupport::JSON.decode(@response.body)
346 assert_equal Settings.api_version, js["version"]
347 assert_equal Settings.generator, js["generator"]
348 assert_single_changeset_json changeset, js
349 assert_equal 2, js["changeset"]["comments"].count
350 assert_equal comment0.id, js["changeset"]["comments"][0]["id"]
351 assert js["changeset"]["comments"][0]["visible"]
352 # maybe will show an empty comment element with visible=false in the future
353 assert_equal comment2.id, js["changeset"]["comments"][1]["id"]
354 assert js["changeset"]["comments"][1]["visible"]
356 # one hidden comment shown to moderators
357 moderator_user = create(:moderator_user)
358 auth_header = bearer_authorization_header moderator_user
359 get changeset_show_path(changeset), :params => { :format => "json", :include_discussion => true, :show_hidden_comments => true },
360 :headers => auth_header
361 assert_response :success, "cannot get closed changeset with comments"
363 js = ActiveSupport::JSON.decode(@response.body)
365 assert_equal Settings.api_version, js["version"]
366 assert_equal Settings.generator, js["generator"]
367 assert_single_changeset_json changeset, js
368 assert_equal 3, js["changeset"]["comments"].count
369 assert_equal comment0.id, js["changeset"]["comments"][0]["id"]
370 assert js["changeset"]["comments"][0]["visible"]
371 assert_equal comment1.id, js["changeset"]["comments"][1]["id"]
372 assert_not js["changeset"]["comments"][1]["visible"]
373 assert_equal comment2.id, js["changeset"]["comments"][2]["id"]
374 assert js["changeset"]["comments"][2]["visible"]
377 def test_show_tags_json
378 changeset = create(:changeset, :closed)
379 create(:changeset_tag, :changeset => changeset, :k => "created_by", :v => "JOSM/1.5 (18364)")
380 create(:changeset_tag, :changeset => changeset, :k => "comment", :v => "changeset comment")
382 get changeset_show_path(changeset, :format => "json")
384 assert_response :success
385 js = ActiveSupport::JSON.decode(@response.body)
387 assert_equal Settings.api_version, js["version"]
388 assert_equal Settings.generator, js["generator"]
389 assert_single_changeset_json changeset, js
390 assert_equal 2, js["changeset"]["tags"].count
391 assert_equal "JOSM/1.5 (18364)", js["changeset"]["tags"]["created_by"]
392 assert_equal "changeset comment", js["changeset"]["tags"]["comment"]
395 def test_show_bbox_json
396 # test bbox attribute
397 changeset = create(:changeset, :min_lat => (-5 * GeoRecord::SCALE).round, :min_lon => (5 * GeoRecord::SCALE).round,
398 :max_lat => (15 * GeoRecord::SCALE).round, :max_lon => (12 * GeoRecord::SCALE).round)
400 get changeset_show_path(changeset), :params => { :format => "json" }
401 assert_response :success, "cannot get first changeset"
403 js = ActiveSupport::JSON.decode(@response.body)
405 assert_equal(-5, js["changeset"]["min_lat"])
406 assert_equal 5, js["changeset"]["min_lon"]
407 assert_equal 15, js["changeset"]["max_lat"]
408 assert_equal 12, js["changeset"]["max_lon"]
412 # check that a changeset that doesn't exist returns an appropriate message
413 def test_show_not_found
414 [0, -32, 233455644, "afg", "213"].each do |id|
415 get changeset_show_path(id)
416 assert_response :not_found, "should get a not found"
417 rescue ActionController::UrlGenerationError => e
418 assert_match(/No route matches/, e.to_s)
423 # test that the user who opened a change can close it
425 private_user = create(:user, :data_public => false)
426 private_changeset = create(:changeset, :user => private_user)
428 changeset = create(:changeset, :user => user)
430 ## Try without authentication
431 put changeset_close_path(changeset)
432 assert_response :unauthorized
434 ## Try using the non-public user
435 auth_header = bearer_authorization_header private_user
436 put changeset_close_path(private_changeset), :headers => auth_header
437 assert_require_public_data
439 ## The try with the public user
440 auth_header = bearer_authorization_header user
443 put changeset_close_path(cs_id), :headers => auth_header
444 assert_response :success
446 # test that it really is closed now
447 cs = Changeset.find(changeset.id)
449 "changeset should be closed now (#{cs.closed_at} > #{Time.now.utc}.")
453 # test that a different user can't close another user's changeset
454 def test_close_invalid
456 changeset = create(:changeset)
458 auth_header = bearer_authorization_header user
460 put changeset_close_path(changeset), :headers => auth_header
461 assert_response :conflict
462 assert_equal "The user doesn't own that changeset", @response.body
466 # test that you can't close using another method
467 def test_close_method_invalid
469 changeset = create(:changeset, :user => user)
471 auth_header = bearer_authorization_header user
473 get changeset_close_path(changeset), :headers => auth_header
474 assert_response :not_found
475 assert_template "rescues/routing_error"
477 post changeset_close_path(changeset), :headers => auth_header
478 assert_response :not_found
479 assert_template "rescues/routing_error"
483 # check that you can't close a changeset that isn't found
484 def test_close_not_found
485 cs_ids = [0, -132, "123"]
487 # First try to do it with no auth
489 put changeset_close_path(id)
490 assert_response :unauthorized, "Shouldn't be able close the non-existant changeset #{id}, when not authorized"
491 rescue ActionController::UrlGenerationError => e
492 assert_match(/No route matches/, e.to_s)
496 auth_header = bearer_authorization_header
498 put changeset_close_path(id), :headers => auth_header
499 assert_response :not_found, "The changeset #{id} doesn't exist, so can't be closed"
500 rescue ActionController::UrlGenerationError => e
501 assert_match(/No route matches/, e.to_s)
506 # upload something simple, but valid and check that it can
508 # Also try without auth and another user.
509 def test_upload_simple_valid
510 private_user = create(:user, :data_public => false)
511 private_changeset = create(:changeset, :user => private_user)
513 changeset = create(:changeset, :user => user)
517 relation = create(:relation)
518 other_relation = create(:relation)
519 # create some tags, since we test that they are removed later
520 create(:node_tag, :node => node)
521 create(:way_tag, :way => way)
522 create(:relation_tag, :relation => relation)
525 changeset_id = changeset.id
527 # simple diff to change a node, way and relation by removing
532 <node id='#{node.id}' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
533 <way id='#{way.id}' changeset='#{changeset_id}' version='1'>
534 <nd ref='#{node.id}'/>
538 <relation id='#{relation.id}' changeset='#{changeset_id}' version='1'>
539 <member type='way' role='some' ref='#{way.id}'/>
540 <member type='node' role='some' ref='#{node.id}'/>
541 <member type='relation' role='some' ref='#{other_relation.id}'/>
548 post changeset_upload_path(changeset), :params => diff
549 assert_response :unauthorized,
550 "shouldn't be able to upload a simple valid diff to changeset: #{@response.body}"
552 ## Now try with a private user
553 auth_header = bearer_authorization_header private_user
554 changeset_id = private_changeset.id
556 # simple diff to change a node, way and relation by removing
561 <node id='#{node.id}' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
562 <way id='#{way.id}' changeset='#{changeset_id}' version='1'>
563 <nd ref='#{node.id}'/>
567 <relation id='#{relation.id}' changeset='#{changeset_id}' version='1'>
568 <member type='way' role='some' ref='#{way.id}'/>
569 <member type='node' role='some' ref='#{node.id}'/>
570 <member type='relation' role='some' ref='#{other_relation.id}'/>
577 post changeset_upload_path(private_changeset), :params => diff, :headers => auth_header
578 assert_response :forbidden,
579 "can't upload a simple valid diff to changeset: #{@response.body}"
581 ## Now try with the public user
582 auth_header = bearer_authorization_header user
583 changeset_id = changeset.id
585 # simple diff to change a node, way and relation by removing
590 <node id='#{node.id}' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
591 <way id='#{way.id}' changeset='#{changeset_id}' version='1'>
592 <nd ref='#{node.id}'/>
596 <relation id='#{relation.id}' changeset='#{changeset_id}' version='1'>
597 <member type='way' role='some' ref='#{way.id}'/>
598 <member type='node' role='some' ref='#{node.id}'/>
599 <member type='relation' role='some' ref='#{other_relation.id}'/>
606 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
607 assert_response :success,
608 "can't upload a simple valid diff to changeset: #{@response.body}"
610 # check that the changes made it into the database
611 assert_equal 0, Node.find(node.id).tags.size, "node #{node.id} should now have no tags"
612 assert_equal 0, Way.find(way.id).tags.size, "way #{way.id} should now have no tags"
613 assert_equal 0, Relation.find(relation.id).tags.size, "relation #{relation.id} should now have no tags"
617 # upload something which creates new objects using placeholders
618 def test_upload_create_valid
620 changeset = create(:changeset, :user => user)
622 way = create(:way_with_nodes, :nodes_count => 2)
623 relation = create(:relation)
625 auth_header = bearer_authorization_header user
627 # simple diff to create a node way and relation using placeholders
631 <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
632 <tag k='foo' v='bar'/>
633 <tag k='baz' v='bat'/>
635 <way id='-1' changeset='#{changeset.id}'>
636 <nd ref='#{node.id}'/>
640 <relation id='-1' changeset='#{changeset.id}'>
641 <member type='way' role='some' ref='#{way.id}'/>
642 <member type='node' role='some' ref='#{node.id}'/>
643 <member type='relation' role='some' ref='#{relation.id}'/>
650 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
651 assert_response :success,
652 "can't upload a simple valid creation to changeset: #{@response.body}"
654 # check the returned payload
655 new_node_id, new_way_id, new_rel_id = nil
656 assert_dom "diffResult[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1 do
657 # inspect the response to find out what the new element IDs are
658 # check the old IDs are all present and negative one
659 # check the versions are present and equal one
660 assert_dom "> node", 1 do |(node_el)|
661 new_node_id = node_el["new_id"].to_i
662 assert_dom "> @old_id", "-1"
663 assert_dom "> @new_version", "1"
665 assert_dom "> way", 1 do |(way_el)|
666 new_way_id = way_el["new_id"].to_i
667 assert_dom "> @old_id", "-1"
668 assert_dom "> @new_version", "1"
670 assert_dom "> relation", 1 do |(rel_el)|
671 new_rel_id = rel_el["new_id"].to_i
672 assert_dom "> @old_id", "-1"
673 assert_dom "> @new_version", "1"
677 # check that the changes made it into the database
678 assert_equal 2, Node.find(new_node_id).tags.size, "new node should have two tags"
679 assert_equal 0, Way.find(new_way_id).tags.size, "new way should have no tags"
680 assert_equal 0, Relation.find(new_rel_id).tags.size, "new relation should have no tags"
684 # test a complex delete where we delete elements which rely on eachother
685 # in the same transaction.
686 def test_upload_delete
687 changeset = create(:changeset)
688 super_relation = create(:relation)
689 used_relation = create(:relation)
690 used_way = create(:way)
691 used_node = create(:node)
692 create(:relation_member, :relation => super_relation, :member => used_relation)
693 create(:relation_member, :relation => super_relation, :member => used_way)
694 create(:relation_member, :relation => super_relation, :member => used_node)
696 auth_header = bearer_authorization_header changeset.user
698 diff = XML::Document.new
699 diff.root = XML::Node.new "osmChange"
700 delete = XML::Node.new "delete"
702 delete << xml_node_for_relation(super_relation)
703 delete << xml_node_for_relation(used_relation)
704 delete << xml_node_for_way(used_way)
705 delete << xml_node_for_node(used_node)
707 # update the changeset to one that this user owns
708 %w[node way relation].each do |type|
709 delete.find("//osmChange/delete/#{type}").each do |n|
710 n["changeset"] = changeset.id.to_s
715 post changeset_upload_path(changeset), :params => diff.to_s, :headers => auth_header
716 assert_response :success,
717 "can't upload a deletion diff to changeset: #{@response.body}"
719 # check the response is well-formed
720 assert_select "diffResult>node", 1
721 assert_select "diffResult>way", 1
722 assert_select "diffResult>relation", 2
724 # check that everything was deleted
725 assert_not Node.find(used_node.id).visible
726 assert_not Way.find(used_way.id).visible
727 assert_not Relation.find(super_relation.id).visible
728 assert_not Relation.find(used_relation.id).visible
732 # test uploading a delete with no lat/lon, as they are optional in
733 # the osmChange spec.
734 def test_upload_nolatlon_delete
736 changeset = create(:changeset)
738 auth_header = bearer_authorization_header changeset.user
739 diff = "<osmChange><delete><node id='#{node.id}' version='#{node.version}' changeset='#{changeset.id}'/></delete></osmChange>"
742 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
743 assert_response :success,
744 "can't upload a deletion diff to changeset: #{@response.body}"
746 # check the response is well-formed
747 assert_select "diffResult>node", 1
749 # check that everything was deleted
750 assert_not Node.find(node.id).visible
753 def test_repeated_changeset_create
755 auth_header = bearer_authorization_header
757 # create a temporary changeset
758 xml = "<osm><changeset>" \
759 "<tag k='created_by' v='osm test suite checking changesets'/>" \
761 assert_difference "Changeset.count", 1 do
762 put changeset_create_path, :params => xml, :headers => auth_header
764 assert_response :success
768 def test_upload_large_changeset
770 auth_header = bearer_authorization_header user
772 # create an old changeset to ensure we have the maximum rate limit
773 create(:changeset, :user => user, :created_at => Time.now.utc - 28.days)
776 put changeset_create_path, :params => "<osm><changeset/></osm>", :headers => auth_header
777 assert_response :success, "Should be able to create a changeset: #{@response.body}"
778 changeset_id = @response.body.to_i
780 # upload some widely-spaced nodes, spiralling positive and negative
784 <node id='-1' lon='-20' lat='-10' changeset='#{changeset_id}'/>
785 <node id='-10' lon='20' lat='10' changeset='#{changeset_id}'/>
786 <node id='-2' lon='-40' lat='-20' changeset='#{changeset_id}'/>
787 <node id='-11' lon='40' lat='20' changeset='#{changeset_id}'/>
788 <node id='-3' lon='-60' lat='-30' changeset='#{changeset_id}'/>
789 <node id='-12' lon='60' lat='30' changeset='#{changeset_id}'/>
790 <node id='-4' lon='-80' lat='-40' changeset='#{changeset_id}'/>
791 <node id='-13' lon='80' lat='40' changeset='#{changeset_id}'/>
792 <node id='-5' lon='-100' lat='-50' changeset='#{changeset_id}'/>
793 <node id='-14' lon='100' lat='50' changeset='#{changeset_id}'/>
794 <node id='-6' lon='-120' lat='-60' changeset='#{changeset_id}'/>
795 <node id='-15' lon='120' lat='60' changeset='#{changeset_id}'/>
796 <node id='-7' lon='-140' lat='-70' changeset='#{changeset_id}'/>
797 <node id='-16' lon='140' lat='70' changeset='#{changeset_id}'/>
798 <node id='-8' lon='-160' lat='-80' changeset='#{changeset_id}'/>
799 <node id='-17' lon='160' lat='80' changeset='#{changeset_id}'/>
800 <node id='-9' lon='-179.9' lat='-89.9' changeset='#{changeset_id}'/>
801 <node id='-18' lon='179.9' lat='89.9' changeset='#{changeset_id}'/>
806 # upload it, which used to cause an error like "PGError: ERROR:
807 # integer out of range" (bug #2152). but shouldn't any more.
808 post changeset_upload_path(changeset_id), :params => diff, :headers => auth_header
809 assert_response :success,
810 "can't upload a spatially-large diff to changeset: #{@response.body}"
812 # check that the changeset bbox is within bounds
813 cs = Changeset.find(changeset_id)
814 assert_operator cs.min_lon, :>=, -180 * GeoRecord::SCALE, "Minimum longitude (#{cs.min_lon / GeoRecord::SCALE}) should be >= -180 to be valid."
815 assert_operator cs.max_lon, :<=, 180 * GeoRecord::SCALE, "Maximum longitude (#{cs.max_lon / GeoRecord::SCALE}) should be <= 180 to be valid."
816 assert_operator cs.min_lat, :>=, -90 * GeoRecord::SCALE, "Minimum latitude (#{cs.min_lat / GeoRecord::SCALE}) should be >= -90 to be valid."
817 assert_operator cs.max_lat, :<=, 90 * GeoRecord::SCALE, "Maximum latitude (#{cs.max_lat / GeoRecord::SCALE}) should be <= 90 to be valid."
821 # test that deleting stuff in a transaction doesn't bypass the checks
822 # to ensure that used elements are not deleted.
823 def test_upload_delete_invalid
824 changeset = create(:changeset)
825 relation = create(:relation)
826 other_relation = create(:relation)
827 used_way = create(:way)
828 used_node = create(:node)
829 create(:relation_member, :relation => relation, :member => used_way)
830 create(:relation_member, :relation => relation, :member => used_node)
832 auth_header = bearer_authorization_header changeset.user
834 diff = XML::Document.new
835 diff.root = XML::Node.new "osmChange"
836 delete = XML::Node.new "delete"
838 delete << xml_node_for_relation(other_relation)
839 delete << xml_node_for_way(used_way)
840 delete << xml_node_for_node(used_node)
842 # update the changeset to one that this user owns
843 %w[node way relation].each do |type|
844 delete.find("//osmChange/delete/#{type}").each do |n|
845 n["changeset"] = changeset.id.to_s
850 post changeset_upload_path(changeset), :params => diff.to_s, :headers => auth_header
851 assert_response :precondition_failed,
852 "shouldn't be able to upload a invalid deletion diff: #{@response.body}"
853 assert_equal "Precondition failed: Way #{used_way.id} is still used by relations #{relation.id}.", @response.body
855 # check that nothing was, in fact, deleted
856 assert Node.find(used_node.id).visible
857 assert Way.find(used_way.id).visible
858 assert Relation.find(relation.id).visible
859 assert Relation.find(other_relation.id).visible
863 # test that a conditional delete of an in use object works.
864 def test_upload_delete_if_unused
865 changeset = create(:changeset)
866 super_relation = create(:relation)
867 used_relation = create(:relation)
868 used_way = create(:way)
869 used_node = create(:node)
870 create(:relation_member, :relation => super_relation, :member => used_relation)
871 create(:relation_member, :relation => super_relation, :member => used_way)
872 create(:relation_member, :relation => super_relation, :member => used_node)
874 auth_header = bearer_authorization_header changeset.user
876 diff = XML::Document.new
877 diff.root = XML::Node.new "osmChange"
878 delete = XML::Node.new "delete"
880 delete["if-unused"] = ""
881 delete << xml_node_for_relation(used_relation)
882 delete << xml_node_for_way(used_way)
883 delete << xml_node_for_node(used_node)
885 # update the changeset to one that this user owns
886 %w[node way relation].each do |type|
887 delete.find("//osmChange/delete/#{type}").each do |n|
888 n["changeset"] = changeset.id.to_s
893 post changeset_upload_path(changeset), :params => diff.to_s, :headers => auth_header
894 assert_response :success,
895 "can't do a conditional delete of in use objects: #{@response.body}"
897 # check the returned payload
898 assert_dom "diffResult[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1 do
899 # check the old IDs are all present and what we expect
900 # check the new IDs are all present and unchanged
901 # check the new versions are all present and unchanged
902 assert_dom "> node", 1 do
903 assert_dom "> @old_id", used_node.id.to_s
904 assert_dom "> @new_id", used_node.id.to_s
905 assert_dom "> @new_version", used_node.version.to_s
907 assert_dom "> way", 1 do
908 assert_dom "> @old_id", used_way.id.to_s
909 assert_dom "> @new_id", used_way.id.to_s
910 assert_dom "> @new_version", used_way.version.to_s
912 assert_dom "> relation", 1 do
913 assert_dom "> @old_id", used_relation.id.to_s
914 assert_dom "> @new_id", used_relation.id.to_s
915 assert_dom "> @new_version", used_relation.version.to_s
919 # check that nothing was, in fact, deleted
920 assert Node.find(used_node.id).visible
921 assert Way.find(used_way.id).visible
922 assert Relation.find(used_relation.id).visible
926 # upload an element with a really long tag value
927 def test_upload_invalid_too_long_tag
928 changeset = create(:changeset)
930 auth_header = bearer_authorization_header changeset.user
932 # simple diff to create a node way and relation using placeholders
936 <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
937 <tag k='foo' v='#{'x' * 256}'/>
944 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
945 assert_response :bad_request,
946 "shouldn't be able to upload too long a tag to changeset: #{@response.body}"
950 # upload something which creates new objects and inserts them into
951 # existing containers using placeholders.
952 def test_upload_complex
955 relation = create(:relation)
956 create(:way_node, :way => way, :node => node)
958 changeset = create(:changeset)
960 auth_header = bearer_authorization_header changeset.user
962 # simple diff to create a node way and relation using placeholders
966 <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
967 <tag k='foo' v='bar'/>
968 <tag k='baz' v='bat'/>
972 <way id='#{way.id}' changeset='#{changeset.id}' version='1'>
974 <nd ref='#{node.id}'/>
976 <relation id='#{relation.id}' changeset='#{changeset.id}' version='1'>
977 <member type='way' role='some' ref='#{way.id}'/>
978 <member type='node' role='some' ref='-1'/>
979 <member type='relation' role='some' ref='#{relation.id}'/>
986 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
987 assert_response :success,
988 "can't upload a complex diff to changeset: #{@response.body}"
990 # check the returned payload
992 assert_dom "diffResult[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1 do
993 assert_dom "> node", 1 do |(node_el)|
994 new_node_id = node_el["new_id"].to_i
996 assert_dom "> way", 1
997 assert_dom "> relation", 1
1000 # check that the changes made it into the database
1001 assert_equal 2, Node.find(new_node_id).tags.size, "new node should have two tags"
1002 assert_equal [new_node_id, node.id], Way.find(way.id).nds, "way nodes should match"
1003 Relation.find(relation.id).members.each do |type, id, _role|
1004 assert_equal new_node_id, id, "relation should contain new node" if type == "node"
1009 # create a diff which references several changesets, which should cause
1010 # a rollback and none of the diff gets committed
1011 def test_upload_invalid_changesets
1012 changeset = create(:changeset)
1013 other_changeset = create(:changeset, :user => changeset.user)
1014 node = create(:node)
1016 relation = create(:relation)
1017 other_relation = create(:relation)
1019 auth_header = bearer_authorization_header changeset.user
1021 # simple diff to create a node way and relation using placeholders
1025 <node id='#{node.id}' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
1026 <way id='#{way.id}' changeset='#{changeset.id}' version='1'>
1027 <nd ref='#{node.id}'/>
1031 <relation id='#{relation.id}' changeset='#{changeset.id}' version='1'>
1032 <member type='way' role='some' ref='#{way.id}'/>
1033 <member type='node' role='some' ref='#{node.id}'/>
1034 <member type='relation' role='some' ref='#{other_relation.id}'/>
1038 <node id='-1' lon='0' lat='0' changeset='#{other_changeset.id}'>
1039 <tag k='foo' v='bar'/>
1040 <tag k='baz' v='bat'/>
1047 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1048 assert_response :conflict,
1049 "uploading a diff with multiple changesets should have failed"
1051 # check that objects are unmodified
1052 assert_nodes_are_equal(node, Node.find(node.id))
1053 assert_ways_are_equal(way, Way.find(way.id))
1054 assert_relations_are_equal(relation, Relation.find(relation.id))
1058 # upload multiple versions of the same element in the same diff.
1059 def test_upload_multiple_valid
1060 node = create(:node)
1061 changeset = create(:changeset)
1062 auth_header = bearer_authorization_header changeset.user
1064 # change the location of a node multiple times, each time referencing
1065 # the last version. doesn't this depend on version numbers being
1070 <node id='#{node.id}' lon='0.0' lat='0.0' changeset='#{changeset.id}' version='1'/>
1071 <node id='#{node.id}' lon='0.1' lat='0.0' changeset='#{changeset.id}' version='2'/>
1072 <node id='#{node.id}' lon='0.1' lat='0.1' changeset='#{changeset.id}' version='3'/>
1073 <node id='#{node.id}' lon='0.1' lat='0.2' changeset='#{changeset.id}' version='4'/>
1074 <node id='#{node.id}' lon='0.2' lat='0.2' changeset='#{changeset.id}' version='5'/>
1075 <node id='#{node.id}' lon='0.3' lat='0.2' changeset='#{changeset.id}' version='6'/>
1076 <node id='#{node.id}' lon='0.3' lat='0.3' changeset='#{changeset.id}' version='7'/>
1077 <node id='#{node.id}' lon='0.9' lat='0.9' changeset='#{changeset.id}' version='8'/>
1083 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1084 assert_response :success,
1085 "can't upload multiple versions of an element in a diff: #{@response.body}"
1087 # check the response is well-formed. its counter-intuitive, but the
1088 # API will return multiple elements with the same ID and different
1089 # version numbers for each change we made.
1090 assert_select "diffResult>node", 8
1094 # upload multiple versions of the same element in the same diff, but
1095 # keep the version numbers the same.
1096 def test_upload_multiple_duplicate
1097 node = create(:node)
1098 changeset = create(:changeset)
1100 auth_header = bearer_authorization_header changeset.user
1105 <node id='#{node.id}' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
1106 <node id='#{node.id}' lon='1' lat='1' changeset='#{changeset.id}' version='1'/>
1112 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1113 assert_response :conflict,
1114 "shouldn't be able to upload the same element twice in a diff: #{@response.body}"
1118 # try to upload some elements without specifying the version
1119 def test_upload_missing_version
1120 changeset = create(:changeset)
1122 auth_header = bearer_authorization_header changeset.user
1127 <node id='1' lon='1' lat='1' changeset='#{changeset.id}'/>
1133 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1134 assert_response :bad_request,
1135 "shouldn't be able to upload an element without version: #{@response.body}"
1139 # try to upload with commands other than create, modify, or delete
1140 def test_action_upload_invalid
1141 changeset = create(:changeset)
1143 auth_header = bearer_authorization_header changeset.user
1148 <node id='1' lon='1' lat='1' changeset='#{changeset.id}' />
1152 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1153 assert_response :bad_request, "Shouldn't be able to upload a diff with the action ping"
1154 assert_equal("Unknown action ping, choices are create, modify, delete", @response.body)
1158 # upload a valid changeset which has a mixture of whitespace
1159 # to check a bug reported by ivansanchez (#1565).
1160 def test_upload_whitespace_valid
1161 changeset = create(:changeset)
1162 node = create(:node)
1163 way = create(:way_with_nodes, :nodes_count => 2)
1164 relation = create(:relation)
1165 other_relation = create(:relation)
1166 create(:relation_tag, :relation => relation)
1168 auth_header = bearer_authorization_header changeset.user
1172 <modify><node id='#{node.id}' lon='0' lat='0' changeset='#{changeset.id}'
1174 <node id='#{node.id}' lon='1' lat='1' changeset='#{changeset.id}' version='2'><tag k='k' v='v'/></node></modify>
1176 <relation id='#{relation.id}' changeset='#{changeset.id}' version='1'><member
1177 type='way' role='some' ref='#{way.id}'/><member
1178 type='node' role='some' ref='#{node.id}'/>
1179 <member type='relation' role='some' ref='#{other_relation.id}'/>
1181 </modify></osmChange>
1185 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1186 assert_response :success,
1187 "can't upload a valid diff with whitespace variations to changeset: #{@response.body}"
1189 # check the response is well-formed
1190 assert_select "diffResult>node", 2
1191 assert_select "diffResult>relation", 1
1193 # check that the changes made it into the database
1194 assert_equal 1, Node.find(node.id).tags.size, "node #{node.id} should now have one tag"
1195 assert_equal 0, Relation.find(relation.id).tags.size, "relation #{relation.id} should now have no tags"
1199 # test that a placeholder can be reused within the same upload.
1200 def test_upload_reuse_placeholder_valid
1201 changeset = create(:changeset)
1203 auth_header = bearer_authorization_header changeset.user
1208 <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
1209 <tag k="foo" v="bar"/>
1213 <node id='-1' lon='1' lat='1' changeset='#{changeset.id}' version='1'/>
1216 <node id='-1' lon='2' lat='2' changeset='#{changeset.id}' version='2'/>
1222 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1223 assert_response :success,
1224 "can't upload a valid diff with re-used placeholders to changeset: #{@response.body}"
1226 # check the response is well-formed
1227 assert_select "diffResult>node", 3
1228 assert_select "diffResult>node[old_id='-1']", 3
1232 # test what happens if a diff upload re-uses placeholder IDs in an
1234 def test_upload_placeholder_invalid
1235 changeset = create(:changeset)
1237 auth_header = bearer_authorization_header changeset.user
1242 <node id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
1243 <node id='-1' lon='1' lat='1' changeset='#{changeset.id}' version='1'/>
1244 <node id='-1' lon='2' lat='2' changeset='#{changeset.id}' version='2'/>
1250 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1251 assert_response :bad_request,
1252 "shouldn't be able to re-use placeholder IDs"
1254 # placeholder_ids must be unique across all action blocks
1258 <node id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
1261 <node id='-1' lon='1' lat='1' changeset='#{changeset.id}' version='1'/>
1267 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1268 assert_response :bad_request,
1269 "shouldn't be able to re-use placeholder IDs"
1272 def test_upload_process_order
1273 changeset = create(:changeset)
1275 auth_header = bearer_authorization_header changeset.user
1280 <node id="-1" lat="1" lon="2" changeset="#{changeset.id}"/>
1281 <way id="-1" changeset="#{changeset.id}">
1285 <node id="-2" lat="1" lon="2" changeset="#{changeset.id}"/>
1291 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1292 assert_response :bad_request,
1293 "shouldn't refer elements behind it"
1296 def test_upload_duplicate_delete
1297 changeset = create(:changeset)
1299 auth_header = bearer_authorization_header changeset.user
1304 <node id="-1" lat="39" lon="116" changeset="#{changeset.id}" />
1307 <node id="-1" version="1" changeset="#{changeset.id}" />
1308 <node id="-1" version="1" changeset="#{changeset.id}" />
1314 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1315 assert_response :gone,
1316 "transaction should be cancelled by second deletion"
1321 <node id="-1" lat="39" lon="116" changeset="#{changeset.id}" />
1323 <delete if-unused="true">
1324 <node id="-1" version="1" changeset="#{changeset.id}" />
1325 <node id="-1" version="1" changeset="#{changeset.id}" />
1331 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1333 assert_select "diffResult>node", 3
1334 assert_select "diffResult>node[old_id='-1']", 3
1335 assert_select "diffResult>node[new_version='1']", 1
1336 assert_select "diffResult>node[new_version='2']", 1
1340 # test that uploading a way referencing invalid placeholders gives a
1341 # proper error, not a 500.
1342 def test_upload_placeholder_invalid_way
1343 changeset = create(:changeset)
1346 auth_header = bearer_authorization_header changeset.user
1351 <node id="-1" lon="0.0" lat="0.0" changeset="#{changeset.id}" version="1"/>
1352 <node id="-2" lon="0.1" lat="0.1" changeset="#{changeset.id}" version="1"/>
1353 <node id="-3" lon="0.2" lat="0.2" changeset="#{changeset.id}" version="1"/>
1354 <way id="-1" changeset="#{changeset.id}" version="1">
1365 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1366 assert_response :bad_request,
1367 "shouldn't be able to use invalid placeholder IDs"
1368 assert_equal "Placeholder node not found for reference -4 in way -1", @response.body
1370 # the same again, but this time use an existing way
1374 <node id="-1" lon="0.0" lat="0.0" changeset="#{changeset.id}" version="1"/>
1375 <node id="-2" lon="0.1" lat="0.1" changeset="#{changeset.id}" version="1"/>
1376 <node id="-3" lon="0.2" lat="0.2" changeset="#{changeset.id}" version="1"/>
1377 <way id="#{way.id}" changeset="#{changeset.id}" version="1">
1388 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1389 assert_response :bad_request,
1390 "shouldn't be able to use invalid placeholder IDs"
1391 assert_equal "Placeholder node not found for reference -4 in way #{way.id}", @response.body
1395 # test that uploading a relation referencing invalid placeholders gives a
1396 # proper error, not a 500.
1397 def test_upload_placeholder_invalid_relation
1398 changeset = create(:changeset)
1399 relation = create(:relation)
1401 auth_header = bearer_authorization_header changeset.user
1406 <node id="-1" lon="0.0" lat="0.0" changeset="#{changeset.id}" version="1"/>
1407 <node id="-2" lon="0.1" lat="0.1" changeset="#{changeset.id}" version="1"/>
1408 <node id="-3" lon="0.2" lat="0.2" changeset="#{changeset.id}" version="1"/>
1409 <relation id="-1" changeset="#{changeset.id}" version="1">
1410 <member type="node" role="foo" ref="-1"/>
1411 <member type="node" role="foo" ref="-2"/>
1412 <member type="node" role="foo" ref="-3"/>
1413 <member type="node" role="foo" ref="-4"/>
1420 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1421 assert_response :bad_request,
1422 "shouldn't be able to use invalid placeholder IDs"
1423 assert_equal "Placeholder Node not found for reference -4 in relation -1.", @response.body
1425 # the same again, but this time use an existing relation
1429 <node id="-1" lon="0.0" lat="0.0" changeset="#{changeset.id}" version="1"/>
1430 <node id="-2" lon="0.1" lat="0.1" changeset="#{changeset.id}" version="1"/>
1431 <node id="-3" lon="0.2" lat="0.2" changeset="#{changeset.id}" version="1"/>
1432 <relation id="#{relation.id}" changeset="#{changeset.id}" version="1">
1433 <member type="node" role="foo" ref="-1"/>
1434 <member type="node" role="foo" ref="-2"/>
1435 <member type="node" role="foo" ref="-3"/>
1436 <member type="way" role="bar" ref="-1"/>
1443 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1444 assert_response :bad_request,
1445 "shouldn't be able to use invalid placeholder IDs"
1446 assert_equal "Placeholder Way not found for reference -1 in relation #{relation.id}.", @response.body
1450 # test what happens if a diff is uploaded containing only a node
1452 def test_upload_node_move
1453 auth_header = bearer_authorization_header
1455 xml = "<osm><changeset>" \
1456 "<tag k='created_by' v='osm test suite checking changesets'/>" \
1457 "</changeset></osm>"
1458 put changeset_create_path, :params => xml, :headers => auth_header
1459 assert_response :success
1460 changeset_id = @response.body.to_i
1462 old_node = create(:node, :lat => 1, :lon => 1)
1464 diff = XML::Document.new
1465 diff.root = XML::Node.new "osmChange"
1466 modify = XML::Node.new "modify"
1467 xml_old_node = xml_node_for_node(old_node)
1468 xml_old_node["lat"] = 2.0.to_s
1469 xml_old_node["lon"] = 2.0.to_s
1470 xml_old_node["changeset"] = changeset_id.to_s
1471 modify << xml_old_node
1475 post changeset_upload_path(changeset_id), :params => diff.to_s, :headers => auth_header
1476 assert_response :success,
1477 "diff should have uploaded OK"
1480 changeset = Changeset.find(changeset_id)
1481 assert_equal 1 * GeoRecord::SCALE, changeset.min_lon, "min_lon should be 1 degree"
1482 assert_equal 2 * GeoRecord::SCALE, changeset.max_lon, "max_lon should be 2 degrees"
1483 assert_equal 1 * GeoRecord::SCALE, changeset.min_lat, "min_lat should be 1 degree"
1484 assert_equal 2 * GeoRecord::SCALE, changeset.max_lat, "max_lat should be 2 degrees"
1488 # test what happens if a diff is uploaded adding a node to a way.
1489 def test_upload_way_extend
1490 auth_header = bearer_authorization_header
1492 xml = "<osm><changeset>" \
1493 "<tag k='created_by' v='osm test suite checking changesets'/>" \
1494 "</changeset></osm>"
1495 put changeset_create_path, :params => xml, :headers => auth_header
1496 assert_response :success
1497 changeset_id = @response.body.to_i
1499 old_way = create(:way)
1500 create(:way_node, :way => old_way, :node => create(:node, :lat => 0.1, :lon => 0.1))
1502 diff = XML::Document.new
1503 diff.root = XML::Node.new "osmChange"
1504 modify = XML::Node.new "modify"
1505 xml_old_way = xml_node_for_way(old_way)
1506 nd_ref = XML::Node.new "nd"
1507 nd_ref["ref"] = create(:node, :lat => 0.3, :lon => 0.3).id.to_s
1508 xml_old_way << nd_ref
1509 xml_old_way["changeset"] = changeset_id.to_s
1510 modify << xml_old_way
1514 post changeset_upload_path(changeset_id), :params => diff.to_s, :headers => auth_header
1515 assert_response :success,
1516 "diff should have uploaded OK"
1519 changeset = Changeset.find(changeset_id)
1520 assert_equal 0.1 * GeoRecord::SCALE, changeset.min_lon, "min_lon should be 0.1 degree"
1521 assert_equal 0.3 * GeoRecord::SCALE, changeset.max_lon, "max_lon should be 0.3 degrees"
1522 assert_equal 0.1 * GeoRecord::SCALE, changeset.min_lat, "min_lat should be 0.1 degree"
1523 assert_equal 0.3 * GeoRecord::SCALE, changeset.max_lat, "max_lat should be 0.3 degrees"
1527 # test for more issues in #1568
1528 def test_upload_empty_invalid
1529 changeset = create(:changeset)
1531 auth_header = bearer_authorization_header changeset.user
1534 "<osmChange></osmChange>",
1535 "<osmChange><modify/></osmChange>",
1536 "<osmChange><modify></modify></osmChange>"].each do |diff|
1538 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1539 assert_response(:success, "should be able to upload " \
1540 "empty changeset: " + diff)
1545 # test that the X-Error-Format header works to request XML errors
1546 def test_upload_xml_errors
1547 changeset = create(:changeset)
1548 node = create(:node)
1549 create(:relation_member, :member => node)
1551 auth_header = bearer_authorization_header changeset.user
1553 # try and delete a node that is in use
1554 diff = XML::Document.new
1555 diff.root = XML::Node.new "osmChange"
1556 delete = XML::Node.new "delete"
1558 delete << xml_node_for_node(node)
1561 error_header = error_format_header "xml"
1562 post changeset_upload_path(changeset), :params => diff.to_s, :headers => auth_header.merge(error_header)
1563 assert_response :success,
1564 "failed to return error in XML format"
1566 # check the returned payload
1567 assert_select "osmError[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
1568 assert_select "osmError>status", 1
1569 assert_select "osmError>message", 1
1572 def test_upload_not_found
1573 changeset = create(:changeset)
1575 auth_header = bearer_authorization_header changeset.user
1581 <node id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
1587 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1588 assert_response :not_found, "Node should not be found"
1594 <way id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
1600 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1601 assert_response :not_found, "Way should not be found"
1607 <relation id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
1613 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1614 assert_response :not_found, "Relation should not be found"
1620 <node id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
1626 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1627 assert_response :not_found, "Node should not be deleted"
1633 <way id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
1639 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1640 assert_response :not_found, "Way should not be deleted"
1646 <relation id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
1652 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1653 assert_response :not_found, "Relation should not be deleted"
1656 def test_upload_relation_placeholder_not_fix
1657 changeset = create(:changeset)
1659 auth_header = bearer_authorization_header changeset.user
1663 <osmChange version='0.6'>
1665 <relation id='-2' version='0' changeset='#{changeset.id}'>
1666 <member type='relation' role='' ref='-4' />
1667 <tag k='type' v='route' />
1668 <tag k='name' v='AtoB' />
1670 <relation id='-3' version='0' changeset='#{changeset.id}'>
1671 <tag k='type' v='route' />
1672 <tag k='name' v='BtoA' />
1674 <relation id='-4' version='0' changeset='#{changeset.id}'>
1675 <member type='relation' role='' ref='-2' />
1676 <member type='relation' role='' ref='-3' />
1677 <tag k='type' v='route_master' />
1678 <tag k='name' v='master' />
1685 post changeset_upload_path(changeset), :params => diff.to_s, :headers => auth_header
1686 assert_response :bad_request, "shouldn't be able to use reference -4 in relation -2: #{@response.body}"
1687 assert_equal "Placeholder Relation not found for reference -4 in relation -2.", @response.body
1690 def test_upload_multiple_delete_block
1691 changeset = create(:changeset)
1693 auth_header = bearer_authorization_header changeset.user
1695 node = create(:node)
1697 create(:way_node, :way => way, :node => node)
1698 alone_node = create(:node)
1702 <osmChange version='0.6'>
1703 <delete version="0.6">
1704 <node id="#{node.id}" version="#{node.version}" changeset="#{changeset.id}"/>
1706 <delete version="0.6" if-unused="true">
1707 <node id="#{alone_node.id}" version="#{alone_node.version}" changeset="#{changeset.id}"/>
1713 post changeset_upload_path(changeset), :params => diff.to_s, :headers => auth_header
1714 assert_response :precondition_failed,
1715 "shouldn't be able to upload a invalid deletion diff: #{@response.body}"
1716 assert_equal "Precondition failed: Node #{node.id} is still used by ways #{way.id}.", @response.body
1720 # test initial rate limit
1721 def test_upload_initial_rate_limit
1723 user = create(:user)
1725 # create some objects to use
1726 node = create(:node)
1727 way = create(:way_with_nodes, :nodes_count => 2)
1728 relation = create(:relation)
1730 # create a changeset that puts us near the initial rate limit
1731 changeset = create(:changeset, :user => user,
1732 :created_at => Time.now.utc - 5.minutes,
1733 :num_changes => Settings.initial_changes_per_hour - 2)
1735 # create authentication header
1736 auth_header = bearer_authorization_header user
1738 # simple diff to create a node way and relation using placeholders
1742 <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
1743 <tag k='foo' v='bar'/>
1744 <tag k='baz' v='bat'/>
1746 <way id='-1' changeset='#{changeset.id}'>
1747 <nd ref='#{node.id}'/>
1751 <relation id='-1' changeset='#{changeset.id}'>
1752 <member type='way' role='some' ref='#{way.id}'/>
1753 <member type='node' role='some' ref='#{node.id}'/>
1754 <member type='relation' role='some' ref='#{relation.id}'/>
1761 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1762 assert_response :too_many_requests, "upload did not hit rate limit"
1766 # test maximum rate limit
1767 def test_upload_maximum_rate_limit
1769 user = create(:user)
1771 # create some objects to use
1772 node = create(:node)
1773 way = create(:way_with_nodes, :nodes_count => 2)
1774 relation = create(:relation)
1776 # create a changeset to establish our initial edit time
1777 changeset = create(:changeset, :user => user,
1778 :created_at => Time.now.utc - 28.days)
1780 # create changeset to put us near the maximum rate limit
1781 total_changes = Settings.max_changes_per_hour - 2
1782 while total_changes.positive?
1783 changes = [total_changes, Changeset::MAX_ELEMENTS].min
1784 changeset = create(:changeset, :user => user,
1785 :created_at => Time.now.utc - 5.minutes,
1786 :num_changes => changes)
1787 total_changes -= changes
1790 # create authentication header
1791 auth_header = bearer_authorization_header user
1793 # simple diff to create a node way and relation using placeholders
1797 <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
1798 <tag k='foo' v='bar'/>
1799 <tag k='baz' v='bat'/>
1801 <way id='-1' changeset='#{changeset.id}'>
1802 <nd ref='#{node.id}'/>
1806 <relation id='-1' changeset='#{changeset.id}'>
1807 <member type='way' role='some' ref='#{way.id}'/>
1808 <member type='node' role='some' ref='#{node.id}'/>
1809 <member type='relation' role='some' ref='#{relation.id}'/>
1816 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1817 assert_response :too_many_requests, "upload did not hit rate limit"
1821 # test initial size limit
1822 def test_upload_initial_size_limit
1824 user = create(:user)
1826 # create a changeset that puts us near the initial size limit
1827 changeset = create(:changeset, :user => user,
1828 :min_lat => (-0.5 * GeoRecord::SCALE).round, :min_lon => (0.5 * GeoRecord::SCALE).round,
1829 :max_lat => (0.5 * GeoRecord::SCALE).round, :max_lon => (2.5 * GeoRecord::SCALE).round)
1831 # create authentication header
1832 auth_header = bearer_authorization_header user
1834 # simple diff to create a node
1838 <node id='-1' lon='0.9' lat='2.9' changeset='#{changeset.id}'>
1839 <tag k='foo' v='bar'/>
1840 <tag k='baz' v='bat'/>
1847 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1848 assert_response :payload_too_large, "upload did not hit size limit"
1852 # test size limit after one week
1853 def test_upload_week_size_limit
1855 user = create(:user)
1857 # create a changeset to establish our initial edit time
1858 create(:changeset, :user => user, :created_at => Time.now.utc - 7.days)
1860 # create a changeset that puts us near the initial size limit
1861 changeset = create(:changeset, :user => user,
1862 :min_lat => (-0.5 * GeoRecord::SCALE).round, :min_lon => (0.5 * GeoRecord::SCALE).round,
1863 :max_lat => (0.5 * GeoRecord::SCALE).round, :max_lon => (2.5 * GeoRecord::SCALE).round)
1865 # create authentication header
1866 auth_header = bearer_authorization_header user
1868 # simple diff to create a node way and relation using placeholders
1872 <node id='-1' lon='35' lat='35' changeset='#{changeset.id}'>
1873 <tag k='foo' v='bar'/>
1874 <tag k='baz' v='bat'/>
1881 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1882 assert_response :payload_too_large, "upload did not hit size limit"
1886 # when we make some simple changes we get the same changes back from the
1888 def test_diff_download_simple
1889 node = create(:node)
1891 ## First try with a non-public user, which should get a forbidden
1892 auth_header = bearer_authorization_header create(:user, :data_public => false)
1894 # create a temporary changeset
1895 xml = "<osm><changeset>" \
1896 "<tag k='created_by' v='osm test suite checking changesets'/>" \
1897 "</changeset></osm>"
1898 put changeset_create_path, :params => xml, :headers => auth_header
1899 assert_response :forbidden
1901 ## Now try with a normal user
1902 auth_header = bearer_authorization_header
1904 # create a temporary changeset
1905 xml = "<osm><changeset>" \
1906 "<tag k='created_by' v='osm test suite checking changesets'/>" \
1907 "</changeset></osm>"
1908 put changeset_create_path, :params => xml, :headers => auth_header
1909 assert_response :success
1910 changeset_id = @response.body.to_i
1916 <node id='#{node.id}' lon='0.0' lat='0.0' changeset='#{changeset_id}' version='1'/>
1917 <node id='#{node.id}' lon='0.1' lat='0.0' changeset='#{changeset_id}' version='2'/>
1918 <node id='#{node.id}' lon='0.1' lat='0.1' changeset='#{changeset_id}' version='3'/>
1919 <node id='#{node.id}' lon='0.1' lat='0.2' changeset='#{changeset_id}' version='4'/>
1920 <node id='#{node.id}' lon='0.2' lat='0.2' changeset='#{changeset_id}' version='5'/>
1921 <node id='#{node.id}' lon='0.3' lat='0.2' changeset='#{changeset_id}' version='6'/>
1922 <node id='#{node.id}' lon='0.3' lat='0.3' changeset='#{changeset_id}' version='7'/>
1923 <node id='#{node.id}' lon='0.9' lat='0.9' changeset='#{changeset_id}' version='8'/>
1929 post changeset_upload_path(changeset_id), :params => diff, :headers => auth_header
1930 assert_response :success,
1931 "can't upload multiple versions of an element in a diff: #{@response.body}"
1933 get changeset_download_path(changeset_id)
1934 assert_response :success
1936 assert_select "osmChange", 1
1937 assert_select "osmChange>modify", 8
1938 assert_select "osmChange>modify>node", 8
1942 # culled this from josm to ensure that nothing in the way that josm
1943 # is formatting the request is causing it to fail.
1945 # NOTE: the error turned out to be something else completely!
1946 def test_josm_upload
1947 auth_header = bearer_authorization_header
1949 # create a temporary changeset
1950 xml = "<osm><changeset>" \
1951 "<tag k='created_by' v='osm test suite checking changesets'/>" \
1952 "</changeset></osm>"
1953 put changeset_create_path, :params => xml, :headers => auth_header
1954 assert_response :success
1955 changeset_id = @response.body.to_i
1958 <osmChange version="0.6" generator="JOSM">
1959 <create version="0.6" generator="JOSM">
1960 <node id='-1' visible='true' changeset='#{changeset_id}' lat='51.49619982187321' lon='-0.18722061869438314' />
1961 <node id='-2' visible='true' changeset='#{changeset_id}' lat='51.496359883909605' lon='-0.18653093576241928' />
1962 <node id='-3' visible='true' changeset='#{changeset_id}' lat='51.49598132358285' lon='-0.18719613290981638' />
1963 <node id='-4' visible='true' changeset='#{changeset_id}' lat='51.4961591711078' lon='-0.18629015888084607' />
1964 <node id='-5' visible='true' changeset='#{changeset_id}' lat='51.49582126021711' lon='-0.18708186591517145' />
1965 <node id='-6' visible='true' changeset='#{changeset_id}' lat='51.49591018437858' lon='-0.1861432441734455' />
1966 <node id='-7' visible='true' changeset='#{changeset_id}' lat='51.49560784152179' lon='-0.18694719410005425' />
1967 <node id='-8' visible='true' changeset='#{changeset_id}' lat='51.49567389979617' lon='-0.1860289771788006' />
1968 <node id='-9' visible='true' changeset='#{changeset_id}' lat='51.49543761398892' lon='-0.186820684213126' />
1969 <way id='-10' action='modify' visible='true' changeset='#{changeset_id}'>
1979 <tag k='highway' v='residential' />
1980 <tag k='name' v='Foobar Street' />
1987 post changeset_upload_path(changeset_id), :params => diff, :headers => auth_header
1988 assert_response :success,
1989 "can't upload a diff from JOSM: #{@response.body}"
1991 get changeset_download_path(changeset_id)
1992 assert_response :success
1994 assert_select "osmChange", 1
1995 assert_select "osmChange>create>node", 9
1996 assert_select "osmChange>create>way", 1
1997 assert_select "osmChange>create>way>nd", 9
1998 assert_select "osmChange>create>way>tag", 2
2002 # when we make some complex changes we get the same changes back from the
2004 def test_diff_download_complex
2005 node = create(:node)
2006 node2 = create(:node)
2008 auth_header = bearer_authorization_header
2010 # create a temporary changeset
2011 xml = "<osm><changeset>" \
2012 "<tag k='created_by' v='osm test suite checking changesets'/>" \
2013 "</changeset></osm>"
2014 put changeset_create_path, :params => xml, :headers => auth_header
2015 assert_response :success
2016 changeset_id = @response.body.to_i
2022 <node id='#{node.id}' lon='0.0' lat='0.0' changeset='#{changeset_id}' version='1'/>
2025 <node id='-1' lon='0.9' lat='0.9' changeset='#{changeset_id}' version='0'/>
2026 <node id='-2' lon='0.8' lat='0.9' changeset='#{changeset_id}' version='0'/>
2027 <node id='-3' lon='0.7' lat='0.9' changeset='#{changeset_id}' version='0'/>
2030 <node id='#{node2.id}' lon='2.0' lat='1.5' changeset='#{changeset_id}' version='1'/>
2031 <way id='#{way.id}' changeset='#{changeset_id}' version='1'>
2032 <nd ref='#{node2.id}'/>
2042 post changeset_upload_path(changeset_id), :params => diff, :headers => auth_header
2043 assert_response :success,
2044 "can't upload multiple versions of an element in a diff: #{@response.body}"
2046 get changeset_download_path(changeset_id)
2047 assert_response :success
2049 assert_select "osmChange", 1
2050 assert_select "osmChange>create", 3
2051 assert_select "osmChange>delete", 1
2052 assert_select "osmChange>modify", 2
2053 assert_select "osmChange>create>node", 3
2054 assert_select "osmChange>delete>node", 1
2055 assert_select "osmChange>modify>node", 1
2056 assert_select "osmChange>modify>way", 1
2059 def test_changeset_download
2060 changeset = create(:changeset)
2061 node = create(:node, :with_history, :version => 1, :changeset => changeset)
2062 tag = create(:old_node_tag, :old_node => node.old_nodes.find_by(:version => 1))
2063 node2 = create(:node, :with_history, :version => 1, :changeset => changeset)
2064 _node3 = create(:node, :with_history, :deleted, :version => 1, :changeset => changeset)
2065 _relation = create(:relation, :with_history, :version => 1, :changeset => changeset)
2066 _relation2 = create(:relation, :with_history, :deleted, :version => 1, :changeset => changeset)
2068 get changeset_download_path(changeset)
2070 assert_response :success
2072 # FIXME: needs more assert_select tests
2073 assert_select "osmChange[version='#{Settings.api_version}'][generator='#{Settings.generator}']" do
2074 assert_select "create", :count => 5
2075 assert_select "create>node[id='#{node.id}'][visible='#{node.visible?}'][version='#{node.version}']" do
2076 assert_select "tag[k='#{tag.k}'][v='#{tag.v}']"
2078 assert_select "create>node[id='#{node2.id}']"
2082 test "sorts downloaded elements by timestamp" do
2083 changeset = create(:changeset)
2084 node1 = create(:old_node, :version => 2, :timestamp => "2020-02-01", :changeset => changeset)
2085 node0 = create(:old_node, :version => 2, :timestamp => "2020-01-01", :changeset => changeset)
2087 get changeset_download_path(changeset)
2088 assert_response :success
2089 assert_dom "modify", :count => 2 do |modify|
2090 assert_dom modify[0], ">node", :count => 1 do |node|
2091 assert_dom node, ">@id", node0.node_id.to_s
2093 assert_dom modify[1], ">node", :count => 1 do |node|
2094 assert_dom node, ">@id", node1.node_id.to_s
2099 test "sorts downloaded elements by version" do
2100 changeset = create(:changeset)
2101 node1 = create(:old_node, :version => 3, :timestamp => "2020-01-01", :changeset => changeset)
2102 node0 = create(:old_node, :version => 2, :timestamp => "2020-01-01", :changeset => changeset)
2104 get changeset_download_path(changeset)
2105 assert_response :success
2106 assert_dom "modify", :count => 2 do |modify|
2107 assert_dom modify[0], ">node", :count => 1 do |node|
2108 assert_dom node, ">@id", node0.node_id.to_s
2110 assert_dom modify[1], ">node", :count => 1 do |node|
2111 assert_dom node, ">@id", node1.node_id.to_s
2117 # check that the bounding box of a changeset gets updated correctly
2118 # FIXME: This should really be moded to a integration test due to the with_controller
2119 def test_changeset_bbox
2121 create(:way_node, :way => way, :node => create(:node, :lat => 0.3, :lon => 0.3))
2123 auth_header = bearer_authorization_header
2125 # create a new changeset
2126 xml = "<osm><changeset/></osm>"
2127 put changeset_create_path, :params => xml, :headers => auth_header
2128 assert_response :success, "Creating of changeset failed."
2129 changeset_id = @response.body.to_i
2131 # add a single node to it
2132 with_controller(NodesController.new) do
2133 xml = "<osm><node lon='0.1' lat='0.2' changeset='#{changeset_id}'/></osm>"
2134 post api_nodes_path, :params => xml, :headers => auth_header
2135 assert_response :success, "Couldn't create node."
2138 # get the bounding box back from the changeset
2139 get changeset_show_path(changeset_id)
2140 assert_response :success, "Couldn't read back changeset."
2141 assert_select "osm>changeset[min_lon='0.1000000']", 1
2142 assert_select "osm>changeset[max_lon='0.1000000']", 1
2143 assert_select "osm>changeset[min_lat='0.2000000']", 1
2144 assert_select "osm>changeset[max_lat='0.2000000']", 1
2146 # add another node to it
2147 with_controller(NodesController.new) do
2148 xml = "<osm><node lon='0.2' lat='0.1' changeset='#{changeset_id}'/></osm>"
2149 post api_nodes_path, :params => xml, :headers => auth_header
2150 assert_response :success, "Couldn't create second node."
2153 # get the bounding box back from the changeset
2154 get changeset_show_path(changeset_id)
2155 assert_response :success, "Couldn't read back changeset for the second time."
2156 assert_select "osm>changeset[min_lon='0.1000000']", 1
2157 assert_select "osm>changeset[max_lon='0.2000000']", 1
2158 assert_select "osm>changeset[min_lat='0.1000000']", 1
2159 assert_select "osm>changeset[max_lat='0.2000000']", 1
2161 # add (delete) a way to it, which contains a point at (3,3)
2162 with_controller(WaysController.new) do
2163 xml = update_changeset(xml_for_way(way), changeset_id)
2164 delete api_way_path(way), :params => xml.to_s, :headers => auth_header
2165 assert_response :success, "Couldn't delete a way."
2168 # get the bounding box back from the changeset
2169 get changeset_show_path(changeset_id)
2170 assert_response :success, "Couldn't read back changeset for the third time."
2171 assert_select "osm>changeset[min_lon='0.1000000']", 1
2172 assert_select "osm>changeset[max_lon='0.3000000']", 1
2173 assert_select "osm>changeset[min_lat='0.1000000']", 1
2174 assert_select "osm>changeset[max_lat='0.3000000']", 1
2178 # test the query functionality of changesets
2180 private_user = create(:user, :data_public => false)
2181 private_user_changeset = create(:changeset, :user => private_user)
2182 private_user_closed_changeset = create(:changeset, :closed, :user => private_user)
2183 user = create(:user)
2184 changeset = create(:changeset, :user => user)
2185 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))
2186 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)
2187 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)
2189 get changesets_path(:bbox => "-10,-10, 10, 10")
2190 assert_response :success, "can't get changesets in bbox"
2191 assert_changesets_in_order [changeset3, changeset2]
2193 get changesets_path(:bbox => "4.5,4.5,4.6,4.6")
2194 assert_response :success, "can't get changesets in bbox"
2195 assert_changesets_in_order [changeset3]
2197 # not found when looking for changesets of non-existing users
2198 get changesets_path(:user => User.maximum(:id) + 1)
2199 assert_response :not_found
2200 assert_equal "text/plain", @response.media_type
2201 get changesets_path(:display_name => " ")
2202 assert_response :not_found
2203 assert_equal "text/plain", @response.media_type
2205 # can't get changesets of user 1 without authenticating
2206 get changesets_path(:user => private_user.id)
2207 assert_response :not_found, "shouldn't be able to get changesets by non-public user (ID)"
2208 get changesets_path(:display_name => private_user.display_name)
2209 assert_response :not_found, "shouldn't be able to get changesets by non-public user (name)"
2211 # but this should work
2212 auth_header = bearer_authorization_header private_user
2213 get changesets_path(:user => private_user.id), :headers => auth_header
2214 assert_response :success, "can't get changesets by user ID"
2215 assert_changesets_in_order [private_user_changeset, private_user_closed_changeset]
2217 get changesets_path(:display_name => private_user.display_name), :headers => auth_header
2218 assert_response :success, "can't get changesets by user name"
2219 assert_changesets_in_order [private_user_changeset, private_user_closed_changeset]
2221 # test json endpoint
2222 get changesets_path(:display_name => private_user.display_name), :headers => auth_header, :params => { :format => "json" }
2223 assert_response :success, "can't get changesets by user name"
2225 js = ActiveSupport::JSON.decode(@response.body)
2228 assert_equal Settings.api_version, js["version"]
2229 assert_equal Settings.generator, js["generator"]
2230 assert_equal 2, js["changesets"].count
2232 # check that the correct error is given when we provide both UID and name
2233 get changesets_path(:user => private_user.id,
2234 :display_name => private_user.display_name), :headers => auth_header
2235 assert_response :bad_request, "should be a bad request to have both ID and name specified"
2237 get changesets_path(:user => private_user.id, :open => true), :headers => auth_header
2238 assert_response :success, "can't get changesets by user and open"
2239 assert_changesets_in_order [private_user_changeset]
2241 get changesets_path(:time => "2007-12-31"), :headers => auth_header
2242 assert_response :success, "can't get changesets by time-since"
2243 assert_changesets_in_order [changeset3, changeset2, changeset, private_user_changeset, private_user_closed_changeset, closed_changeset]
2245 get changesets_path(:time => "2008-01-01T12:34Z"), :headers => auth_header
2246 assert_response :success, "can't get changesets by time-since with hour"
2247 assert_changesets_in_order [changeset3, changeset2, changeset, private_user_changeset, private_user_closed_changeset, closed_changeset]
2249 get changesets_path(:time => "2007-12-31T23:59Z,2008-01-02T00:01Z"), :headers => auth_header
2250 assert_response :success, "can't get changesets by time-range"
2251 assert_changesets_in_order [closed_changeset]
2253 get changesets_path(:open => "true"), :headers => auth_header
2254 assert_response :success, "can't get changesets by open-ness"
2255 assert_changesets_in_order [changeset3, changeset2, changeset, private_user_changeset]
2257 get changesets_path(:closed => "true"), :headers => auth_header
2258 assert_response :success, "can't get changesets by closed-ness"
2259 assert_changesets_in_order [private_user_closed_changeset, closed_changeset]
2261 get changesets_path(:closed => "true", :user => private_user.id), :headers => auth_header
2262 assert_response :success, "can't get changesets by closed-ness and user"
2263 assert_changesets_in_order [private_user_closed_changeset]
2265 get changesets_path(:closed => "true", :user => user.id), :headers => auth_header
2266 assert_response :success, "can't get changesets by closed-ness and user"
2267 assert_changesets_in_order [closed_changeset]
2269 get changesets_path(:changesets => "#{private_user_changeset.id},#{changeset.id},#{closed_changeset.id}"), :headers => auth_header
2270 assert_response :success, "can't get changesets by id (as comma-separated string)"
2271 assert_changesets_in_order [changeset, private_user_changeset, closed_changeset]
2273 get changesets_path(:changesets => ""), :headers => auth_header
2274 assert_response :bad_request, "should be a bad request since changesets is empty"
2278 # test the query functionality of changesets with the limit parameter
2279 def test_query_limit
2280 user = create(:user)
2281 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))
2282 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))
2283 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))
2284 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))
2285 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))
2288 assert_response :success
2289 assert_changesets_in_order [changeset5, changeset4, changeset3, changeset2, changeset1]
2291 get changesets_path(:limit => "3")
2292 assert_response :success
2293 assert_changesets_in_order [changeset5, changeset4, changeset3]
2295 get changesets_path(:limit => "0")
2296 assert_response :bad_request
2298 get changesets_path(:limit => Settings.max_changeset_query_limit)
2299 assert_response :success
2300 assert_changesets_in_order [changeset5, changeset4, changeset3, changeset2, changeset1]
2302 get changesets_path(:limit => Settings.max_changeset_query_limit + 1)
2303 assert_response :bad_request
2307 # test the query functionality of sequential changesets with order and time parameters
2308 def test_query_order
2309 user = create(:user)
2310 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))
2311 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))
2312 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))
2313 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))
2314 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))
2315 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))
2318 assert_response :success
2319 assert_changesets_in_order [changeset6, changeset5, changeset4, changeset3, changeset2, changeset1]
2321 get changesets_path(:order => "oldest")
2322 assert_response :success
2323 assert_changesets_in_order [changeset1, changeset2, changeset3, changeset4, changeset5, changeset6]
2326 # lower time bound at the opening time of a changeset
2327 ["2008-02-01T00:00:00Z", "2008-05-15T00:00:00Z", [changeset5, changeset4, changeset3, changeset2], [changeset5, changeset4, changeset3, changeset2]],
2328 # lower time bound in the middle of a changeset
2329 ["2008-02-01T12:00:00Z", "2008-05-15T00:00:00Z", [changeset5, changeset4, changeset3, changeset2], [changeset5, changeset4, changeset3]],
2330 # lower time bound at the closing time of a changeset
2331 ["2008-02-02T00:00:00Z", "2008-05-15T00:00:00Z", [changeset5, changeset4, changeset3, changeset2], [changeset5, changeset4, changeset3]],
2332 # lower time bound after the closing time of a changeset
2333 ["2008-02-02T00:00:01Z", "2008-05-15T00:00:00Z", [changeset5, changeset4, changeset3], [changeset5, changeset4, changeset3]],
2334 # upper time bound in the middle of a changeset
2335 ["2007-09-09T12:00:00Z", "2008-04-01T12:00:00Z", [changeset4, changeset3, changeset2, changeset1], [changeset4, changeset3, changeset2, changeset1]],
2337 ["2009-02-02T00:00:01Z", "2018-05-15T00:00:00Z", [], []]
2338 ].each do |from, to, interval_changesets, point_changesets|
2339 get changesets_path(:time => "#{from},#{to}")
2340 assert_response :success
2341 assert_changesets_in_order interval_changesets
2343 get changesets_path(:from => from, :to => to)
2344 assert_response :success
2345 assert_changesets_in_order point_changesets
2347 get changesets_path(:from => from, :to => to, :order => "oldest")
2348 assert_response :success
2349 assert_changesets_in_order point_changesets.reverse
2354 # test the query functionality of overlapping changesets with order and time parameters
2355 def test_query_order_overlapping
2356 user = create(:user)
2357 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))
2358 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))
2359 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))
2360 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))
2361 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))
2363 get changesets_path(:time => "2015-06-04T00:00:00Z")
2364 assert_response :success
2365 assert_changesets_in_order [changeset1, changeset2, changeset3, changeset4]
2367 get changesets_path(:from => "2015-06-04T00:00:00Z")
2368 assert_response :success
2369 assert_changesets_in_order [changeset1, changeset2, changeset3]
2371 get changesets_path(:from => "2015-06-04T00:00:00Z", :order => "oldest")
2372 assert_response :success
2373 assert_changesets_in_order [changeset3, changeset2, changeset1]
2375 get changesets_path(:time => "2015-06-04T16:00:00Z,2015-06-04T17:30:00Z")
2376 assert_response :success
2377 assert_changesets_in_order [changeset1, changeset2, changeset3, changeset4]
2379 get changesets_path(:from => "2015-06-04T16:00:00Z", :to => "2015-06-04T17:30:00Z")
2380 assert_response :success
2381 assert_changesets_in_order [changeset1, changeset2]
2383 get changesets_path(:from => "2015-06-04T16:00:00Z", :to => "2015-06-04T17:30:00Z", :order => "oldest")
2384 assert_response :success
2385 assert_changesets_in_order [changeset2, changeset1]
2389 # check that errors are returned if garbage is inserted
2390 # into query strings
2391 def test_query_invalid
2394 ";drop table users;"].each do |bbox|
2395 get changesets_path(:bbox => bbox)
2396 assert_response :bad_request, "'#{bbox}' isn't a bbox"
2401 ";drop table users;",
2403 "-,-"].each do |time|
2404 get changesets_path(:time => time)
2405 assert_response :bad_request, "'#{time}' isn't a valid time range"
2412 get changesets_path(:user => uid)
2413 assert_response :bad_request, "'#{uid}' isn't a valid user ID"
2416 get changesets_path(:order => "oldest", :time => "2008-01-01T00:00Z,2018-01-01T00:00Z")
2417 assert_response :bad_request, "cannot use order=oldest with time"
2421 # check updating tags on a changeset
2422 def test_changeset_update
2423 private_user = create(:user, :data_public => false)
2424 private_changeset = create(:changeset, :user => private_user)
2425 user = create(:user)
2426 changeset = create(:changeset, :user => user)
2428 ## First try with a non-public user
2429 new_changeset = create_changeset_xml(:user => private_user)
2430 new_tag = XML::Node.new "tag"
2431 new_tag["k"] = "tagtesting"
2432 new_tag["v"] = "valuetesting"
2433 new_changeset.find("//osm/changeset").first << new_tag
2435 # try without any authorization
2436 put changeset_show_path(private_changeset), :params => new_changeset.to_s
2437 assert_response :unauthorized
2439 # try with the wrong authorization
2440 auth_header = bearer_authorization_header
2441 put changeset_show_path(private_changeset), :params => new_changeset.to_s, :headers => auth_header
2442 assert_response :conflict
2444 # now this should get an unauthorized
2445 auth_header = bearer_authorization_header private_user
2446 put changeset_show_path(private_changeset), :params => new_changeset.to_s, :headers => auth_header
2447 assert_require_public_data "user with their data non-public, shouldn't be able to edit their changeset"
2449 ## Now try with the public user
2450 new_changeset = create_changeset_xml(:id => 1)
2451 new_tag = XML::Node.new "tag"
2452 new_tag["k"] = "tagtesting"
2453 new_tag["v"] = "valuetesting"
2454 new_changeset.find("//osm/changeset").first << new_tag
2456 # try without any authorization
2457 put changeset_show_path(changeset), :params => new_changeset.to_s
2458 assert_response :unauthorized
2460 # try with the wrong authorization
2461 auth_header = bearer_authorization_header
2462 put changeset_show_path(changeset), :params => new_changeset.to_s, :headers => auth_header
2463 assert_response :conflict
2465 # now this should work...
2466 auth_header = bearer_authorization_header user
2467 put changeset_show_path(changeset), :params => new_changeset.to_s, :headers => auth_header
2468 assert_response :success
2470 assert_select "osm>changeset[id='#{changeset.id}']", 1
2471 assert_select "osm>changeset>tag", 1
2472 assert_select "osm>changeset>tag[k='tagtesting'][v='valuetesting']", 1
2476 # check that a user different from the one who opened the changeset
2478 def test_changeset_update_invalid
2479 auth_header = bearer_authorization_header
2481 changeset = create(:changeset)
2482 new_changeset = create_changeset_xml(:user => changeset.user, :id => changeset.id)
2483 new_tag = XML::Node.new "tag"
2484 new_tag["k"] = "testing"
2485 new_tag["v"] = "testing"
2486 new_changeset.find("//osm/changeset").first << new_tag
2488 put changeset_show_path(changeset), :params => new_changeset.to_s, :headers => auth_header
2489 assert_response :conflict
2493 # check that a changeset can contain a certain max number of changes.
2494 ## FIXME should be changed to an integration test due to the with_controller
2495 def test_changeset_limits
2496 user = create(:user)
2497 auth_header = bearer_authorization_header user
2499 # create an old changeset to ensure we have the maximum rate limit
2500 create(:changeset, :user => user, :created_at => Time.now.utc - 28.days)
2502 # open a new changeset
2503 xml = "<osm><changeset/></osm>"
2504 put changeset_create_path, :params => xml, :headers => auth_header
2505 assert_response :success, "can't create a new changeset"
2506 cs_id = @response.body.to_i
2508 # start the counter just short of where the changeset should finish.
2510 # alter the database to set the counter on the changeset directly,
2511 # otherwise it takes about 6 minutes to fill all of them.
2512 changeset = Changeset.find(cs_id)
2513 changeset.num_changes = Changeset::MAX_ELEMENTS - offset
2516 with_controller(NodesController.new) do
2518 xml = "<osm><node changeset='#{cs_id}' lat='0.0' lon='0.0'/></osm>"
2519 post api_nodes_path, :params => xml, :headers => auth_header
2520 assert_response :success, "can't create a new node"
2521 node_id = @response.body.to_i
2523 get api_node_path(node_id)
2524 assert_response :success, "can't read back new node"
2525 node_doc = XML::Parser.string(@response.body).parse
2526 node_xml = node_doc.find("//osm/node").first
2528 # loop until we fill the changeset with nodes
2530 node_xml["lat"] = rand.to_s
2531 node_xml["lon"] = rand.to_s
2532 node_xml["version"] = (i + 1).to_s
2534 put api_node_path(node_id), :params => node_doc.to_s, :headers => auth_header
2535 assert_response :success, "attempt #{i} should have succeeded"
2538 # trying again should fail
2539 node_xml["lat"] = rand.to_s
2540 node_xml["lon"] = rand.to_s
2541 node_xml["version"] = offset.to_s
2543 put api_node_path(node_id), :params => node_doc.to_s, :headers => auth_header
2544 assert_response :conflict, "final attempt should have failed"
2547 changeset = Changeset.find(cs_id)
2548 assert_equal Changeset::MAX_ELEMENTS + 1, changeset.num_changes
2550 # check that the changeset is now closed as well
2551 assert_not(changeset.open?,
2552 "changeset should have been auto-closed by exceeding " \
2557 # check that the changeset download for a changeset with a redacted
2558 # element in it doesn't contain that element.
2559 def test_diff_download_redacted
2560 changeset = create(:changeset)
2561 node = create(:node, :with_history, :version => 2, :changeset => changeset)
2562 node_v1 = node.old_nodes.find_by(:version => 1)
2563 node_v1.redact!(create(:redaction))
2565 get changeset_download_path(changeset)
2566 assert_response :success
2568 assert_select "osmChange", 1
2569 # this changeset contains the node in versions 1 & 2, but 1 should
2571 assert_select "osmChange node[id='#{node.id}']", 1
2572 assert_select "osmChange node[id='#{node.id}'][version='1']", 0
2576 # test subscribe success
2577 def test_subscribe_success
2578 auth_header = bearer_authorization_header
2579 changeset = create(:changeset, :closed)
2581 assert_difference "changeset.subscribers.count", 1 do
2582 post api_changeset_subscribe_path(changeset), :headers => auth_header
2584 assert_response :success
2586 # not closed changeset
2587 changeset = create(:changeset)
2588 assert_difference "changeset.subscribers.count", 1 do
2589 post api_changeset_subscribe_path(changeset), :headers => auth_header
2591 assert_response :success
2595 # test subscribe fail
2596 def test_subscribe_fail
2597 user = create(:user)
2600 changeset = create(:changeset, :closed)
2601 assert_no_difference "changeset.subscribers.count" do
2602 post api_changeset_subscribe_path(changeset)
2604 assert_response :unauthorized
2606 auth_header = bearer_authorization_header user
2609 assert_no_difference "changeset.subscribers.count" do
2610 post api_changeset_subscribe_path(999111), :headers => auth_header
2612 assert_response :not_found
2614 # trying to subscribe when already subscribed
2615 changeset = create(:changeset, :closed)
2616 changeset.subscribers.push(user)
2617 assert_no_difference "changeset.subscribers.count" do
2618 post api_changeset_subscribe_path(changeset), :headers => auth_header
2620 assert_response :conflict
2624 # test unsubscribe success
2625 def test_unsubscribe_success
2626 user = create(:user)
2627 auth_header = bearer_authorization_header user
2628 changeset = create(:changeset, :closed)
2629 changeset.subscribers.push(user)
2631 assert_difference "changeset.subscribers.count", -1 do
2632 post api_changeset_unsubscribe_path(changeset), :headers => auth_header
2634 assert_response :success
2636 # not closed changeset
2637 changeset = create(:changeset)
2638 changeset.subscribers.push(user)
2640 assert_difference "changeset.subscribers.count", -1 do
2641 post api_changeset_unsubscribe_path(changeset), :headers => auth_header
2643 assert_response :success
2647 # test unsubscribe fail
2648 def test_unsubscribe_fail
2650 changeset = create(:changeset, :closed)
2651 assert_no_difference "changeset.subscribers.count" do
2652 post api_changeset_unsubscribe_path(changeset)
2654 assert_response :unauthorized
2656 auth_header = bearer_authorization_header
2659 assert_no_difference "changeset.subscribers.count" do
2660 post api_changeset_unsubscribe_path(999111), :headers => auth_header
2662 assert_response :not_found
2664 # trying to unsubscribe when not subscribed
2665 changeset = create(:changeset, :closed)
2666 assert_no_difference "changeset.subscribers.count" do
2667 post api_changeset_unsubscribe_path(changeset), :headers => auth_header
2669 assert_response :not_found
2675 # check that the output consists of one specific changeset
2676 def assert_single_changeset(changeset, &)
2677 assert_dom "> changeset", 1 do
2678 assert_dom "> @id", changeset.id.to_s
2679 assert_dom "> @created_at", changeset.created_at.xmlschema
2681 assert_dom "> @open", "true"
2682 assert_dom "> @closed_at", 0
2684 assert_dom "> @open", "false"
2685 assert_dom "> @closed_at", changeset.closed_at.xmlschema
2687 assert_dom "> @comments_count", changeset.comments.length.to_s
2688 assert_dom "> @changes_count", changeset.num_changes.to_s
2689 yield if block_given?
2693 def assert_single_changeset_json(changeset, js)
2694 assert_equal changeset.id, js["changeset"]["id"]
2695 assert_equal changeset.created_at.xmlschema, js["changeset"]["created_at"]
2697 assert js["changeset"]["open"]
2698 assert_nil js["changeset"]["closed_at"]
2700 assert_not js["changeset"]["open"]
2701 assert_equal changeset.closed_at.xmlschema, js["changeset"]["closed_at"]
2703 assert_equal changeset.comments.length, js["changeset"]["comments_count"]
2704 assert_equal changeset.num_changes, js["changeset"]["changes_count"]
2708 # check that certain changesets exist in the output in the specified order
2709 def assert_changesets_in_order(changesets)
2710 assert_select "osm>changeset", changesets.size
2711 changesets.each_with_index do |changeset, index|
2712 assert_select "osm>changeset:nth-child(#{index + 1})[id='#{changeset.id}']", 1
2717 # update the changeset_id of a way element
2718 def update_changeset(xml, changeset_id)
2719 xml_attr_rewrite(xml, "changeset", changeset_id)
2723 # update an attribute in a way element
2724 def xml_attr_rewrite(xml, name, value)
2725 xml.find("//osm/way").first[name] = value.to_s
2730 # build XML for changesets
2731 def create_changeset_xml(user: nil, id: nil)
2732 root = XML::Document.new
2733 root.root = XML::Node.new "osm"
2734 cs = XML::Node.new "changeset"
2736 cs["user"] = user.display_name
2737 cs["uid"] = user.id.to_s
2739 cs["id"] = id.to_s if id