4 class ChangesetsControllerTest < ActionDispatch::IntegrationTest
6 # test all routes which lead to this controller
9 { :path => "/api/0.6/changesets", :method => :post },
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 { :controller => "api/changesets", :action => "create" },
63 { :path => "/api/0.6/changeset/create", :method => :put }
67 # -----------------------
68 # Test simple changeset creation
69 # -----------------------
72 auth_header = bearer_authorization_header create(:user, :data_public => false)
73 # Create the first user's changeset
74 xml = "<osm><changeset>" \
75 "<tag k='created_by' v='osm test suite checking changesets'/>" \
77 post api_changesets_path, :params => xml, :headers => auth_header
78 assert_require_public_data
80 auth_header = bearer_authorization_header
81 # Create the first user's changeset
82 xml = "<osm><changeset>" \
83 "<tag k='created_by' v='osm test suite checking changesets'/>" \
85 post api_changesets_path, :params => xml, :headers => auth_header
87 assert_response :success, "Creation of changeset did not return success status"
88 newid = @response.body.to_i
90 # check end time, should be an hour ahead of creation time
91 cs = Changeset.find(newid)
92 duration = cs.closed_at - cs.created_at
93 # the difference can either be a rational, or a floating point number
94 # of seconds, depending on the code path taken :-(
95 if duration.instance_of?(Rational)
96 assert_equal Rational(1, 24), duration, "initial idle timeout should be an hour (#{cs.created_at} -> #{cs.closed_at})"
98 # must be number of seconds...
99 assert_equal 3600, duration.round, "initial idle timeout should be an hour (#{cs.created_at} -> #{cs.closed_at})"
102 # checks if uploader was subscribed
103 assert_equal 1, cs.subscribers.length
106 def test_create_invalid
107 auth_header = bearer_authorization_header create(:user, :data_public => false)
108 xml = "<osm><changeset></osm>"
109 post api_changesets_path, :params => xml, :headers => auth_header
110 assert_require_public_data
112 ## Try the public user
113 auth_header = bearer_authorization_header
114 xml = "<osm><changeset></osm>"
115 post api_changesets_path, :params => xml, :headers => auth_header
116 assert_response :bad_request, "creating a invalid changeset should fail"
119 def test_create_invalid_no_content
120 ## First check with no auth
121 post api_changesets_path
122 assert_response :unauthorized, "shouldn't be able to create a changeset with no auth"
124 ## Now try to with a non-public user
125 auth_header = bearer_authorization_header create(:user, :data_public => false)
126 post api_changesets_path, :headers => auth_header
127 assert_require_public_data
129 ## Try an inactive user
130 auth_header = bearer_authorization_header create(:user, :pending)
131 post api_changesets_path, :headers => auth_header
134 ## Now try to use a normal user
135 auth_header = bearer_authorization_header
136 post api_changesets_path, :headers => auth_header
137 assert_response :bad_request, "creating a changeset with no content should fail"
140 def test_create_wrong_method
141 auth_header = bearer_authorization_header
143 put api_changesets_path, :headers => auth_header
144 assert_response :not_found
145 assert_template "rescues/routing_error"
148 def test_create_legacy_path
149 auth_header = bearer_authorization_header
150 xml = "<osm><changeset></changeset></osm>"
152 assert_difference "Changeset.count", 1 do
153 put "/api/0.6/changeset/create", :params => xml, :headers => auth_header
156 assert_response :success, "Creation of changeset did not return success status"
157 assert_equal Changeset.last.id, @response.body.to_i
161 # check that the changeset can be shown and returns the correct
162 # document structure.
164 changeset = create(:changeset)
166 get changeset_show_path(changeset)
167 assert_response :success, "cannot get first changeset"
169 assert_dom "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
170 assert_single_changeset changeset do
171 assert_dom "> discussion", 0
174 get changeset_show_path(changeset), :params => { :include_discussion => true }
175 assert_response :success, "cannot get first changeset with comments"
177 assert_dom "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
178 assert_single_changeset changeset do
179 assert_dom "> discussion", 1
180 assert_dom "> discussion > comment", 0
184 def test_show_comments
185 # all comments visible
186 changeset = create(:changeset, :closed)
187 comment1, comment2, comment3 = create_list(:changeset_comment, 3, :changeset_id => changeset.id)
189 get changeset_show_path(changeset), :params => { :include_discussion => true }
190 assert_response :success, "cannot get closed changeset with comments"
192 assert_dom "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1 do
193 assert_single_changeset changeset do
194 assert_dom "> discussion", 1 do
195 assert_dom "> comment", 3 do |dom_comments|
196 assert_dom dom_comments[0], "> @id", comment1.id.to_s
197 assert_dom dom_comments[0], "> @visible", "true"
198 assert_dom dom_comments[1], "> @id", comment2.id.to_s
199 assert_dom dom_comments[1], "> @visible", "true"
200 assert_dom dom_comments[2], "> @id", comment3.id.to_s
201 assert_dom dom_comments[2], "> @visible", "true"
207 # one hidden comment not included because not asked for
208 comment2.update(:visible => false)
211 get changeset_show_path(changeset), :params => { :include_discussion => true }
212 assert_response :success, "cannot get closed changeset with comments"
214 assert_dom "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
215 assert_single_changeset changeset do
216 assert_dom "> discussion", 1 do
217 assert_dom "> comment", 2 do |dom_comments|
218 assert_dom dom_comments[0], "> @id", comment1.id.to_s
219 assert_dom dom_comments[0], "> @visible", "true"
220 assert_dom dom_comments[1], "> @id", comment3.id.to_s
221 assert_dom dom_comments[1], "> @visible", "true"
226 # one hidden comment not included because no permissions
227 get changeset_show_path(changeset), :params => { :include_discussion => true, :show_hidden_comments => true }
228 assert_response :success, "cannot get closed changeset with comments"
230 assert_dom "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
231 assert_single_changeset changeset do
232 assert_dom "> discussion", 1 do
233 assert_dom "> comment", 2 do |dom_comments|
234 assert_dom dom_comments[0], "> @id", comment1.id.to_s
235 assert_dom dom_comments[0], "> @visible", "true"
236 # maybe will show an empty comment element with visible=false in the future
237 assert_dom dom_comments[1], "> @id", comment3.id.to_s
238 assert_dom dom_comments[1], "> @visible", "true"
243 # one hidden comment shown to moderators
244 moderator_user = create(:moderator_user)
245 auth_header = bearer_authorization_header moderator_user
246 get changeset_show_path(changeset), :params => { :include_discussion => true, :show_hidden_comments => true },
247 :headers => auth_header
248 assert_response :success, "cannot get closed changeset with comments"
250 assert_dom "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
251 assert_single_changeset changeset do
252 assert_dom "> discussion", 1 do
253 assert_dom "> comment", 3 do |dom_comments|
254 assert_dom dom_comments[0], "> @id", comment1.id.to_s
255 assert_dom dom_comments[0], "> @visible", "true"
256 assert_dom dom_comments[1], "> @id", comment2.id.to_s
257 assert_dom dom_comments[1], "> @visible", "false"
258 assert_dom dom_comments[2], "> @id", comment3.id.to_s
259 assert_dom dom_comments[2], "> @visible", "true"
266 changeset = create(:changeset, :closed)
267 create(:changeset_tag, :changeset => changeset, :k => "created_by", :v => "JOSM/1.5 (18364)")
268 create(:changeset_tag, :changeset => changeset, :k => "comment", :v => "changeset comment")
270 get changeset_show_path(changeset)
272 assert_response :success
273 assert_dom "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
274 assert_single_changeset changeset do
275 assert_dom "> tag", 2
276 assert_dom "> tag[k='created_by'][v='JOSM/1.5 (18364)']", 1
277 assert_dom "> tag[k='comment'][v='changeset comment']", 1
282 changeset = create(:changeset)
284 get changeset_show_path(changeset), :params => { :format => "json" }
285 assert_response :success, "cannot get first changeset"
287 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"]["comments"]
295 assert_equal changeset.user.id, js["changeset"]["uid"]
296 assert_equal changeset.user.display_name, js["changeset"]["user"]
298 get changeset_show_path(changeset), :params => { :format => "json", :include_discussion => true }
299 assert_response :success, "cannot get first changeset with comments"
301 js = ActiveSupport::JSON.decode(@response.body)
303 assert_equal Settings.api_version, js["version"]
304 assert_equal Settings.generator, js["generator"]
305 assert_single_changeset_json changeset, js
306 assert_nil js["changeset"]["tags"]
307 assert_nil js["changeset"]["min_lat"]
308 assert_nil js["changeset"]["min_lon"]
309 assert_nil js["changeset"]["max_lat"]
310 assert_nil js["changeset"]["max_lon"]
311 assert_equal 0, js["changeset"]["comments"].count
314 def test_show_comments_json
315 # all comments visible
316 changeset = create(:changeset, :closed)
317 comment0, comment1, comment2 = create_list(:changeset_comment, 3, :changeset_id => changeset.id)
319 get changeset_show_path(changeset), :params => { :format => "json", :include_discussion => true }
320 assert_response :success, "cannot get closed changeset with comments"
322 js = ActiveSupport::JSON.decode(@response.body)
324 assert_equal Settings.api_version, js["version"]
325 assert_equal Settings.generator, js["generator"]
326 assert_single_changeset_json changeset, js
327 assert_equal 3, js["changeset"]["comments"].count
328 assert_equal comment0.id, js["changeset"]["comments"][0]["id"]
329 assert js["changeset"]["comments"][0]["visible"]
330 assert_equal comment1.id, js["changeset"]["comments"][1]["id"]
331 assert js["changeset"]["comments"][1]["visible"]
332 assert_equal comment2.id, js["changeset"]["comments"][2]["id"]
333 assert js["changeset"]["comments"][2]["visible"]
335 # one hidden comment not included because not asked for
336 comment1.update(:visible => false)
339 get changeset_show_path(changeset), :params => { :format => "json", :include_discussion => true }
340 assert_response :success, "cannot get closed changeset with comments"
342 js = ActiveSupport::JSON.decode(@response.body)
344 assert_equal Settings.api_version, js["version"]
345 assert_equal Settings.generator, js["generator"]
346 assert_single_changeset_json changeset, js
347 assert_equal 2, js["changeset"]["comments"].count
348 assert_equal comment0.id, js["changeset"]["comments"][0]["id"]
349 assert js["changeset"]["comments"][0]["visible"]
350 assert_equal comment2.id, js["changeset"]["comments"][1]["id"]
351 assert js["changeset"]["comments"][1]["visible"]
353 # one hidden comment not included because no permissions
354 get changeset_show_path(changeset), :params => { :format => "json", :include_discussion => true, :show_hidden_comments => true }
355 assert_response :success, "cannot get closed changeset with comments"
357 js = ActiveSupport::JSON.decode(@response.body)
359 assert_equal Settings.api_version, js["version"]
360 assert_equal Settings.generator, js["generator"]
361 assert_single_changeset_json changeset, js
362 assert_equal 2, js["changeset"]["comments"].count
363 assert_equal comment0.id, js["changeset"]["comments"][0]["id"]
364 assert js["changeset"]["comments"][0]["visible"]
365 # maybe will show an empty comment element with visible=false in the future
366 assert_equal comment2.id, js["changeset"]["comments"][1]["id"]
367 assert js["changeset"]["comments"][1]["visible"]
369 # one hidden comment shown to moderators
370 moderator_user = create(:moderator_user)
371 auth_header = bearer_authorization_header moderator_user
372 get changeset_show_path(changeset), :params => { :format => "json", :include_discussion => true, :show_hidden_comments => true },
373 :headers => auth_header
374 assert_response :success, "cannot get closed changeset with comments"
376 js = ActiveSupport::JSON.decode(@response.body)
378 assert_equal Settings.api_version, js["version"]
379 assert_equal Settings.generator, js["generator"]
380 assert_single_changeset_json changeset, js
381 assert_equal 3, js["changeset"]["comments"].count
382 assert_equal comment0.id, js["changeset"]["comments"][0]["id"]
383 assert js["changeset"]["comments"][0]["visible"]
384 assert_equal comment1.id, js["changeset"]["comments"][1]["id"]
385 assert_not js["changeset"]["comments"][1]["visible"]
386 assert_equal comment2.id, js["changeset"]["comments"][2]["id"]
387 assert js["changeset"]["comments"][2]["visible"]
390 def test_show_tags_json
391 changeset = create(:changeset, :closed)
392 create(:changeset_tag, :changeset => changeset, :k => "created_by", :v => "JOSM/1.5 (18364)")
393 create(:changeset_tag, :changeset => changeset, :k => "comment", :v => "changeset comment")
395 get changeset_show_path(changeset, :format => "json")
397 assert_response :success
398 js = ActiveSupport::JSON.decode(@response.body)
400 assert_equal Settings.api_version, js["version"]
401 assert_equal Settings.generator, js["generator"]
402 assert_single_changeset_json changeset, js
403 assert_equal 2, js["changeset"]["tags"].count
404 assert_equal "JOSM/1.5 (18364)", js["changeset"]["tags"]["created_by"]
405 assert_equal "changeset comment", js["changeset"]["tags"]["comment"]
408 def test_show_bbox_json
409 # test bbox attribute
410 changeset = create(:changeset, :min_lat => (-5 * GeoRecord::SCALE).round, :min_lon => (5 * GeoRecord::SCALE).round,
411 :max_lat => (15 * GeoRecord::SCALE).round, :max_lon => (12 * GeoRecord::SCALE).round)
413 get changeset_show_path(changeset), :params => { :format => "json" }
414 assert_response :success, "cannot get first changeset"
416 js = ActiveSupport::JSON.decode(@response.body)
418 assert_equal(-5, js["changeset"]["min_lat"])
419 assert_equal 5, js["changeset"]["min_lon"]
420 assert_equal 15, js["changeset"]["max_lat"]
421 assert_equal 12, js["changeset"]["max_lon"]
425 # check that a changeset that doesn't exist returns an appropriate message
426 def test_show_not_found
427 [0, -32, 233455644, "afg", "213"].each do |id|
428 get changeset_show_path(id)
429 assert_response :not_found, "should get a not found"
430 rescue ActionController::UrlGenerationError => e
431 assert_match(/No route matches/, e.to_s)
436 # test that the user who opened a change can close it
438 private_user = create(:user, :data_public => false)
439 private_changeset = create(:changeset, :user => private_user)
441 changeset = create(:changeset, :user => user)
443 ## Try without authentication
444 put changeset_close_path(changeset)
445 assert_response :unauthorized
447 ## Try using the non-public user
448 auth_header = bearer_authorization_header private_user
449 put changeset_close_path(private_changeset), :headers => auth_header
450 assert_require_public_data
452 ## The try with the public user
453 auth_header = bearer_authorization_header user
456 put changeset_close_path(cs_id), :headers => auth_header
457 assert_response :success
459 # test that it really is closed now
460 cs = Changeset.find(changeset.id)
462 "changeset should be closed now (#{cs.closed_at} > #{Time.now.utc}.")
466 # test that a different user can't close another user's changeset
467 def test_close_invalid
469 changeset = create(:changeset)
471 auth_header = bearer_authorization_header user
473 put changeset_close_path(changeset), :headers => auth_header
474 assert_response :conflict
475 assert_equal "The user doesn't own that changeset", @response.body
479 # test that you can't close using another method
480 def test_close_method_invalid
482 changeset = create(:changeset, :user => user)
484 auth_header = bearer_authorization_header user
486 get changeset_close_path(changeset), :headers => auth_header
487 assert_response :not_found
488 assert_template "rescues/routing_error"
490 post changeset_close_path(changeset), :headers => auth_header
491 assert_response :not_found
492 assert_template "rescues/routing_error"
496 # check that you can't close a changeset that isn't found
497 def test_close_not_found
498 cs_ids = [0, -132, "123"]
500 # First try to do it with no auth
502 put changeset_close_path(id)
503 assert_response :unauthorized, "Shouldn't be able close the non-existant changeset #{id}, when not authorized"
504 rescue ActionController::UrlGenerationError => e
505 assert_match(/No route matches/, e.to_s)
509 auth_header = bearer_authorization_header
511 put changeset_close_path(id), :headers => auth_header
512 assert_response :not_found, "The changeset #{id} doesn't exist, so can't be closed"
513 rescue ActionController::UrlGenerationError => e
514 assert_match(/No route matches/, e.to_s)
519 # upload something simple, but valid and check that it can
521 # Also try without auth and another user.
522 def test_upload_simple_valid
523 private_user = create(:user, :data_public => false)
524 private_changeset = create(:changeset, :user => private_user)
526 changeset = create(:changeset, :user => user)
530 relation = create(:relation)
531 other_relation = create(:relation)
532 # create some tags, since we test that they are removed later
533 create(:node_tag, :node => node)
534 create(:way_tag, :way => way)
535 create(:relation_tag, :relation => relation)
538 changeset_id = changeset.id
540 # simple diff to change a node, way and relation by removing
545 <node id='#{node.id}' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
546 <way id='#{way.id}' changeset='#{changeset_id}' version='1'>
547 <nd ref='#{node.id}'/>
551 <relation id='#{relation.id}' changeset='#{changeset_id}' version='1'>
552 <member type='way' role='some' ref='#{way.id}'/>
553 <member type='node' role='some' ref='#{node.id}'/>
554 <member type='relation' role='some' ref='#{other_relation.id}'/>
561 post changeset_upload_path(changeset), :params => diff
562 assert_response :unauthorized,
563 "shouldn't be able to upload a simple valid diff to changeset: #{@response.body}"
565 ## Now try with a private user
566 auth_header = bearer_authorization_header private_user
567 changeset_id = private_changeset.id
569 # simple diff to change a node, way and relation by removing
574 <node id='#{node.id}' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
575 <way id='#{way.id}' changeset='#{changeset_id}' version='1'>
576 <nd ref='#{node.id}'/>
580 <relation id='#{relation.id}' changeset='#{changeset_id}' version='1'>
581 <member type='way' role='some' ref='#{way.id}'/>
582 <member type='node' role='some' ref='#{node.id}'/>
583 <member type='relation' role='some' ref='#{other_relation.id}'/>
590 post changeset_upload_path(private_changeset), :params => diff, :headers => auth_header
591 assert_response :forbidden,
592 "can't upload a simple valid diff to changeset: #{@response.body}"
594 ## Now try with the public user
595 auth_header = bearer_authorization_header user
596 changeset_id = changeset.id
598 # simple diff to change a node, way and relation by removing
603 <node id='#{node.id}' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
604 <way id='#{way.id}' changeset='#{changeset_id}' version='1'>
605 <nd ref='#{node.id}'/>
609 <relation id='#{relation.id}' changeset='#{changeset_id}' version='1'>
610 <member type='way' role='some' ref='#{way.id}'/>
611 <member type='node' role='some' ref='#{node.id}'/>
612 <member type='relation' role='some' ref='#{other_relation.id}'/>
619 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
620 assert_response :success,
621 "can't upload a simple valid diff to changeset: #{@response.body}"
623 # check that the changes made it into the database
624 assert_equal 0, Node.find(node.id).tags.size, "node #{node.id} should now have no tags"
625 assert_equal 0, Way.find(way.id).tags.size, "way #{way.id} should now have no tags"
626 assert_equal 0, Relation.find(relation.id).tags.size, "relation #{relation.id} should now have no tags"
630 # upload something which creates new objects using placeholders
631 def test_upload_create_valid
633 changeset = create(:changeset, :user => user)
635 way = create(:way_with_nodes, :nodes_count => 2)
636 relation = create(:relation)
638 auth_header = bearer_authorization_header user
640 # simple diff to create a node way and relation using placeholders
644 <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
645 <tag k='foo' v='bar'/>
646 <tag k='baz' v='bat'/>
648 <way id='-1' changeset='#{changeset.id}'>
649 <nd ref='#{node.id}'/>
653 <relation id='-1' changeset='#{changeset.id}'>
654 <member type='way' role='some' ref='#{way.id}'/>
655 <member type='node' role='some' ref='#{node.id}'/>
656 <member type='relation' role='some' ref='#{relation.id}'/>
663 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
664 assert_response :success,
665 "can't upload a simple valid creation to changeset: #{@response.body}"
667 # check the returned payload
668 new_node_id, new_way_id, new_rel_id = nil
669 assert_dom "diffResult[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1 do
670 # inspect the response to find out what the new element IDs are
671 # check the old IDs are all present and negative one
672 # check the versions are present and equal one
673 assert_dom "> node", 1 do |(node_el)|
674 new_node_id = node_el["new_id"].to_i
675 assert_dom "> @old_id", "-1"
676 assert_dom "> @new_version", "1"
678 assert_dom "> way", 1 do |(way_el)|
679 new_way_id = way_el["new_id"].to_i
680 assert_dom "> @old_id", "-1"
681 assert_dom "> @new_version", "1"
683 assert_dom "> relation", 1 do |(rel_el)|
684 new_rel_id = rel_el["new_id"].to_i
685 assert_dom "> @old_id", "-1"
686 assert_dom "> @new_version", "1"
690 # check that the changes made it into the database
691 assert_equal 2, Node.find(new_node_id).tags.size, "new node should have two tags"
692 assert_equal 0, Way.find(new_way_id).tags.size, "new way should have no tags"
693 assert_equal 0, Relation.find(new_rel_id).tags.size, "new relation should have no tags"
697 # test a complex delete where we delete elements which rely on eachother
698 # in the same transaction.
699 def test_upload_delete
700 changeset = create(:changeset)
701 super_relation = create(:relation)
702 used_relation = create(:relation)
703 used_way = create(:way)
704 used_node = create(:node)
705 create(:relation_member, :relation => super_relation, :member => used_relation)
706 create(:relation_member, :relation => super_relation, :member => used_way)
707 create(:relation_member, :relation => super_relation, :member => used_node)
709 auth_header = bearer_authorization_header changeset.user
711 diff = XML::Document.new
712 diff.root = XML::Node.new "osmChange"
713 delete = XML::Node.new "delete"
715 delete << xml_node_for_relation(super_relation)
716 delete << xml_node_for_relation(used_relation)
717 delete << xml_node_for_way(used_way)
718 delete << xml_node_for_node(used_node)
720 # update the changeset to one that this user owns
721 %w[node way relation].each do |type|
722 delete.find("//osmChange/delete/#{type}").each do |n|
723 n["changeset"] = changeset.id.to_s
728 post changeset_upload_path(changeset), :params => diff.to_s, :headers => auth_header
729 assert_response :success,
730 "can't upload a deletion diff to changeset: #{@response.body}"
732 # check the response is well-formed
733 assert_select "diffResult>node", 1
734 assert_select "diffResult>way", 1
735 assert_select "diffResult>relation", 2
737 # check that everything was deleted
738 assert_not Node.find(used_node.id).visible
739 assert_not Way.find(used_way.id).visible
740 assert_not Relation.find(super_relation.id).visible
741 assert_not Relation.find(used_relation.id).visible
745 # test uploading a delete with no lat/lon, as they are optional in
746 # the osmChange spec.
747 def test_upload_nolatlon_delete
749 changeset = create(:changeset)
751 auth_header = bearer_authorization_header changeset.user
752 diff = "<osmChange><delete><node id='#{node.id}' version='#{node.version}' changeset='#{changeset.id}'/></delete></osmChange>"
755 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
756 assert_response :success,
757 "can't upload a deletion diff to changeset: #{@response.body}"
759 # check the response is well-formed
760 assert_select "diffResult>node", 1
762 # check that everything was deleted
763 assert_not Node.find(node.id).visible
766 def test_repeated_changeset_create
768 auth_header = bearer_authorization_header
770 # create a temporary changeset
771 xml = "<osm><changeset>" \
772 "<tag k='created_by' v='osm test suite checking changesets'/>" \
774 assert_difference "Changeset.count", 1 do
775 post api_changesets_path, :params => xml, :headers => auth_header
777 assert_response :success
781 def test_upload_large_changeset
783 auth_header = bearer_authorization_header user
785 # create an old changeset to ensure we have the maximum rate limit
786 create(:changeset, :user => user, :created_at => Time.now.utc - 28.days)
789 post api_changesets_path, :params => "<osm><changeset/></osm>", :headers => auth_header
790 assert_response :success, "Should be able to create a changeset: #{@response.body}"
791 changeset_id = @response.body.to_i
793 # upload some widely-spaced nodes, spiralling positive and negative
797 <node id='-1' lon='-20' lat='-10' changeset='#{changeset_id}'/>
798 <node id='-10' lon='20' lat='10' changeset='#{changeset_id}'/>
799 <node id='-2' lon='-40' lat='-20' changeset='#{changeset_id}'/>
800 <node id='-11' lon='40' lat='20' changeset='#{changeset_id}'/>
801 <node id='-3' lon='-60' lat='-30' changeset='#{changeset_id}'/>
802 <node id='-12' lon='60' lat='30' changeset='#{changeset_id}'/>
803 <node id='-4' lon='-80' lat='-40' changeset='#{changeset_id}'/>
804 <node id='-13' lon='80' lat='40' changeset='#{changeset_id}'/>
805 <node id='-5' lon='-100' lat='-50' changeset='#{changeset_id}'/>
806 <node id='-14' lon='100' lat='50' changeset='#{changeset_id}'/>
807 <node id='-6' lon='-120' lat='-60' changeset='#{changeset_id}'/>
808 <node id='-15' lon='120' lat='60' changeset='#{changeset_id}'/>
809 <node id='-7' lon='-140' lat='-70' changeset='#{changeset_id}'/>
810 <node id='-16' lon='140' lat='70' changeset='#{changeset_id}'/>
811 <node id='-8' lon='-160' lat='-80' changeset='#{changeset_id}'/>
812 <node id='-17' lon='160' lat='80' changeset='#{changeset_id}'/>
813 <node id='-9' lon='-179.9' lat='-89.9' changeset='#{changeset_id}'/>
814 <node id='-18' lon='179.9' lat='89.9' changeset='#{changeset_id}'/>
819 # upload it, which used to cause an error like "PGError: ERROR:
820 # integer out of range" (bug #2152). but shouldn't any more.
821 post changeset_upload_path(changeset_id), :params => diff, :headers => auth_header
822 assert_response :success,
823 "can't upload a spatially-large diff to changeset: #{@response.body}"
825 # check that the changeset bbox is within bounds
826 cs = Changeset.find(changeset_id)
827 assert_operator cs.min_lon, :>=, -180 * GeoRecord::SCALE, "Minimum longitude (#{cs.min_lon / GeoRecord::SCALE}) should be >= -180 to be valid."
828 assert_operator cs.max_lon, :<=, 180 * GeoRecord::SCALE, "Maximum longitude (#{cs.max_lon / GeoRecord::SCALE}) should be <= 180 to be valid."
829 assert_operator cs.min_lat, :>=, -90 * GeoRecord::SCALE, "Minimum latitude (#{cs.min_lat / GeoRecord::SCALE}) should be >= -90 to be valid."
830 assert_operator cs.max_lat, :<=, 90 * GeoRecord::SCALE, "Maximum latitude (#{cs.max_lat / GeoRecord::SCALE}) should be <= 90 to be valid."
834 # test that deleting stuff in a transaction doesn't bypass the checks
835 # to ensure that used elements are not deleted.
836 def test_upload_delete_invalid
837 changeset = create(:changeset)
838 relation = create(:relation)
839 other_relation = create(:relation)
840 used_way = create(:way)
841 used_node = create(:node)
842 create(:relation_member, :relation => relation, :member => used_way)
843 create(:relation_member, :relation => relation, :member => used_node)
845 auth_header = bearer_authorization_header changeset.user
847 diff = XML::Document.new
848 diff.root = XML::Node.new "osmChange"
849 delete = XML::Node.new "delete"
851 delete << xml_node_for_relation(other_relation)
852 delete << xml_node_for_way(used_way)
853 delete << xml_node_for_node(used_node)
855 # update the changeset to one that this user owns
856 %w[node way relation].each do |type|
857 delete.find("//osmChange/delete/#{type}").each do |n|
858 n["changeset"] = changeset.id.to_s
863 post changeset_upload_path(changeset), :params => diff.to_s, :headers => auth_header
864 assert_response :precondition_failed,
865 "shouldn't be able to upload a invalid deletion diff: #{@response.body}"
866 assert_equal "Precondition failed: Way #{used_way.id} is still used by relations #{relation.id}.", @response.body
868 # check that nothing was, in fact, deleted
869 assert Node.find(used_node.id).visible
870 assert Way.find(used_way.id).visible
871 assert Relation.find(relation.id).visible
872 assert Relation.find(other_relation.id).visible
876 # test that a conditional delete of an in use object works.
877 def test_upload_delete_if_unused
878 changeset = create(:changeset)
879 super_relation = create(:relation)
880 used_relation = create(:relation)
881 used_way = create(:way)
882 used_node = create(:node)
883 create(:relation_member, :relation => super_relation, :member => used_relation)
884 create(:relation_member, :relation => super_relation, :member => used_way)
885 create(:relation_member, :relation => super_relation, :member => used_node)
887 auth_header = bearer_authorization_header changeset.user
889 diff = XML::Document.new
890 diff.root = XML::Node.new "osmChange"
891 delete = XML::Node.new "delete"
893 delete["if-unused"] = ""
894 delete << xml_node_for_relation(used_relation)
895 delete << xml_node_for_way(used_way)
896 delete << xml_node_for_node(used_node)
898 # update the changeset to one that this user owns
899 %w[node way relation].each do |type|
900 delete.find("//osmChange/delete/#{type}").each do |n|
901 n["changeset"] = changeset.id.to_s
906 post changeset_upload_path(changeset), :params => diff.to_s, :headers => auth_header
907 assert_response :success,
908 "can't do a conditional delete of in use objects: #{@response.body}"
910 # check the returned payload
911 assert_dom "diffResult[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1 do
912 # check the old IDs are all present and what we expect
913 # check the new IDs are all present and unchanged
914 # check the new versions are all present and unchanged
915 assert_dom "> node", 1 do
916 assert_dom "> @old_id", used_node.id.to_s
917 assert_dom "> @new_id", used_node.id.to_s
918 assert_dom "> @new_version", used_node.version.to_s
920 assert_dom "> way", 1 do
921 assert_dom "> @old_id", used_way.id.to_s
922 assert_dom "> @new_id", used_way.id.to_s
923 assert_dom "> @new_version", used_way.version.to_s
925 assert_dom "> relation", 1 do
926 assert_dom "> @old_id", used_relation.id.to_s
927 assert_dom "> @new_id", used_relation.id.to_s
928 assert_dom "> @new_version", used_relation.version.to_s
932 # check that nothing was, in fact, deleted
933 assert Node.find(used_node.id).visible
934 assert Way.find(used_way.id).visible
935 assert Relation.find(used_relation.id).visible
939 # upload an element with a really long tag value
940 def test_upload_invalid_too_long_tag
941 changeset = create(:changeset)
943 auth_header = bearer_authorization_header changeset.user
945 # simple diff to create a node way and relation using placeholders
949 <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
950 <tag k='foo' v='#{'x' * 256}'/>
957 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
958 assert_response :bad_request,
959 "shouldn't be able to upload too long a tag to changeset: #{@response.body}"
963 # upload something which creates new objects and inserts them into
964 # existing containers using placeholders.
965 def test_upload_complex
968 relation = create(:relation)
969 create(:way_node, :way => way, :node => node)
971 changeset = create(:changeset)
973 auth_header = bearer_authorization_header changeset.user
975 # simple diff to create a node way and relation using placeholders
979 <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
980 <tag k='foo' v='bar'/>
981 <tag k='baz' v='bat'/>
985 <way id='#{way.id}' changeset='#{changeset.id}' version='1'>
987 <nd ref='#{node.id}'/>
989 <relation id='#{relation.id}' changeset='#{changeset.id}' version='1'>
990 <member type='way' role='some' ref='#{way.id}'/>
991 <member type='node' role='some' ref='-1'/>
992 <member type='relation' role='some' ref='#{relation.id}'/>
999 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1000 assert_response :success,
1001 "can't upload a complex diff to changeset: #{@response.body}"
1003 # check the returned payload
1005 assert_dom "diffResult[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1 do
1006 assert_dom "> node", 1 do |(node_el)|
1007 new_node_id = node_el["new_id"].to_i
1009 assert_dom "> way", 1
1010 assert_dom "> relation", 1
1013 # check that the changes made it into the database
1014 assert_equal 2, Node.find(new_node_id).tags.size, "new node should have two tags"
1015 assert_equal [new_node_id, node.id], Way.find(way.id).nds, "way nodes should match"
1016 Relation.find(relation.id).members.each do |type, id, _role|
1017 assert_equal new_node_id, id, "relation should contain new node" if type == "node"
1022 # create a diff which references several changesets, which should cause
1023 # a rollback and none of the diff gets committed
1024 def test_upload_invalid_changesets
1025 changeset = create(:changeset)
1026 other_changeset = create(:changeset, :user => changeset.user)
1027 node = create(:node)
1029 relation = create(:relation)
1030 other_relation = create(:relation)
1032 auth_header = bearer_authorization_header changeset.user
1034 # simple diff to create a node way and relation using placeholders
1038 <node id='#{node.id}' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
1039 <way id='#{way.id}' changeset='#{changeset.id}' version='1'>
1040 <nd ref='#{node.id}'/>
1044 <relation id='#{relation.id}' changeset='#{changeset.id}' version='1'>
1045 <member type='way' role='some' ref='#{way.id}'/>
1046 <member type='node' role='some' ref='#{node.id}'/>
1047 <member type='relation' role='some' ref='#{other_relation.id}'/>
1051 <node id='-1' lon='0' lat='0' changeset='#{other_changeset.id}'>
1052 <tag k='foo' v='bar'/>
1053 <tag k='baz' v='bat'/>
1060 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1061 assert_response :conflict,
1062 "uploading a diff with multiple changesets should have failed"
1064 # check that objects are unmodified
1065 assert_nodes_are_equal(node, Node.find(node.id))
1066 assert_ways_are_equal(way, Way.find(way.id))
1067 assert_relations_are_equal(relation, Relation.find(relation.id))
1071 # upload multiple versions of the same element in the same diff.
1072 def test_upload_multiple_valid
1073 node = create(:node)
1074 changeset = create(:changeset)
1075 auth_header = bearer_authorization_header changeset.user
1077 # change the location of a node multiple times, each time referencing
1078 # the last version. doesn't this depend on version numbers being
1083 <node id='#{node.id}' lon='0.0' lat='0.0' changeset='#{changeset.id}' version='1'/>
1084 <node id='#{node.id}' lon='0.1' lat='0.0' changeset='#{changeset.id}' version='2'/>
1085 <node id='#{node.id}' lon='0.1' lat='0.1' changeset='#{changeset.id}' version='3'/>
1086 <node id='#{node.id}' lon='0.1' lat='0.2' changeset='#{changeset.id}' version='4'/>
1087 <node id='#{node.id}' lon='0.2' lat='0.2' changeset='#{changeset.id}' version='5'/>
1088 <node id='#{node.id}' lon='0.3' lat='0.2' changeset='#{changeset.id}' version='6'/>
1089 <node id='#{node.id}' lon='0.3' lat='0.3' changeset='#{changeset.id}' version='7'/>
1090 <node id='#{node.id}' lon='0.9' lat='0.9' changeset='#{changeset.id}' version='8'/>
1096 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1097 assert_response :success,
1098 "can't upload multiple versions of an element in a diff: #{@response.body}"
1100 # check the response is well-formed. its counter-intuitive, but the
1101 # API will return multiple elements with the same ID and different
1102 # version numbers for each change we made.
1103 assert_select "diffResult>node", 8
1107 # upload multiple versions of the same element in the same diff, but
1108 # keep the version numbers the same.
1109 def test_upload_multiple_duplicate
1110 node = create(:node)
1111 changeset = create(:changeset)
1113 auth_header = bearer_authorization_header changeset.user
1118 <node id='#{node.id}' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
1119 <node id='#{node.id}' lon='1' lat='1' changeset='#{changeset.id}' version='1'/>
1125 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1126 assert_response :conflict,
1127 "shouldn't be able to upload the same element twice in a diff: #{@response.body}"
1131 # try to upload some elements without specifying the version
1132 def test_upload_missing_version
1133 changeset = create(:changeset)
1135 auth_header = bearer_authorization_header changeset.user
1140 <node id='1' lon='1' lat='1' changeset='#{changeset.id}'/>
1146 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1147 assert_response :bad_request,
1148 "shouldn't be able to upload an element without version: #{@response.body}"
1152 # try to upload with commands other than create, modify, or delete
1153 def test_action_upload_invalid
1154 changeset = create(:changeset)
1156 auth_header = bearer_authorization_header changeset.user
1161 <node id='1' lon='1' lat='1' changeset='#{changeset.id}' />
1165 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1166 assert_response :bad_request, "Shouldn't be able to upload a diff with the action ping"
1167 assert_equal("Unknown action ping, choices are create, modify, delete", @response.body)
1171 # upload a valid changeset which has a mixture of whitespace
1172 # to check a bug reported by ivansanchez (#1565).
1173 def test_upload_whitespace_valid
1174 changeset = create(:changeset)
1175 node = create(:node)
1176 way = create(:way_with_nodes, :nodes_count => 2)
1177 relation = create(:relation)
1178 other_relation = create(:relation)
1179 create(:relation_tag, :relation => relation)
1181 auth_header = bearer_authorization_header changeset.user
1185 <modify><node id='#{node.id}' lon='0' lat='0' changeset='#{changeset.id}'
1187 <node id='#{node.id}' lon='1' lat='1' changeset='#{changeset.id}' version='2'><tag k='k' v='v'/></node></modify>
1189 <relation id='#{relation.id}' changeset='#{changeset.id}' version='1'><member
1190 type='way' role='some' ref='#{way.id}'/><member
1191 type='node' role='some' ref='#{node.id}'/>
1192 <member type='relation' role='some' ref='#{other_relation.id}'/>
1194 </modify></osmChange>
1198 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1199 assert_response :success,
1200 "can't upload a valid diff with whitespace variations to changeset: #{@response.body}"
1202 # check the response is well-formed
1203 assert_select "diffResult>node", 2
1204 assert_select "diffResult>relation", 1
1206 # check that the changes made it into the database
1207 assert_equal 1, Node.find(node.id).tags.size, "node #{node.id} should now have one tag"
1208 assert_equal 0, Relation.find(relation.id).tags.size, "relation #{relation.id} should now have no tags"
1212 # test that a placeholder can be reused within the same upload.
1213 def test_upload_reuse_placeholder_valid
1214 changeset = create(:changeset)
1216 auth_header = bearer_authorization_header changeset.user
1221 <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
1222 <tag k="foo" v="bar"/>
1226 <node id='-1' lon='1' lat='1' changeset='#{changeset.id}' version='1'/>
1229 <node id='-1' lon='2' lat='2' changeset='#{changeset.id}' version='2'/>
1235 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1236 assert_response :success,
1237 "can't upload a valid diff with re-used placeholders to changeset: #{@response.body}"
1239 # check the response is well-formed
1240 assert_select "diffResult>node", 3
1241 assert_select "diffResult>node[old_id='-1']", 3
1245 # test what happens if a diff upload re-uses placeholder IDs in an
1247 def test_upload_placeholder_invalid
1248 changeset = create(:changeset)
1250 auth_header = bearer_authorization_header changeset.user
1255 <node id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
1256 <node id='-1' lon='1' lat='1' changeset='#{changeset.id}' version='1'/>
1257 <node id='-1' lon='2' lat='2' changeset='#{changeset.id}' version='2'/>
1263 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1264 assert_response :bad_request,
1265 "shouldn't be able to re-use placeholder IDs"
1267 # placeholder_ids must be unique across all action blocks
1271 <node id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
1274 <node id='-1' lon='1' lat='1' changeset='#{changeset.id}' version='1'/>
1280 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1281 assert_response :bad_request,
1282 "shouldn't be able to re-use placeholder IDs"
1285 def test_upload_process_order
1286 changeset = create(:changeset)
1288 auth_header = bearer_authorization_header changeset.user
1293 <node id="-1" lat="1" lon="2" changeset="#{changeset.id}"/>
1294 <way id="-1" changeset="#{changeset.id}">
1298 <node id="-2" lat="1" lon="2" changeset="#{changeset.id}"/>
1304 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1305 assert_response :bad_request,
1306 "shouldn't refer elements behind it"
1309 def test_upload_duplicate_delete
1310 changeset = create(:changeset)
1312 auth_header = bearer_authorization_header changeset.user
1317 <node id="-1" lat="39" lon="116" changeset="#{changeset.id}" />
1320 <node id="-1" version="1" changeset="#{changeset.id}" />
1321 <node id="-1" version="1" changeset="#{changeset.id}" />
1327 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1328 assert_response :gone,
1329 "transaction should be cancelled by second deletion"
1334 <node id="-1" lat="39" lon="116" changeset="#{changeset.id}" />
1336 <delete if-unused="true">
1337 <node id="-1" version="1" changeset="#{changeset.id}" />
1338 <node id="-1" version="1" changeset="#{changeset.id}" />
1344 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1346 assert_select "diffResult>node", 3
1347 assert_select "diffResult>node[old_id='-1']", 3
1348 assert_select "diffResult>node[new_version='1']", 1
1349 assert_select "diffResult>node[new_version='2']", 1
1353 # test that uploading a way referencing invalid placeholders gives a
1354 # proper error, not a 500.
1355 def test_upload_placeholder_invalid_way
1356 changeset = create(:changeset)
1359 auth_header = bearer_authorization_header changeset.user
1364 <node id="-1" lon="0.0" lat="0.0" changeset="#{changeset.id}" version="1"/>
1365 <node id="-2" lon="0.1" lat="0.1" changeset="#{changeset.id}" version="1"/>
1366 <node id="-3" lon="0.2" lat="0.2" changeset="#{changeset.id}" version="1"/>
1367 <way id="-1" changeset="#{changeset.id}" version="1">
1378 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1379 assert_response :bad_request,
1380 "shouldn't be able to use invalid placeholder IDs"
1381 assert_equal "Placeholder node not found for reference -4 in way -1", @response.body
1383 # the same again, but this time use an existing way
1387 <node id="-1" lon="0.0" lat="0.0" changeset="#{changeset.id}" version="1"/>
1388 <node id="-2" lon="0.1" lat="0.1" changeset="#{changeset.id}" version="1"/>
1389 <node id="-3" lon="0.2" lat="0.2" changeset="#{changeset.id}" version="1"/>
1390 <way id="#{way.id}" changeset="#{changeset.id}" version="1">
1401 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1402 assert_response :bad_request,
1403 "shouldn't be able to use invalid placeholder IDs"
1404 assert_equal "Placeholder node not found for reference -4 in way #{way.id}", @response.body
1408 # test that uploading a relation referencing invalid placeholders gives a
1409 # proper error, not a 500.
1410 def test_upload_placeholder_invalid_relation
1411 changeset = create(:changeset)
1412 relation = create(:relation)
1414 auth_header = bearer_authorization_header changeset.user
1419 <node id="-1" lon="0.0" lat="0.0" changeset="#{changeset.id}" version="1"/>
1420 <node id="-2" lon="0.1" lat="0.1" changeset="#{changeset.id}" version="1"/>
1421 <node id="-3" lon="0.2" lat="0.2" changeset="#{changeset.id}" version="1"/>
1422 <relation id="-1" changeset="#{changeset.id}" version="1">
1423 <member type="node" role="foo" ref="-1"/>
1424 <member type="node" role="foo" ref="-2"/>
1425 <member type="node" role="foo" ref="-3"/>
1426 <member type="node" role="foo" ref="-4"/>
1433 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1434 assert_response :bad_request,
1435 "shouldn't be able to use invalid placeholder IDs"
1436 assert_equal "Placeholder Node not found for reference -4 in relation -1.", @response.body
1438 # the same again, but this time use an existing relation
1442 <node id="-1" lon="0.0" lat="0.0" changeset="#{changeset.id}" version="1"/>
1443 <node id="-2" lon="0.1" lat="0.1" changeset="#{changeset.id}" version="1"/>
1444 <node id="-3" lon="0.2" lat="0.2" changeset="#{changeset.id}" version="1"/>
1445 <relation id="#{relation.id}" changeset="#{changeset.id}" version="1">
1446 <member type="node" role="foo" ref="-1"/>
1447 <member type="node" role="foo" ref="-2"/>
1448 <member type="node" role="foo" ref="-3"/>
1449 <member type="way" role="bar" ref="-1"/>
1456 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1457 assert_response :bad_request,
1458 "shouldn't be able to use invalid placeholder IDs"
1459 assert_equal "Placeholder Way not found for reference -1 in relation #{relation.id}.", @response.body
1463 # test what happens if a diff is uploaded containing only a node
1465 def test_upload_node_move
1466 auth_header = bearer_authorization_header
1468 xml = "<osm><changeset>" \
1469 "<tag k='created_by' v='osm test suite checking changesets'/>" \
1470 "</changeset></osm>"
1471 post api_changesets_path, :params => xml, :headers => auth_header
1472 assert_response :success
1473 changeset_id = @response.body.to_i
1475 old_node = create(:node, :lat => 1, :lon => 1)
1477 diff = XML::Document.new
1478 diff.root = XML::Node.new "osmChange"
1479 modify = XML::Node.new "modify"
1480 xml_old_node = xml_node_for_node(old_node)
1481 xml_old_node["lat"] = 2.0.to_s
1482 xml_old_node["lon"] = 2.0.to_s
1483 xml_old_node["changeset"] = changeset_id.to_s
1484 modify << xml_old_node
1488 post changeset_upload_path(changeset_id), :params => diff.to_s, :headers => auth_header
1489 assert_response :success,
1490 "diff should have uploaded OK"
1493 changeset = Changeset.find(changeset_id)
1494 assert_equal 1 * GeoRecord::SCALE, changeset.min_lon, "min_lon should be 1 degree"
1495 assert_equal 2 * GeoRecord::SCALE, changeset.max_lon, "max_lon should be 2 degrees"
1496 assert_equal 1 * GeoRecord::SCALE, changeset.min_lat, "min_lat should be 1 degree"
1497 assert_equal 2 * GeoRecord::SCALE, changeset.max_lat, "max_lat should be 2 degrees"
1501 # test what happens if a diff is uploaded adding a node to a way.
1502 def test_upload_way_extend
1503 auth_header = bearer_authorization_header
1505 xml = "<osm><changeset>" \
1506 "<tag k='created_by' v='osm test suite checking changesets'/>" \
1507 "</changeset></osm>"
1508 post api_changesets_path, :params => xml, :headers => auth_header
1509 assert_response :success
1510 changeset_id = @response.body.to_i
1512 old_way = create(:way)
1513 create(:way_node, :way => old_way, :node => create(:node, :lat => 0.1, :lon => 0.1))
1515 diff = XML::Document.new
1516 diff.root = XML::Node.new "osmChange"
1517 modify = XML::Node.new "modify"
1518 xml_old_way = xml_node_for_way(old_way)
1519 nd_ref = XML::Node.new "nd"
1520 nd_ref["ref"] = create(:node, :lat => 0.3, :lon => 0.3).id.to_s
1521 xml_old_way << nd_ref
1522 xml_old_way["changeset"] = changeset_id.to_s
1523 modify << xml_old_way
1527 post changeset_upload_path(changeset_id), :params => diff.to_s, :headers => auth_header
1528 assert_response :success,
1529 "diff should have uploaded OK"
1532 changeset = Changeset.find(changeset_id)
1533 assert_equal 0.1 * GeoRecord::SCALE, changeset.min_lon, "min_lon should be 0.1 degree"
1534 assert_equal 0.3 * GeoRecord::SCALE, changeset.max_lon, "max_lon should be 0.3 degrees"
1535 assert_equal 0.1 * GeoRecord::SCALE, changeset.min_lat, "min_lat should be 0.1 degree"
1536 assert_equal 0.3 * GeoRecord::SCALE, changeset.max_lat, "max_lat should be 0.3 degrees"
1540 # test for more issues in #1568
1541 def test_upload_empty_invalid
1542 changeset = create(:changeset)
1544 auth_header = bearer_authorization_header changeset.user
1547 "<osmChange></osmChange>",
1548 "<osmChange><modify/></osmChange>",
1549 "<osmChange><modify></modify></osmChange>"].each do |diff|
1551 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1552 assert_response(:success, "should be able to upload " \
1553 "empty changeset: " + diff)
1558 # test that the X-Error-Format header works to request XML errors
1559 def test_upload_xml_errors
1560 changeset = create(:changeset)
1561 node = create(:node)
1562 create(:relation_member, :member => node)
1564 auth_header = bearer_authorization_header changeset.user
1566 # try and delete a node that is in use
1567 diff = XML::Document.new
1568 diff.root = XML::Node.new "osmChange"
1569 delete = XML::Node.new "delete"
1571 delete << xml_node_for_node(node)
1574 error_header = error_format_header "xml"
1575 post changeset_upload_path(changeset), :params => diff.to_s, :headers => auth_header.merge(error_header)
1576 assert_response :success,
1577 "failed to return error in XML format"
1579 # check the returned payload
1580 assert_select "osmError[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
1581 assert_select "osmError>status", 1
1582 assert_select "osmError>message", 1
1585 def test_upload_not_found
1586 changeset = create(:changeset)
1588 auth_header = bearer_authorization_header changeset.user
1594 <node 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, "Node should not be found"
1607 <way 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, "Way should not be found"
1620 <relation 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, "Relation should not be found"
1633 <node 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, "Node should not be deleted"
1646 <way 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, "Way should not be deleted"
1659 <relation id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
1665 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1666 assert_response :not_found, "Relation should not be deleted"
1669 def test_upload_relation_placeholder_not_fix
1670 changeset = create(:changeset)
1672 auth_header = bearer_authorization_header changeset.user
1676 <osmChange version='0.6'>
1678 <relation id='-2' version='0' changeset='#{changeset.id}'>
1679 <member type='relation' role='' ref='-4' />
1680 <tag k='type' v='route' />
1681 <tag k='name' v='AtoB' />
1683 <relation id='-3' version='0' changeset='#{changeset.id}'>
1684 <tag k='type' v='route' />
1685 <tag k='name' v='BtoA' />
1687 <relation id='-4' version='0' changeset='#{changeset.id}'>
1688 <member type='relation' role='' ref='-2' />
1689 <member type='relation' role='' ref='-3' />
1690 <tag k='type' v='route_master' />
1691 <tag k='name' v='master' />
1698 post changeset_upload_path(changeset), :params => diff.to_s, :headers => auth_header
1699 assert_response :bad_request, "shouldn't be able to use reference -4 in relation -2: #{@response.body}"
1700 assert_equal "Placeholder Relation not found for reference -4 in relation -2.", @response.body
1703 def test_upload_multiple_delete_block
1704 changeset = create(:changeset)
1706 auth_header = bearer_authorization_header changeset.user
1708 node = create(:node)
1710 create(:way_node, :way => way, :node => node)
1711 alone_node = create(:node)
1715 <osmChange version='0.6'>
1716 <delete version="0.6">
1717 <node id="#{node.id}" version="#{node.version}" changeset="#{changeset.id}"/>
1719 <delete version="0.6" if-unused="true">
1720 <node id="#{alone_node.id}" version="#{alone_node.version}" changeset="#{changeset.id}"/>
1726 post changeset_upload_path(changeset), :params => diff.to_s, :headers => auth_header
1727 assert_response :precondition_failed,
1728 "shouldn't be able to upload a invalid deletion diff: #{@response.body}"
1729 assert_equal "Precondition failed: Node #{node.id} is still used by ways #{way.id}.", @response.body
1733 # test initial rate limit
1734 def test_upload_initial_rate_limit
1736 user = create(:user)
1738 # create some objects to use
1739 node = create(:node)
1740 way = create(:way_with_nodes, :nodes_count => 2)
1741 relation = create(:relation)
1743 # create a changeset that puts us near the initial rate limit
1744 changeset = create(:changeset, :user => user,
1745 :created_at => Time.now.utc - 5.minutes,
1746 :num_changes => Settings.initial_changes_per_hour - 2)
1748 # create authentication header
1749 auth_header = bearer_authorization_header user
1751 # simple diff to create a node way and relation using placeholders
1755 <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
1756 <tag k='foo' v='bar'/>
1757 <tag k='baz' v='bat'/>
1759 <way id='-1' changeset='#{changeset.id}'>
1760 <nd ref='#{node.id}'/>
1764 <relation id='-1' changeset='#{changeset.id}'>
1765 <member type='way' role='some' ref='#{way.id}'/>
1766 <member type='node' role='some' ref='#{node.id}'/>
1767 <member type='relation' role='some' ref='#{relation.id}'/>
1774 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1775 assert_response :too_many_requests, "upload did not hit rate limit"
1779 # test maximum rate limit
1780 def test_upload_maximum_rate_limit
1782 user = create(:user)
1784 # create some objects to use
1785 node = create(:node)
1786 way = create(:way_with_nodes, :nodes_count => 2)
1787 relation = create(:relation)
1789 # create a changeset to establish our initial edit time
1790 changeset = create(:changeset, :user => user,
1791 :created_at => Time.now.utc - 28.days)
1793 # create changeset to put us near the maximum rate limit
1794 total_changes = Settings.max_changes_per_hour - 2
1795 while total_changes.positive?
1796 changes = [total_changes, Changeset::MAX_ELEMENTS].min
1797 changeset = create(:changeset, :user => user,
1798 :created_at => Time.now.utc - 5.minutes,
1799 :num_changes => changes)
1800 total_changes -= changes
1803 # create authentication header
1804 auth_header = bearer_authorization_header user
1806 # simple diff to create a node way and relation using placeholders
1810 <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
1811 <tag k='foo' v='bar'/>
1812 <tag k='baz' v='bat'/>
1814 <way id='-1' changeset='#{changeset.id}'>
1815 <nd ref='#{node.id}'/>
1819 <relation id='-1' changeset='#{changeset.id}'>
1820 <member type='way' role='some' ref='#{way.id}'/>
1821 <member type='node' role='some' ref='#{node.id}'/>
1822 <member type='relation' role='some' ref='#{relation.id}'/>
1829 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1830 assert_response :too_many_requests, "upload did not hit rate limit"
1834 # test initial size limit
1835 def test_upload_initial_size_limit
1837 user = create(:user)
1839 # create a changeset that puts us near the initial size limit
1840 changeset = create(:changeset, :user => user,
1841 :min_lat => (-0.5 * GeoRecord::SCALE).round, :min_lon => (0.5 * GeoRecord::SCALE).round,
1842 :max_lat => (0.5 * GeoRecord::SCALE).round, :max_lon => (2.5 * GeoRecord::SCALE).round)
1844 # create authentication header
1845 auth_header = bearer_authorization_header user
1847 # simple diff to create a node
1851 <node id='-1' lon='0.9' lat='2.9' changeset='#{changeset.id}'>
1852 <tag k='foo' v='bar'/>
1853 <tag k='baz' v='bat'/>
1860 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1861 assert_response :payload_too_large, "upload did not hit size limit"
1865 # test size limit after one week
1866 def test_upload_week_size_limit
1868 user = create(:user)
1870 # create a changeset to establish our initial edit time
1871 create(:changeset, :user => user, :created_at => Time.now.utc - 7.days)
1873 # create a changeset that puts us near the initial size limit
1874 changeset = create(:changeset, :user => user,
1875 :min_lat => (-0.5 * GeoRecord::SCALE).round, :min_lon => (0.5 * GeoRecord::SCALE).round,
1876 :max_lat => (0.5 * GeoRecord::SCALE).round, :max_lon => (2.5 * GeoRecord::SCALE).round)
1878 # create authentication header
1879 auth_header = bearer_authorization_header user
1881 # simple diff to create a node way and relation using placeholders
1885 <node id='-1' lon='35' lat='35' changeset='#{changeset.id}'>
1886 <tag k='foo' v='bar'/>
1887 <tag k='baz' v='bat'/>
1894 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1895 assert_response :payload_too_large, "upload did not hit size limit"
1899 # when we make some simple changes we get the same changes back from the
1901 def test_diff_download_simple
1902 node = create(:node)
1904 ## First try with a non-public user, which should get a forbidden
1905 auth_header = bearer_authorization_header create(:user, :data_public => false)
1907 # create a temporary changeset
1908 xml = "<osm><changeset>" \
1909 "<tag k='created_by' v='osm test suite checking changesets'/>" \
1910 "</changeset></osm>"
1911 post api_changesets_path, :params => xml, :headers => auth_header
1912 assert_response :forbidden
1914 ## Now try with a normal user
1915 auth_header = bearer_authorization_header
1917 # create a temporary changeset
1918 xml = "<osm><changeset>" \
1919 "<tag k='created_by' v='osm test suite checking changesets'/>" \
1920 "</changeset></osm>"
1921 post api_changesets_path, :params => xml, :headers => auth_header
1922 assert_response :success
1923 changeset_id = @response.body.to_i
1929 <node id='#{node.id}' lon='0.0' lat='0.0' changeset='#{changeset_id}' version='1'/>
1930 <node id='#{node.id}' lon='0.1' lat='0.0' changeset='#{changeset_id}' version='2'/>
1931 <node id='#{node.id}' lon='0.1' lat='0.1' changeset='#{changeset_id}' version='3'/>
1932 <node id='#{node.id}' lon='0.1' lat='0.2' changeset='#{changeset_id}' version='4'/>
1933 <node id='#{node.id}' lon='0.2' lat='0.2' changeset='#{changeset_id}' version='5'/>
1934 <node id='#{node.id}' lon='0.3' lat='0.2' changeset='#{changeset_id}' version='6'/>
1935 <node id='#{node.id}' lon='0.3' lat='0.3' changeset='#{changeset_id}' version='7'/>
1936 <node id='#{node.id}' lon='0.9' lat='0.9' changeset='#{changeset_id}' version='8'/>
1942 post changeset_upload_path(changeset_id), :params => diff, :headers => auth_header
1943 assert_response :success,
1944 "can't upload multiple versions of an element in a diff: #{@response.body}"
1946 get changeset_download_path(changeset_id)
1947 assert_response :success
1949 assert_select "osmChange", 1
1950 assert_select "osmChange>modify", 8
1951 assert_select "osmChange>modify>node", 8
1955 # culled this from josm to ensure that nothing in the way that josm
1956 # is formatting the request is causing it to fail.
1958 # NOTE: the error turned out to be something else completely!
1959 def test_josm_upload
1960 auth_header = bearer_authorization_header
1962 # create a temporary changeset
1963 xml = "<osm><changeset>" \
1964 "<tag k='created_by' v='osm test suite checking changesets'/>" \
1965 "</changeset></osm>"
1966 post api_changesets_path, :params => xml, :headers => auth_header
1967 assert_response :success
1968 changeset_id = @response.body.to_i
1971 <osmChange version="0.6" generator="JOSM">
1972 <create version="0.6" generator="JOSM">
1973 <node id='-1' visible='true' changeset='#{changeset_id}' lat='51.49619982187321' lon='-0.18722061869438314' />
1974 <node id='-2' visible='true' changeset='#{changeset_id}' lat='51.496359883909605' lon='-0.18653093576241928' />
1975 <node id='-3' visible='true' changeset='#{changeset_id}' lat='51.49598132358285' lon='-0.18719613290981638' />
1976 <node id='-4' visible='true' changeset='#{changeset_id}' lat='51.4961591711078' lon='-0.18629015888084607' />
1977 <node id='-5' visible='true' changeset='#{changeset_id}' lat='51.49582126021711' lon='-0.18708186591517145' />
1978 <node id='-6' visible='true' changeset='#{changeset_id}' lat='51.49591018437858' lon='-0.1861432441734455' />
1979 <node id='-7' visible='true' changeset='#{changeset_id}' lat='51.49560784152179' lon='-0.18694719410005425' />
1980 <node id='-8' visible='true' changeset='#{changeset_id}' lat='51.49567389979617' lon='-0.1860289771788006' />
1981 <node id='-9' visible='true' changeset='#{changeset_id}' lat='51.49543761398892' lon='-0.186820684213126' />
1982 <way id='-10' action='modify' visible='true' changeset='#{changeset_id}'>
1992 <tag k='highway' v='residential' />
1993 <tag k='name' v='Foobar Street' />
2000 post changeset_upload_path(changeset_id), :params => diff, :headers => auth_header
2001 assert_response :success,
2002 "can't upload a diff from JOSM: #{@response.body}"
2004 get changeset_download_path(changeset_id)
2005 assert_response :success
2007 assert_select "osmChange", 1
2008 assert_select "osmChange>create>node", 9
2009 assert_select "osmChange>create>way", 1
2010 assert_select "osmChange>create>way>nd", 9
2011 assert_select "osmChange>create>way>tag", 2
2015 # when we make some complex changes we get the same changes back from the
2017 def test_diff_download_complex
2018 node = create(:node)
2019 node2 = create(:node)
2021 auth_header = bearer_authorization_header
2023 # create a temporary changeset
2024 xml = "<osm><changeset>" \
2025 "<tag k='created_by' v='osm test suite checking changesets'/>" \
2026 "</changeset></osm>"
2027 post api_changesets_path, :params => xml, :headers => auth_header
2028 assert_response :success
2029 changeset_id = @response.body.to_i
2035 <node id='#{node.id}' lon='0.0' lat='0.0' changeset='#{changeset_id}' version='1'/>
2038 <node id='-1' lon='0.9' lat='0.9' changeset='#{changeset_id}' version='0'/>
2039 <node id='-2' lon='0.8' lat='0.9' changeset='#{changeset_id}' version='0'/>
2040 <node id='-3' lon='0.7' lat='0.9' changeset='#{changeset_id}' version='0'/>
2043 <node id='#{node2.id}' lon='2.0' lat='1.5' changeset='#{changeset_id}' version='1'/>
2044 <way id='#{way.id}' changeset='#{changeset_id}' version='1'>
2045 <nd ref='#{node2.id}'/>
2055 post changeset_upload_path(changeset_id), :params => diff, :headers => auth_header
2056 assert_response :success,
2057 "can't upload multiple versions of an element in a diff: #{@response.body}"
2059 get changeset_download_path(changeset_id)
2060 assert_response :success
2062 assert_select "osmChange", 1
2063 assert_select "osmChange>create", 3
2064 assert_select "osmChange>delete", 1
2065 assert_select "osmChange>modify", 2
2066 assert_select "osmChange>create>node", 3
2067 assert_select "osmChange>delete>node", 1
2068 assert_select "osmChange>modify>node", 1
2069 assert_select "osmChange>modify>way", 1
2072 def test_changeset_download
2073 changeset = create(:changeset)
2074 node = create(:node, :with_history, :version => 1, :changeset => changeset)
2075 tag = create(:old_node_tag, :old_node => node.old_nodes.find_by(:version => 1))
2076 node2 = create(:node, :with_history, :version => 1, :changeset => changeset)
2077 _node3 = create(:node, :with_history, :deleted, :version => 1, :changeset => changeset)
2078 _relation = create(:relation, :with_history, :version => 1, :changeset => changeset)
2079 _relation2 = create(:relation, :with_history, :deleted, :version => 1, :changeset => changeset)
2081 get changeset_download_path(changeset)
2083 assert_response :success
2085 # FIXME: needs more assert_select tests
2086 assert_select "osmChange[version='#{Settings.api_version}'][generator='#{Settings.generator}']" do
2087 assert_select "create", :count => 5
2088 assert_select "create>node[id='#{node.id}'][visible='#{node.visible?}'][version='#{node.version}']" do
2089 assert_select "tag[k='#{tag.k}'][v='#{tag.v}']"
2091 assert_select "create>node[id='#{node2.id}']"
2095 test "sorts downloaded elements by timestamp" do
2096 changeset = create(:changeset)
2097 node1 = create(:old_node, :version => 2, :timestamp => "2020-02-01", :changeset => changeset)
2098 node0 = create(:old_node, :version => 2, :timestamp => "2020-01-01", :changeset => changeset)
2100 get changeset_download_path(changeset)
2101 assert_response :success
2102 assert_dom "modify", :count => 2 do |modify|
2103 assert_dom modify[0], ">node", :count => 1 do |node|
2104 assert_dom node, ">@id", node0.node_id.to_s
2106 assert_dom modify[1], ">node", :count => 1 do |node|
2107 assert_dom node, ">@id", node1.node_id.to_s
2112 test "sorts downloaded elements by version" do
2113 changeset = create(:changeset)
2114 node1 = create(:old_node, :version => 3, :timestamp => "2020-01-01", :changeset => changeset)
2115 node0 = create(:old_node, :version => 2, :timestamp => "2020-01-01", :changeset => changeset)
2117 get changeset_download_path(changeset)
2118 assert_response :success
2119 assert_dom "modify", :count => 2 do |modify|
2120 assert_dom modify[0], ">node", :count => 1 do |node|
2121 assert_dom node, ">@id", node0.node_id.to_s
2123 assert_dom modify[1], ">node", :count => 1 do |node|
2124 assert_dom node, ">@id", node1.node_id.to_s
2130 # check that the bounding box of a changeset gets updated correctly
2131 # FIXME: This should really be moded to a integration test due to the with_controller
2132 def test_changeset_bbox
2134 create(:way_node, :way => way, :node => create(:node, :lat => 0.3, :lon => 0.3))
2136 auth_header = bearer_authorization_header
2138 # create a new changeset
2139 xml = "<osm><changeset/></osm>"
2140 post api_changesets_path, :params => xml, :headers => auth_header
2141 assert_response :success, "Creating of changeset failed."
2142 changeset_id = @response.body.to_i
2144 # add a single node to it
2145 with_controller(NodesController.new) do
2146 xml = "<osm><node lon='0.1' lat='0.2' changeset='#{changeset_id}'/></osm>"
2147 post api_nodes_path, :params => xml, :headers => auth_header
2148 assert_response :success, "Couldn't create node."
2151 # get the bounding box back from the changeset
2152 get changeset_show_path(changeset_id)
2153 assert_response :success, "Couldn't read back changeset."
2154 assert_select "osm>changeset[min_lon='0.1000000']", 1
2155 assert_select "osm>changeset[max_lon='0.1000000']", 1
2156 assert_select "osm>changeset[min_lat='0.2000000']", 1
2157 assert_select "osm>changeset[max_lat='0.2000000']", 1
2159 # add another node to it
2160 with_controller(NodesController.new) do
2161 xml = "<osm><node lon='0.2' lat='0.1' changeset='#{changeset_id}'/></osm>"
2162 post api_nodes_path, :params => xml, :headers => auth_header
2163 assert_response :success, "Couldn't create second node."
2166 # get the bounding box back from the changeset
2167 get changeset_show_path(changeset_id)
2168 assert_response :success, "Couldn't read back changeset for the second time."
2169 assert_select "osm>changeset[min_lon='0.1000000']", 1
2170 assert_select "osm>changeset[max_lon='0.2000000']", 1
2171 assert_select "osm>changeset[min_lat='0.1000000']", 1
2172 assert_select "osm>changeset[max_lat='0.2000000']", 1
2174 # add (delete) a way to it, which contains a point at (3,3)
2175 with_controller(WaysController.new) do
2176 xml = update_changeset(xml_for_way(way), changeset_id)
2177 delete api_way_path(way), :params => xml.to_s, :headers => auth_header
2178 assert_response :success, "Couldn't delete a way."
2181 # get the bounding box back from the changeset
2182 get changeset_show_path(changeset_id)
2183 assert_response :success, "Couldn't read back changeset for the third time."
2184 assert_select "osm>changeset[min_lon='0.1000000']", 1
2185 assert_select "osm>changeset[max_lon='0.3000000']", 1
2186 assert_select "osm>changeset[min_lat='0.1000000']", 1
2187 assert_select "osm>changeset[max_lat='0.3000000']", 1
2191 # test the query functionality of changesets
2193 private_user = create(:user, :data_public => false)
2194 private_user_changeset = create(:changeset, :user => private_user)
2195 private_user_closed_changeset = create(:changeset, :closed, :user => private_user)
2196 user = create(:user)
2197 changeset = create(:changeset, :user => user)
2198 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))
2199 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)
2200 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)
2202 get changesets_path(:bbox => "-10,-10, 10, 10")
2203 assert_response :success, "can't get changesets in bbox"
2204 assert_changesets_in_order [changeset3, changeset2]
2206 get changesets_path(:bbox => "4.5,4.5,4.6,4.6")
2207 assert_response :success, "can't get changesets in bbox"
2208 assert_changesets_in_order [changeset3]
2210 # not found when looking for changesets of non-existing users
2211 get changesets_path(:user => User.maximum(:id) + 1)
2212 assert_response :not_found
2213 assert_equal "text/plain", @response.media_type
2214 get changesets_path(:display_name => " ")
2215 assert_response :not_found
2216 assert_equal "text/plain", @response.media_type
2218 # can't get changesets of user 1 without authenticating
2219 get changesets_path(:user => private_user.id)
2220 assert_response :not_found, "shouldn't be able to get changesets by non-public user (ID)"
2221 get changesets_path(:display_name => private_user.display_name)
2222 assert_response :not_found, "shouldn't be able to get changesets by non-public user (name)"
2224 # but this should work
2225 auth_header = bearer_authorization_header private_user
2226 get changesets_path(:user => private_user.id), :headers => auth_header
2227 assert_response :success, "can't get changesets by user ID"
2228 assert_changesets_in_order [private_user_changeset, private_user_closed_changeset]
2230 get changesets_path(:display_name => private_user.display_name), :headers => auth_header
2231 assert_response :success, "can't get changesets by user name"
2232 assert_changesets_in_order [private_user_changeset, private_user_closed_changeset]
2234 # test json endpoint
2235 get changesets_path(:display_name => private_user.display_name), :headers => auth_header, :params => { :format => "json" }
2236 assert_response :success, "can't get changesets by user name"
2238 js = ActiveSupport::JSON.decode(@response.body)
2241 assert_equal Settings.api_version, js["version"]
2242 assert_equal Settings.generator, js["generator"]
2243 assert_equal 2, js["changesets"].count
2245 # check that the correct error is given when we provide both UID and name
2246 get changesets_path(:user => private_user.id,
2247 :display_name => private_user.display_name), :headers => auth_header
2248 assert_response :bad_request, "should be a bad request to have both ID and name specified"
2250 get changesets_path(:user => private_user.id, :open => true), :headers => auth_header
2251 assert_response :success, "can't get changesets by user and open"
2252 assert_changesets_in_order [private_user_changeset]
2254 get changesets_path(:time => "2007-12-31"), :headers => auth_header
2255 assert_response :success, "can't get changesets by time-since"
2256 assert_changesets_in_order [changeset3, changeset2, changeset, private_user_changeset, private_user_closed_changeset, closed_changeset]
2258 get changesets_path(:time => "2008-01-01T12:34Z"), :headers => auth_header
2259 assert_response :success, "can't get changesets by time-since with hour"
2260 assert_changesets_in_order [changeset3, changeset2, changeset, private_user_changeset, private_user_closed_changeset, closed_changeset]
2262 get changesets_path(:time => "2007-12-31T23:59Z,2008-01-02T00:01Z"), :headers => auth_header
2263 assert_response :success, "can't get changesets by time-range"
2264 assert_changesets_in_order [closed_changeset]
2266 get changesets_path(:open => "true"), :headers => auth_header
2267 assert_response :success, "can't get changesets by open-ness"
2268 assert_changesets_in_order [changeset3, changeset2, changeset, private_user_changeset]
2270 get changesets_path(:closed => "true"), :headers => auth_header
2271 assert_response :success, "can't get changesets by closed-ness"
2272 assert_changesets_in_order [private_user_closed_changeset, closed_changeset]
2274 get changesets_path(:closed => "true", :user => private_user.id), :headers => auth_header
2275 assert_response :success, "can't get changesets by closed-ness and user"
2276 assert_changesets_in_order [private_user_closed_changeset]
2278 get changesets_path(:closed => "true", :user => user.id), :headers => auth_header
2279 assert_response :success, "can't get changesets by closed-ness and user"
2280 assert_changesets_in_order [closed_changeset]
2282 get changesets_path(:changesets => "#{private_user_changeset.id},#{changeset.id},#{closed_changeset.id}"), :headers => auth_header
2283 assert_response :success, "can't get changesets by id (as comma-separated string)"
2284 assert_changesets_in_order [changeset, private_user_changeset, closed_changeset]
2286 get changesets_path(:changesets => ""), :headers => auth_header
2287 assert_response :bad_request, "should be a bad request since changesets is empty"
2291 # test the query functionality of changesets with the limit parameter
2292 def test_query_limit
2293 user = create(:user)
2294 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))
2295 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))
2296 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))
2297 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))
2298 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))
2301 assert_response :success
2302 assert_changesets_in_order [changeset5, changeset4, changeset3, changeset2, changeset1]
2304 get changesets_path(:limit => "3")
2305 assert_response :success
2306 assert_changesets_in_order [changeset5, changeset4, changeset3]
2308 get changesets_path(:limit => "0")
2309 assert_response :bad_request
2311 get changesets_path(:limit => Settings.max_changeset_query_limit)
2312 assert_response :success
2313 assert_changesets_in_order [changeset5, changeset4, changeset3, changeset2, changeset1]
2315 get changesets_path(:limit => Settings.max_changeset_query_limit + 1)
2316 assert_response :bad_request
2320 # test the query functionality of sequential changesets with order and time parameters
2321 def test_query_order
2322 user = create(:user)
2323 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))
2324 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))
2325 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))
2326 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))
2327 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))
2328 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))
2331 assert_response :success
2332 assert_changesets_in_order [changeset6, changeset5, changeset4, changeset3, changeset2, changeset1]
2334 get changesets_path(:order => "oldest")
2335 assert_response :success
2336 assert_changesets_in_order [changeset1, changeset2, changeset3, changeset4, changeset5, changeset6]
2339 # lower time bound at the opening time of a changeset
2340 ["2008-02-01T00:00:00Z", "2008-05-15T00:00:00Z", [changeset5, changeset4, changeset3, changeset2], [changeset5, changeset4, changeset3, changeset2]],
2341 # lower time bound in the middle of a changeset
2342 ["2008-02-01T12:00:00Z", "2008-05-15T00:00:00Z", [changeset5, changeset4, changeset3, changeset2], [changeset5, changeset4, changeset3]],
2343 # lower time bound at the closing time of a changeset
2344 ["2008-02-02T00:00:00Z", "2008-05-15T00:00:00Z", [changeset5, changeset4, changeset3, changeset2], [changeset5, changeset4, changeset3]],
2345 # lower time bound after the closing time of a changeset
2346 ["2008-02-02T00:00:01Z", "2008-05-15T00:00:00Z", [changeset5, changeset4, changeset3], [changeset5, changeset4, changeset3]],
2347 # upper time bound in the middle of a changeset
2348 ["2007-09-09T12:00:00Z", "2008-04-01T12:00:00Z", [changeset4, changeset3, changeset2, changeset1], [changeset4, changeset3, changeset2, changeset1]],
2350 ["2009-02-02T00:00:01Z", "2018-05-15T00:00:00Z", [], []]
2351 ].each do |from, to, interval_changesets, point_changesets|
2352 get changesets_path(:time => "#{from},#{to}")
2353 assert_response :success
2354 assert_changesets_in_order interval_changesets
2356 get changesets_path(:from => from, :to => to)
2357 assert_response :success
2358 assert_changesets_in_order point_changesets
2360 get changesets_path(:from => from, :to => to, :order => "oldest")
2361 assert_response :success
2362 assert_changesets_in_order point_changesets.reverse
2367 # test the query functionality of overlapping changesets with order and time parameters
2368 def test_query_order_overlapping
2369 user = create(:user)
2370 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))
2371 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))
2372 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))
2373 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))
2374 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))
2376 get changesets_path(:time => "2015-06-04T00:00:00Z")
2377 assert_response :success
2378 assert_changesets_in_order [changeset1, changeset2, changeset3, changeset4]
2380 get changesets_path(:from => "2015-06-04T00:00:00Z")
2381 assert_response :success
2382 assert_changesets_in_order [changeset1, changeset2, changeset3]
2384 get changesets_path(:from => "2015-06-04T00:00:00Z", :order => "oldest")
2385 assert_response :success
2386 assert_changesets_in_order [changeset3, changeset2, changeset1]
2388 get changesets_path(:time => "2015-06-04T16:00:00Z,2015-06-04T17:30:00Z")
2389 assert_response :success
2390 assert_changesets_in_order [changeset1, changeset2, changeset3, changeset4]
2392 get changesets_path(:from => "2015-06-04T16:00:00Z", :to => "2015-06-04T17:30:00Z")
2393 assert_response :success
2394 assert_changesets_in_order [changeset1, changeset2]
2396 get changesets_path(:from => "2015-06-04T16:00:00Z", :to => "2015-06-04T17:30:00Z", :order => "oldest")
2397 assert_response :success
2398 assert_changesets_in_order [changeset2, changeset1]
2402 # check that errors are returned if garbage is inserted
2403 # into query strings
2404 def test_query_invalid
2407 ";drop table users;"].each do |bbox|
2408 get changesets_path(:bbox => bbox)
2409 assert_response :bad_request, "'#{bbox}' isn't a bbox"
2414 ";drop table users;",
2416 "-,-"].each do |time|
2417 get changesets_path(:time => time)
2418 assert_response :bad_request, "'#{time}' isn't a valid time range"
2425 get changesets_path(:user => uid)
2426 assert_response :bad_request, "'#{uid}' isn't a valid user ID"
2429 get changesets_path(:order => "oldest", :time => "2008-01-01T00:00Z,2018-01-01T00:00Z")
2430 assert_response :bad_request, "cannot use order=oldest with time"
2434 # check updating tags on a changeset
2435 def test_changeset_update
2436 private_user = create(:user, :data_public => false)
2437 private_changeset = create(:changeset, :user => private_user)
2438 user = create(:user)
2439 changeset = create(:changeset, :user => user)
2441 ## First try with a non-public user
2442 new_changeset = create_changeset_xml(:user => private_user)
2443 new_tag = XML::Node.new "tag"
2444 new_tag["k"] = "tagtesting"
2445 new_tag["v"] = "valuetesting"
2446 new_changeset.find("//osm/changeset").first << new_tag
2448 # try without any authorization
2449 put changeset_show_path(private_changeset), :params => new_changeset.to_s
2450 assert_response :unauthorized
2452 # try with the wrong authorization
2453 auth_header = bearer_authorization_header
2454 put changeset_show_path(private_changeset), :params => new_changeset.to_s, :headers => auth_header
2455 assert_response :conflict
2457 # now this should get an unauthorized
2458 auth_header = bearer_authorization_header private_user
2459 put changeset_show_path(private_changeset), :params => new_changeset.to_s, :headers => auth_header
2460 assert_require_public_data "user with their data non-public, shouldn't be able to edit their changeset"
2462 ## Now try with the public user
2463 new_changeset = create_changeset_xml(:id => 1)
2464 new_tag = XML::Node.new "tag"
2465 new_tag["k"] = "tagtesting"
2466 new_tag["v"] = "valuetesting"
2467 new_changeset.find("//osm/changeset").first << new_tag
2469 # try without any authorization
2470 put changeset_show_path(changeset), :params => new_changeset.to_s
2471 assert_response :unauthorized
2473 # try with the wrong authorization
2474 auth_header = bearer_authorization_header
2475 put changeset_show_path(changeset), :params => new_changeset.to_s, :headers => auth_header
2476 assert_response :conflict
2478 # now this should work...
2479 auth_header = bearer_authorization_header user
2480 put changeset_show_path(changeset), :params => new_changeset.to_s, :headers => auth_header
2481 assert_response :success
2483 assert_select "osm>changeset[id='#{changeset.id}']", 1
2484 assert_select "osm>changeset>tag", 1
2485 assert_select "osm>changeset>tag[k='tagtesting'][v='valuetesting']", 1
2489 # check that a user different from the one who opened the changeset
2491 def test_changeset_update_invalid
2492 auth_header = bearer_authorization_header
2494 changeset = create(:changeset)
2495 new_changeset = create_changeset_xml(:user => changeset.user, :id => changeset.id)
2496 new_tag = XML::Node.new "tag"
2497 new_tag["k"] = "testing"
2498 new_tag["v"] = "testing"
2499 new_changeset.find("//osm/changeset").first << new_tag
2501 put changeset_show_path(changeset), :params => new_changeset.to_s, :headers => auth_header
2502 assert_response :conflict
2506 # check that a changeset can contain a certain max number of changes.
2507 ## FIXME should be changed to an integration test due to the with_controller
2508 def test_changeset_limits
2509 user = create(:user)
2510 auth_header = bearer_authorization_header user
2512 # create an old changeset to ensure we have the maximum rate limit
2513 create(:changeset, :user => user, :created_at => Time.now.utc - 28.days)
2515 # open a new changeset
2516 xml = "<osm><changeset/></osm>"
2517 post api_changesets_path, :params => xml, :headers => auth_header
2518 assert_response :success, "can't create a new changeset"
2519 cs_id = @response.body.to_i
2521 # start the counter just short of where the changeset should finish.
2523 # alter the database to set the counter on the changeset directly,
2524 # otherwise it takes about 6 minutes to fill all of them.
2525 changeset = Changeset.find(cs_id)
2526 changeset.num_changes = Changeset::MAX_ELEMENTS - offset
2529 with_controller(NodesController.new) do
2531 xml = "<osm><node changeset='#{cs_id}' lat='0.0' lon='0.0'/></osm>"
2532 post api_nodes_path, :params => xml, :headers => auth_header
2533 assert_response :success, "can't create a new node"
2534 node_id = @response.body.to_i
2536 get api_node_path(node_id)
2537 assert_response :success, "can't read back new node"
2538 node_doc = XML::Parser.string(@response.body).parse
2539 node_xml = node_doc.find("//osm/node").first
2541 # loop until we fill the changeset with nodes
2543 node_xml["lat"] = rand.to_s
2544 node_xml["lon"] = rand.to_s
2545 node_xml["version"] = (i + 1).to_s
2547 put api_node_path(node_id), :params => node_doc.to_s, :headers => auth_header
2548 assert_response :success, "attempt #{i} should have succeeded"
2551 # trying again should fail
2552 node_xml["lat"] = rand.to_s
2553 node_xml["lon"] = rand.to_s
2554 node_xml["version"] = offset.to_s
2556 put api_node_path(node_id), :params => node_doc.to_s, :headers => auth_header
2557 assert_response :conflict, "final attempt should have failed"
2560 changeset = Changeset.find(cs_id)
2561 assert_equal Changeset::MAX_ELEMENTS + 1, changeset.num_changes
2563 # check that the changeset is now closed as well
2564 assert_not(changeset.open?,
2565 "changeset should have been auto-closed by exceeding " \
2570 # check that the changeset download for a changeset with a redacted
2571 # element in it doesn't contain that element.
2572 def test_diff_download_redacted
2573 changeset = create(:changeset)
2574 node = create(:node, :with_history, :version => 2, :changeset => changeset)
2575 node_v1 = node.old_nodes.find_by(:version => 1)
2576 node_v1.redact!(create(:redaction))
2578 get changeset_download_path(changeset)
2579 assert_response :success
2581 assert_select "osmChange", 1
2582 # this changeset contains the node in versions 1 & 2, but 1 should
2584 assert_select "osmChange node[id='#{node.id}']", 1
2585 assert_select "osmChange node[id='#{node.id}'][version='1']", 0
2589 # test subscribe success
2590 def test_subscribe_success
2591 auth_header = bearer_authorization_header
2592 changeset = create(:changeset, :closed)
2594 assert_difference "changeset.subscribers.count", 1 do
2595 post api_changeset_subscribe_path(changeset), :headers => auth_header
2597 assert_response :success
2599 # not closed changeset
2600 changeset = create(:changeset)
2601 assert_difference "changeset.subscribers.count", 1 do
2602 post api_changeset_subscribe_path(changeset), :headers => auth_header
2604 assert_response :success
2608 # test subscribe fail
2609 def test_subscribe_fail
2610 user = create(:user)
2613 changeset = create(:changeset, :closed)
2614 assert_no_difference "changeset.subscribers.count" do
2615 post api_changeset_subscribe_path(changeset)
2617 assert_response :unauthorized
2619 auth_header = bearer_authorization_header user
2622 assert_no_difference "changeset.subscribers.count" do
2623 post api_changeset_subscribe_path(999111), :headers => auth_header
2625 assert_response :not_found
2627 # trying to subscribe when already subscribed
2628 changeset = create(:changeset, :closed)
2629 changeset.subscribers.push(user)
2630 assert_no_difference "changeset.subscribers.count" do
2631 post api_changeset_subscribe_path(changeset), :headers => auth_header
2633 assert_response :conflict
2637 # test unsubscribe success
2638 def test_unsubscribe_success
2639 user = create(:user)
2640 auth_header = bearer_authorization_header user
2641 changeset = create(:changeset, :closed)
2642 changeset.subscribers.push(user)
2644 assert_difference "changeset.subscribers.count", -1 do
2645 post api_changeset_unsubscribe_path(changeset), :headers => auth_header
2647 assert_response :success
2649 # not closed changeset
2650 changeset = create(:changeset)
2651 changeset.subscribers.push(user)
2653 assert_difference "changeset.subscribers.count", -1 do
2654 post api_changeset_unsubscribe_path(changeset), :headers => auth_header
2656 assert_response :success
2660 # test unsubscribe fail
2661 def test_unsubscribe_fail
2663 changeset = create(:changeset, :closed)
2664 assert_no_difference "changeset.subscribers.count" do
2665 post api_changeset_unsubscribe_path(changeset)
2667 assert_response :unauthorized
2669 auth_header = bearer_authorization_header
2672 assert_no_difference "changeset.subscribers.count" do
2673 post api_changeset_unsubscribe_path(999111), :headers => auth_header
2675 assert_response :not_found
2677 # trying to unsubscribe when not subscribed
2678 changeset = create(:changeset, :closed)
2679 assert_no_difference "changeset.subscribers.count" do
2680 post api_changeset_unsubscribe_path(changeset), :headers => auth_header
2682 assert_response :not_found
2688 # check that the output consists of one specific changeset
2689 def assert_single_changeset(changeset, &)
2690 assert_dom "> changeset", 1 do
2691 assert_dom "> @id", changeset.id.to_s
2692 assert_dom "> @created_at", changeset.created_at.xmlschema
2694 assert_dom "> @open", "true"
2695 assert_dom "> @closed_at", 0
2697 assert_dom "> @open", "false"
2698 assert_dom "> @closed_at", changeset.closed_at.xmlschema
2700 assert_dom "> @comments_count", changeset.comments.length.to_s
2701 assert_dom "> @changes_count", changeset.num_changes.to_s
2702 yield if block_given?
2706 def assert_single_changeset_json(changeset, js)
2707 assert_equal changeset.id, js["changeset"]["id"]
2708 assert_equal changeset.created_at.xmlschema, js["changeset"]["created_at"]
2710 assert js["changeset"]["open"]
2711 assert_nil js["changeset"]["closed_at"]
2713 assert_not js["changeset"]["open"]
2714 assert_equal changeset.closed_at.xmlschema, js["changeset"]["closed_at"]
2716 assert_equal changeset.comments.length, js["changeset"]["comments_count"]
2717 assert_equal changeset.num_changes, js["changeset"]["changes_count"]
2721 # check that certain changesets exist in the output in the specified order
2722 def assert_changesets_in_order(changesets)
2723 assert_select "osm>changeset", changesets.size
2724 changesets.each_with_index do |changeset, index|
2725 assert_select "osm>changeset:nth-child(#{index + 1})[id='#{changeset.id}']", 1
2730 # update the changeset_id of a way element
2731 def update_changeset(xml, changeset_id)
2732 xml_attr_rewrite(xml, "changeset", changeset_id)
2736 # update an attribute in a way element
2737 def xml_attr_rewrite(xml, name, value)
2738 xml.find("//osm/way").first[name] = value.to_s
2743 # build XML for changesets
2744 def create_changeset_xml(user: nil, id: nil)
2745 root = XML::Document.new
2746 root.root = XML::Node.new "osm"
2747 cs = XML::Node.new "changeset"
2749 cs["user"] = user.display_name
2750 cs["uid"] = user.id.to_s
2752 cs["id"] = id.to_s if id