4 class ChangesetsControllerTest < ActionDispatch::IntegrationTest
6 # test all routes which lead to this controller
9 { :path => "/api/0.6/changeset/create", :method => :put },
10 { :controller => "api/changesets", :action => "create" }
13 { :path => "/api/0.6/changeset/1/upload", :method => :post },
14 { :controller => "api/changesets", :action => "upload", :id => "1" }
17 { :path => "/api/0.6/changeset/1/download", :method => :get },
18 { :controller => "api/changesets", :action => "download", :id => "1" }
21 { :path => "/api/0.6/changeset/1", :method => :get },
22 { :controller => "api/changesets", :action => "show", :id => "1" }
25 { :path => "/api/0.6/changeset/1.json", :method => :get },
26 { :controller => "api/changesets", :action => "show", :id => "1", :format => "json" }
29 { :path => "/api/0.6/changeset/1/subscribe", :method => :post },
30 { :controller => "api/changesets", :action => "subscribe", :id => "1" }
33 { :path => "/api/0.6/changeset/1/subscribe.json", :method => :post },
34 { :controller => "api/changesets", :action => "subscribe", :id => "1", :format => "json" }
37 { :path => "/api/0.6/changeset/1/unsubscribe", :method => :post },
38 { :controller => "api/changesets", :action => "unsubscribe", :id => "1" }
41 { :path => "/api/0.6/changeset/1/unsubscribe.json", :method => :post },
42 { :controller => "api/changesets", :action => "unsubscribe", :id => "1", :format => "json" }
45 { :path => "/api/0.6/changeset/1", :method => :put },
46 { :controller => "api/changesets", :action => "update", :id => "1" }
49 { :path => "/api/0.6/changeset/1/close", :method => :put },
50 { :controller => "api/changesets", :action => "close", :id => "1" }
53 { :path => "/api/0.6/changesets", :method => :get },
54 { :controller => "api/changesets", :action => "index" }
57 { :path => "/api/0.6/changesets.json", :method => :get },
58 { :controller => "api/changesets", :action => "index", :format => "json" }
62 # -----------------------
63 # Test simple changeset creation
64 # -----------------------
67 auth_header = bearer_authorization_header create(:user, :data_public => false)
68 # Create the first user's changeset
69 xml = "<osm><changeset>" \
70 "<tag k='created_by' v='osm test suite checking changesets'/>" \
72 put changeset_create_path, :params => xml, :headers => auth_header
73 assert_require_public_data
75 auth_header = bearer_authorization_header
76 # Create the first user's changeset
77 xml = "<osm><changeset>" \
78 "<tag k='created_by' v='osm test suite checking changesets'/>" \
80 put changeset_create_path, :params => xml, :headers => auth_header
82 assert_response :success, "Creation of changeset did not return success status"
83 newid = @response.body.to_i
85 # check end time, should be an hour ahead of creation time
86 cs = Changeset.find(newid)
87 duration = cs.closed_at - cs.created_at
88 # the difference can either be a rational, or a floating point number
89 # of seconds, depending on the code path taken :-(
90 if duration.instance_of?(Rational)
91 assert_equal Rational(1, 24), duration, "initial idle timeout should be an hour (#{cs.created_at} -> #{cs.closed_at})"
93 # must be number of seconds...
94 assert_equal 3600, duration.round, "initial idle timeout should be an hour (#{cs.created_at} -> #{cs.closed_at})"
97 # checks if uploader was subscribed
98 assert_equal 1, cs.subscribers.length
101 def test_create_invalid
102 auth_header = bearer_authorization_header create(:user, :data_public => false)
103 xml = "<osm><changeset></osm>"
104 put changeset_create_path, :params => xml, :headers => auth_header
105 assert_require_public_data
107 ## Try the public user
108 auth_header = bearer_authorization_header
109 xml = "<osm><changeset></osm>"
110 put changeset_create_path, :params => xml, :headers => auth_header
111 assert_response :bad_request, "creating a invalid changeset should fail"
114 def test_create_invalid_no_content
115 ## First check with no auth
116 put changeset_create_path
117 assert_response :unauthorized, "shouldn't be able to create a changeset with no auth"
119 ## Now try to with a non-public user
120 auth_header = bearer_authorization_header create(:user, :data_public => false)
121 put changeset_create_path, :headers => auth_header
122 assert_require_public_data
124 ## Try an inactive user
125 auth_header = bearer_authorization_header create(:user, :pending)
126 put changeset_create_path, :headers => auth_header
129 ## Now try to use a normal user
130 auth_header = bearer_authorization_header
131 put changeset_create_path, :headers => auth_header
132 assert_response :bad_request, "creating a changeset with no content should fail"
135 def test_create_wrong_method
136 auth_header = bearer_authorization_header
138 get changeset_create_path, :headers => auth_header
139 assert_response :not_found
140 assert_template "rescues/routing_error"
142 post changeset_create_path, :headers => auth_header
143 assert_response :not_found
144 assert_template "rescues/routing_error"
148 # check that the changeset can be shown and returns the correct
149 # document structure.
151 changeset = create(:changeset)
153 get changeset_show_path(changeset)
154 assert_response :success, "cannot get first changeset"
156 assert_dom "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
157 assert_single_changeset changeset do
158 assert_dom "> discussion", 0
161 get changeset_show_path(changeset), :params => { :include_discussion => true }
162 assert_response :success, "cannot get first changeset with comments"
164 assert_dom "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
165 assert_single_changeset changeset do
166 assert_dom "> discussion", 1
167 assert_dom "> discussion > comment", 0
171 def test_show_comments
172 # all comments visible
173 changeset = create(:changeset, :closed)
174 comment1, comment2, comment3 = create_list(:changeset_comment, 3, :changeset_id => changeset.id)
176 get changeset_show_path(changeset), :params => { :include_discussion => true }
177 assert_response :success, "cannot get closed changeset with comments"
179 assert_dom "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1 do
180 assert_single_changeset changeset do
181 assert_dom "> discussion", 1 do
182 assert_dom "> comment", 3 do |dom_comments|
183 assert_dom dom_comments[0], "> @id", comment1.id.to_s
184 assert_dom dom_comments[0], "> @visible", "true"
185 assert_dom dom_comments[1], "> @id", comment2.id.to_s
186 assert_dom dom_comments[1], "> @visible", "true"
187 assert_dom dom_comments[2], "> @id", comment3.id.to_s
188 assert_dom dom_comments[2], "> @visible", "true"
194 # one hidden comment not included because not asked for
195 comment2.update(:visible => false)
198 get changeset_show_path(changeset), :params => { :include_discussion => true }
199 assert_response :success, "cannot get closed changeset with comments"
201 assert_dom "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
202 assert_single_changeset changeset do
203 assert_dom "> discussion", 1 do
204 assert_dom "> comment", 2 do |dom_comments|
205 assert_dom dom_comments[0], "> @id", comment1.id.to_s
206 assert_dom dom_comments[0], "> @visible", "true"
207 assert_dom dom_comments[1], "> @id", comment3.id.to_s
208 assert_dom dom_comments[1], "> @visible", "true"
213 # one hidden comment not included because no permissions
214 get changeset_show_path(changeset), :params => { :include_discussion => true, :show_hidden_comments => true }
215 assert_response :success, "cannot get closed changeset with comments"
217 assert_dom "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
218 assert_single_changeset changeset do
219 assert_dom "> discussion", 1 do
220 assert_dom "> comment", 2 do |dom_comments|
221 assert_dom dom_comments[0], "> @id", comment1.id.to_s
222 assert_dom dom_comments[0], "> @visible", "true"
223 # maybe will show an empty comment element with visible=false in the future
224 assert_dom dom_comments[1], "> @id", comment3.id.to_s
225 assert_dom dom_comments[1], "> @visible", "true"
230 # one hidden comment shown to moderators
231 moderator_user = create(:moderator_user)
232 auth_header = bearer_authorization_header moderator_user
233 get changeset_show_path(changeset), :params => { :include_discussion => true, :show_hidden_comments => true },
234 :headers => auth_header
235 assert_response :success, "cannot get closed changeset with comments"
237 assert_dom "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
238 assert_single_changeset changeset do
239 assert_dom "> discussion", 1 do
240 assert_dom "> comment", 3 do |dom_comments|
241 assert_dom dom_comments[0], "> @id", comment1.id.to_s
242 assert_dom dom_comments[0], "> @visible", "true"
243 assert_dom dom_comments[1], "> @id", comment2.id.to_s
244 assert_dom dom_comments[1], "> @visible", "false"
245 assert_dom dom_comments[2], "> @id", comment3.id.to_s
246 assert_dom dom_comments[2], "> @visible", "true"
253 changeset = create(:changeset)
255 get changeset_show_path(changeset), :params => { :format => "json" }
256 assert_response :success, "cannot get first changeset"
258 js = ActiveSupport::JSON.decode(@response.body)
261 assert_equal Settings.api_version, js["version"]
262 assert_equal Settings.generator, js["generator"]
263 assert_single_changeset_json changeset, js
264 assert_nil js["changeset"]["tags"]
265 assert_nil js["changeset"]["comments"]
266 assert_equal changeset.user.id, js["changeset"]["uid"]
267 assert_equal changeset.user.display_name, js["changeset"]["user"]
269 get changeset_show_path(changeset), :params => { :format => "json", :include_discussion => true }
270 assert_response :success, "cannot get first changeset with comments"
272 js = ActiveSupport::JSON.decode(@response.body)
274 assert_equal Settings.api_version, js["version"]
275 assert_equal Settings.generator, js["generator"]
276 assert_single_changeset_json changeset, js
277 assert_nil js["changeset"]["tags"]
278 assert_nil js["changeset"]["min_lat"]
279 assert_nil js["changeset"]["min_lon"]
280 assert_nil js["changeset"]["max_lat"]
281 assert_nil js["changeset"]["max_lon"]
282 assert_equal 0, js["changeset"]["comments"].count
285 def test_show_comments_json
286 # all comments visible
287 changeset = create(:changeset, :closed)
288 comment0, comment1, comment2 = create_list(:changeset_comment, 3, :changeset_id => changeset.id)
290 get changeset_show_path(changeset), :params => { :format => "json", :include_discussion => true }
291 assert_response :success, "cannot get closed changeset with comments"
293 js = ActiveSupport::JSON.decode(@response.body)
295 assert_equal Settings.api_version, js["version"]
296 assert_equal Settings.generator, js["generator"]
297 assert_single_changeset_json changeset, js
298 assert_equal 3, js["changeset"]["comments"].count
299 assert_equal comment0.id, js["changeset"]["comments"][0]["id"]
300 assert js["changeset"]["comments"][0]["visible"]
301 assert_equal comment1.id, js["changeset"]["comments"][1]["id"]
302 assert js["changeset"]["comments"][1]["visible"]
303 assert_equal comment2.id, js["changeset"]["comments"][2]["id"]
304 assert js["changeset"]["comments"][2]["visible"]
306 # one hidden comment not included because not asked for
307 comment1.update(:visible => false)
310 get changeset_show_path(changeset), :params => { :format => "json", :include_discussion => true }
311 assert_response :success, "cannot get closed changeset with comments"
313 js = ActiveSupport::JSON.decode(@response.body)
315 assert_equal Settings.api_version, js["version"]
316 assert_equal Settings.generator, js["generator"]
317 assert_single_changeset_json changeset, js
318 assert_equal 2, js["changeset"]["comments"].count
319 assert_equal comment0.id, js["changeset"]["comments"][0]["id"]
320 assert js["changeset"]["comments"][0]["visible"]
321 assert_equal comment2.id, js["changeset"]["comments"][1]["id"]
322 assert js["changeset"]["comments"][1]["visible"]
324 # one hidden comment not included because no permissions
325 get changeset_show_path(changeset), :params => { :format => "json", :include_discussion => true, :show_hidden_comments => true }
326 assert_response :success, "cannot get closed changeset with comments"
328 js = ActiveSupport::JSON.decode(@response.body)
330 assert_equal Settings.api_version, js["version"]
331 assert_equal Settings.generator, js["generator"]
332 assert_single_changeset_json changeset, js
333 assert_equal 2, js["changeset"]["comments"].count
334 assert_equal comment0.id, js["changeset"]["comments"][0]["id"]
335 assert js["changeset"]["comments"][0]["visible"]
336 # maybe will show an empty comment element with visible=false in the future
337 assert_equal comment2.id, js["changeset"]["comments"][1]["id"]
338 assert js["changeset"]["comments"][1]["visible"]
340 # one hidden comment shown to moderators
341 moderator_user = create(:moderator_user)
342 auth_header = bearer_authorization_header moderator_user
343 get changeset_show_path(changeset), :params => { :format => "json", :include_discussion => true, :show_hidden_comments => true },
344 :headers => auth_header
345 assert_response :success, "cannot get closed changeset with comments"
347 js = ActiveSupport::JSON.decode(@response.body)
349 assert_equal Settings.api_version, js["version"]
350 assert_equal Settings.generator, js["generator"]
351 assert_single_changeset_json changeset, js
352 assert_equal 3, js["changeset"]["comments"].count
353 assert_equal comment0.id, js["changeset"]["comments"][0]["id"]
354 assert js["changeset"]["comments"][0]["visible"]
355 assert_equal comment1.id, js["changeset"]["comments"][1]["id"]
356 assert_not js["changeset"]["comments"][1]["visible"]
357 assert_equal comment2.id, js["changeset"]["comments"][2]["id"]
358 assert js["changeset"]["comments"][2]["visible"]
361 def test_show_tag_and_discussion_json
362 changeset = create(:changeset, :closed)
363 create(:changeset_tag, :changeset => changeset, :k => "created_by", :v => "JOSM/1.5 (18364)")
364 create(:changeset_tag, :changeset => changeset, :k => "comment", :v => "changeset comment")
365 create_list(:changeset_comment, 3, :changeset_id => changeset.id)
367 get changeset_show_path(changeset), :params => { :format => "json", :include_discussion => true }
368 assert_response :success, "cannot get closed changeset with comments"
370 js = ActiveSupport::JSON.decode(@response.body)
373 assert_equal Settings.api_version, js["version"]
374 assert_equal Settings.generator, js["generator"]
375 assert_single_changeset_json changeset, js
376 assert_equal 2, js["changeset"]["tags"].count
377 assert_equal 3, js["changeset"]["comments"].count
378 assert_not_nil js["changeset"]["comments"][0]["uid"]
379 assert_not_nil js["changeset"]["comments"][0]["user"]
380 assert_not_nil js["changeset"]["comments"][0]["text"]
383 def test_show_bbox_json
384 # test bbox attribute
385 changeset = create(:changeset, :min_lat => (-5 * GeoRecord::SCALE).round, :min_lon => (5 * GeoRecord::SCALE).round,
386 :max_lat => (15 * GeoRecord::SCALE).round, :max_lon => (12 * GeoRecord::SCALE).round)
388 get changeset_show_path(changeset), :params => { :format => "json" }
389 assert_response :success, "cannot get first changeset"
391 js = ActiveSupport::JSON.decode(@response.body)
393 assert_equal(-5, js["changeset"]["min_lat"])
394 assert_equal 5, js["changeset"]["min_lon"]
395 assert_equal 15, js["changeset"]["max_lat"]
396 assert_equal 12, js["changeset"]["max_lon"]
400 # check that a changeset that doesn't exist returns an appropriate message
401 def test_show_not_found
402 [0, -32, 233455644, "afg", "213"].each do |id|
403 get changeset_show_path(id)
404 assert_response :not_found, "should get a not found"
405 rescue ActionController::UrlGenerationError => e
406 assert_match(/No route matches/, e.to_s)
411 # test that the user who opened a change can close it
413 private_user = create(:user, :data_public => false)
414 private_changeset = create(:changeset, :user => private_user)
416 changeset = create(:changeset, :user => user)
418 ## Try without authentication
419 put changeset_close_path(changeset)
420 assert_response :unauthorized
422 ## Try using the non-public user
423 auth_header = bearer_authorization_header private_user
424 put changeset_close_path(private_changeset), :headers => auth_header
425 assert_require_public_data
427 ## The try with the public user
428 auth_header = bearer_authorization_header user
431 put changeset_close_path(cs_id), :headers => auth_header
432 assert_response :success
434 # test that it really is closed now
435 cs = Changeset.find(changeset.id)
437 "changeset should be closed now (#{cs.closed_at} > #{Time.now.utc}.")
441 # test that a different user can't close another user's changeset
442 def test_close_invalid
444 changeset = create(:changeset)
446 auth_header = bearer_authorization_header user
448 put changeset_close_path(changeset), :headers => auth_header
449 assert_response :conflict
450 assert_equal "The user doesn't own that changeset", @response.body
454 # test that you can't close using another method
455 def test_close_method_invalid
457 changeset = create(:changeset, :user => user)
459 auth_header = bearer_authorization_header user
461 get changeset_close_path(changeset), :headers => auth_header
462 assert_response :not_found
463 assert_template "rescues/routing_error"
465 post changeset_close_path(changeset), :headers => auth_header
466 assert_response :not_found
467 assert_template "rescues/routing_error"
471 # check that you can't close a changeset that isn't found
472 def test_close_not_found
473 cs_ids = [0, -132, "123"]
475 # First try to do it with no auth
477 put changeset_close_path(id)
478 assert_response :unauthorized, "Shouldn't be able close the non-existant changeset #{id}, when not authorized"
479 rescue ActionController::UrlGenerationError => e
480 assert_match(/No route matches/, e.to_s)
484 auth_header = bearer_authorization_header
486 put changeset_close_path(id), :headers => auth_header
487 assert_response :not_found, "The changeset #{id} doesn't exist, so can't be closed"
488 rescue ActionController::UrlGenerationError => e
489 assert_match(/No route matches/, e.to_s)
494 # upload something simple, but valid and check that it can
496 # Also try without auth and another user.
497 def test_upload_simple_valid
498 private_user = create(:user, :data_public => false)
499 private_changeset = create(:changeset, :user => private_user)
501 changeset = create(:changeset, :user => user)
505 relation = create(:relation)
506 other_relation = create(:relation)
507 # create some tags, since we test that they are removed later
508 create(:node_tag, :node => node)
509 create(:way_tag, :way => way)
510 create(:relation_tag, :relation => relation)
513 changeset_id = changeset.id
515 # simple diff to change a node, way and relation by removing
520 <node id='#{node.id}' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
521 <way id='#{way.id}' changeset='#{changeset_id}' version='1'>
522 <nd ref='#{node.id}'/>
526 <relation id='#{relation.id}' changeset='#{changeset_id}' version='1'>
527 <member type='way' role='some' ref='#{way.id}'/>
528 <member type='node' role='some' ref='#{node.id}'/>
529 <member type='relation' role='some' ref='#{other_relation.id}'/>
536 post changeset_upload_path(changeset), :params => diff
537 assert_response :unauthorized,
538 "shouldn't be able to upload a simple valid diff to changeset: #{@response.body}"
540 ## Now try with a private user
541 auth_header = bearer_authorization_header private_user
542 changeset_id = private_changeset.id
544 # simple diff to change a node, way and relation by removing
549 <node id='#{node.id}' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
550 <way id='#{way.id}' changeset='#{changeset_id}' version='1'>
551 <nd ref='#{node.id}'/>
555 <relation id='#{relation.id}' changeset='#{changeset_id}' version='1'>
556 <member type='way' role='some' ref='#{way.id}'/>
557 <member type='node' role='some' ref='#{node.id}'/>
558 <member type='relation' role='some' ref='#{other_relation.id}'/>
565 post changeset_upload_path(private_changeset), :params => diff, :headers => auth_header
566 assert_response :forbidden,
567 "can't upload a simple valid diff to changeset: #{@response.body}"
569 ## Now try with the public user
570 auth_header = bearer_authorization_header user
571 changeset_id = changeset.id
573 # simple diff to change a node, way and relation by removing
578 <node id='#{node.id}' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
579 <way id='#{way.id}' changeset='#{changeset_id}' version='1'>
580 <nd ref='#{node.id}'/>
584 <relation id='#{relation.id}' changeset='#{changeset_id}' version='1'>
585 <member type='way' role='some' ref='#{way.id}'/>
586 <member type='node' role='some' ref='#{node.id}'/>
587 <member type='relation' role='some' ref='#{other_relation.id}'/>
594 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
595 assert_response :success,
596 "can't upload a simple valid diff to changeset: #{@response.body}"
598 # check that the changes made it into the database
599 assert_equal 0, Node.find(node.id).tags.size, "node #{node.id} should now have no tags"
600 assert_equal 0, Way.find(way.id).tags.size, "way #{way.id} should now have no tags"
601 assert_equal 0, Relation.find(relation.id).tags.size, "relation #{relation.id} should now have no tags"
605 # upload something which creates new objects using placeholders
606 def test_upload_create_valid
608 changeset = create(:changeset, :user => user)
610 way = create(:way_with_nodes, :nodes_count => 2)
611 relation = create(:relation)
613 auth_header = bearer_authorization_header user
615 # simple diff to create a node way and relation using placeholders
619 <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
620 <tag k='foo' v='bar'/>
621 <tag k='baz' v='bat'/>
623 <way id='-1' changeset='#{changeset.id}'>
624 <nd ref='#{node.id}'/>
628 <relation id='-1' changeset='#{changeset.id}'>
629 <member type='way' role='some' ref='#{way.id}'/>
630 <member type='node' role='some' ref='#{node.id}'/>
631 <member type='relation' role='some' ref='#{relation.id}'/>
638 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
639 assert_response :success,
640 "can't upload a simple valid creation to changeset: #{@response.body}"
642 # check the returned payload
643 new_node_id, new_way_id, new_rel_id = nil
644 assert_dom "diffResult[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1 do
645 # inspect the response to find out what the new element IDs are
646 # check the old IDs are all present and negative one
647 # check the versions are present and equal one
648 assert_dom "> node", 1 do |(node_el)|
649 new_node_id = node_el["new_id"].to_i
650 assert_dom "> @old_id", "-1"
651 assert_dom "> @new_version", "1"
653 assert_dom "> way", 1 do |(way_el)|
654 new_way_id = way_el["new_id"].to_i
655 assert_dom "> @old_id", "-1"
656 assert_dom "> @new_version", "1"
658 assert_dom "> relation", 1 do |(rel_el)|
659 new_rel_id = rel_el["new_id"].to_i
660 assert_dom "> @old_id", "-1"
661 assert_dom "> @new_version", "1"
665 # check that the changes made it into the database
666 assert_equal 2, Node.find(new_node_id).tags.size, "new node should have two tags"
667 assert_equal 0, Way.find(new_way_id).tags.size, "new way should have no tags"
668 assert_equal 0, Relation.find(new_rel_id).tags.size, "new relation should have no tags"
672 # test a complex delete where we delete elements which rely on eachother
673 # in the same transaction.
674 def test_upload_delete
675 changeset = create(:changeset)
676 super_relation = create(:relation)
677 used_relation = create(:relation)
678 used_way = create(:way)
679 used_node = create(:node)
680 create(:relation_member, :relation => super_relation, :member => used_relation)
681 create(:relation_member, :relation => super_relation, :member => used_way)
682 create(:relation_member, :relation => super_relation, :member => used_node)
684 auth_header = bearer_authorization_header changeset.user
686 diff = XML::Document.new
687 diff.root = XML::Node.new "osmChange"
688 delete = XML::Node.new "delete"
690 delete << xml_node_for_relation(super_relation)
691 delete << xml_node_for_relation(used_relation)
692 delete << xml_node_for_way(used_way)
693 delete << xml_node_for_node(used_node)
695 # update the changeset to one that this user owns
696 %w[node way relation].each do |type|
697 delete.find("//osmChange/delete/#{type}").each do |n|
698 n["changeset"] = changeset.id.to_s
703 post changeset_upload_path(changeset), :params => diff.to_s, :headers => auth_header
704 assert_response :success,
705 "can't upload a deletion diff to changeset: #{@response.body}"
707 # check the response is well-formed
708 assert_select "diffResult>node", 1
709 assert_select "diffResult>way", 1
710 assert_select "diffResult>relation", 2
712 # check that everything was deleted
713 assert_not Node.find(used_node.id).visible
714 assert_not Way.find(used_way.id).visible
715 assert_not Relation.find(super_relation.id).visible
716 assert_not Relation.find(used_relation.id).visible
720 # test uploading a delete with no lat/lon, as they are optional in
721 # the osmChange spec.
722 def test_upload_nolatlon_delete
724 changeset = create(:changeset)
726 auth_header = bearer_authorization_header changeset.user
727 diff = "<osmChange><delete><node id='#{node.id}' version='#{node.version}' changeset='#{changeset.id}'/></delete></osmChange>"
730 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
731 assert_response :success,
732 "can't upload a deletion diff to changeset: #{@response.body}"
734 # check the response is well-formed
735 assert_select "diffResult>node", 1
737 # check that everything was deleted
738 assert_not Node.find(node.id).visible
741 def test_repeated_changeset_create
743 auth_header = bearer_authorization_header
745 # create a temporary changeset
746 xml = "<osm><changeset>" \
747 "<tag k='created_by' v='osm test suite checking changesets'/>" \
749 assert_difference "Changeset.count", 1 do
750 put changeset_create_path, :params => xml, :headers => auth_header
752 assert_response :success
756 def test_upload_large_changeset
758 auth_header = bearer_authorization_header user
760 # create an old changeset to ensure we have the maximum rate limit
761 create(:changeset, :user => user, :created_at => Time.now.utc - 28.days)
764 put changeset_create_path, :params => "<osm><changeset/></osm>", :headers => auth_header
765 assert_response :success, "Should be able to create a changeset: #{@response.body}"
766 changeset_id = @response.body.to_i
768 # upload some widely-spaced nodes, spiralling positive and negative
772 <node id='-1' lon='-20' lat='-10' changeset='#{changeset_id}'/>
773 <node id='-10' lon='20' lat='10' changeset='#{changeset_id}'/>
774 <node id='-2' lon='-40' lat='-20' changeset='#{changeset_id}'/>
775 <node id='-11' lon='40' lat='20' changeset='#{changeset_id}'/>
776 <node id='-3' lon='-60' lat='-30' changeset='#{changeset_id}'/>
777 <node id='-12' lon='60' lat='30' changeset='#{changeset_id}'/>
778 <node id='-4' lon='-80' lat='-40' changeset='#{changeset_id}'/>
779 <node id='-13' lon='80' lat='40' changeset='#{changeset_id}'/>
780 <node id='-5' lon='-100' lat='-50' changeset='#{changeset_id}'/>
781 <node id='-14' lon='100' lat='50' changeset='#{changeset_id}'/>
782 <node id='-6' lon='-120' lat='-60' changeset='#{changeset_id}'/>
783 <node id='-15' lon='120' lat='60' changeset='#{changeset_id}'/>
784 <node id='-7' lon='-140' lat='-70' changeset='#{changeset_id}'/>
785 <node id='-16' lon='140' lat='70' changeset='#{changeset_id}'/>
786 <node id='-8' lon='-160' lat='-80' changeset='#{changeset_id}'/>
787 <node id='-17' lon='160' lat='80' changeset='#{changeset_id}'/>
788 <node id='-9' lon='-179.9' lat='-89.9' changeset='#{changeset_id}'/>
789 <node id='-18' lon='179.9' lat='89.9' changeset='#{changeset_id}'/>
794 # upload it, which used to cause an error like "PGError: ERROR:
795 # integer out of range" (bug #2152). but shouldn't any more.
796 post changeset_upload_path(changeset_id), :params => diff, :headers => auth_header
797 assert_response :success,
798 "can't upload a spatially-large diff to changeset: #{@response.body}"
800 # check that the changeset bbox is within bounds
801 cs = Changeset.find(changeset_id)
802 assert_operator cs.min_lon, :>=, -180 * GeoRecord::SCALE, "Minimum longitude (#{cs.min_lon / GeoRecord::SCALE}) should be >= -180 to be valid."
803 assert_operator cs.max_lon, :<=, 180 * GeoRecord::SCALE, "Maximum longitude (#{cs.max_lon / GeoRecord::SCALE}) should be <= 180 to be valid."
804 assert_operator cs.min_lat, :>=, -90 * GeoRecord::SCALE, "Minimum latitude (#{cs.min_lat / GeoRecord::SCALE}) should be >= -90 to be valid."
805 assert_operator cs.max_lat, :<=, 90 * GeoRecord::SCALE, "Maximum latitude (#{cs.max_lat / GeoRecord::SCALE}) should be <= 90 to be valid."
809 # test that deleting stuff in a transaction doesn't bypass the checks
810 # to ensure that used elements are not deleted.
811 def test_upload_delete_invalid
812 changeset = create(:changeset)
813 relation = create(:relation)
814 other_relation = create(:relation)
815 used_way = create(:way)
816 used_node = create(:node)
817 create(:relation_member, :relation => relation, :member => used_way)
818 create(:relation_member, :relation => relation, :member => used_node)
820 auth_header = bearer_authorization_header changeset.user
822 diff = XML::Document.new
823 diff.root = XML::Node.new "osmChange"
824 delete = XML::Node.new "delete"
826 delete << xml_node_for_relation(other_relation)
827 delete << xml_node_for_way(used_way)
828 delete << xml_node_for_node(used_node)
830 # update the changeset to one that this user owns
831 %w[node way relation].each do |type|
832 delete.find("//osmChange/delete/#{type}").each do |n|
833 n["changeset"] = changeset.id.to_s
838 post changeset_upload_path(changeset), :params => diff.to_s, :headers => auth_header
839 assert_response :precondition_failed,
840 "shouldn't be able to upload a invalid deletion diff: #{@response.body}"
841 assert_equal "Precondition failed: Way #{used_way.id} is still used by relations #{relation.id}.", @response.body
843 # check that nothing was, in fact, deleted
844 assert Node.find(used_node.id).visible
845 assert Way.find(used_way.id).visible
846 assert Relation.find(relation.id).visible
847 assert Relation.find(other_relation.id).visible
851 # test that a conditional delete of an in use object works.
852 def test_upload_delete_if_unused
853 changeset = create(:changeset)
854 super_relation = create(:relation)
855 used_relation = create(:relation)
856 used_way = create(:way)
857 used_node = create(:node)
858 create(:relation_member, :relation => super_relation, :member => used_relation)
859 create(:relation_member, :relation => super_relation, :member => used_way)
860 create(:relation_member, :relation => super_relation, :member => used_node)
862 auth_header = bearer_authorization_header changeset.user
864 diff = XML::Document.new
865 diff.root = XML::Node.new "osmChange"
866 delete = XML::Node.new "delete"
868 delete["if-unused"] = ""
869 delete << xml_node_for_relation(used_relation)
870 delete << xml_node_for_way(used_way)
871 delete << xml_node_for_node(used_node)
873 # update the changeset to one that this user owns
874 %w[node way relation].each do |type|
875 delete.find("//osmChange/delete/#{type}").each do |n|
876 n["changeset"] = changeset.id.to_s
881 post changeset_upload_path(changeset), :params => diff.to_s, :headers => auth_header
882 assert_response :success,
883 "can't do a conditional delete of in use objects: #{@response.body}"
885 # check the returned payload
886 assert_dom "diffResult[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1 do
887 # check the old IDs are all present and what we expect
888 # check the new IDs are all present and unchanged
889 # check the new versions are all present and unchanged
890 assert_dom "> node", 1 do
891 assert_dom "> @old_id", used_node.id.to_s
892 assert_dom "> @new_id", used_node.id.to_s
893 assert_dom "> @new_version", used_node.version.to_s
895 assert_dom "> way", 1 do
896 assert_dom "> @old_id", used_way.id.to_s
897 assert_dom "> @new_id", used_way.id.to_s
898 assert_dom "> @new_version", used_way.version.to_s
900 assert_dom "> relation", 1 do
901 assert_dom "> @old_id", used_relation.id.to_s
902 assert_dom "> @new_id", used_relation.id.to_s
903 assert_dom "> @new_version", used_relation.version.to_s
907 # check that nothing was, in fact, deleted
908 assert Node.find(used_node.id).visible
909 assert Way.find(used_way.id).visible
910 assert Relation.find(used_relation.id).visible
914 # upload an element with a really long tag value
915 def test_upload_invalid_too_long_tag
916 changeset = create(:changeset)
918 auth_header = bearer_authorization_header changeset.user
920 # simple diff to create a node way and relation using placeholders
924 <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
925 <tag k='foo' v='#{'x' * 256}'/>
932 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
933 assert_response :bad_request,
934 "shouldn't be able to upload too long a tag to changeset: #{@response.body}"
938 # upload something which creates new objects and inserts them into
939 # existing containers using placeholders.
940 def test_upload_complex
943 relation = create(:relation)
944 create(:way_node, :way => way, :node => node)
946 changeset = create(:changeset)
948 auth_header = bearer_authorization_header changeset.user
950 # simple diff to create a node way and relation using placeholders
954 <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
955 <tag k='foo' v='bar'/>
956 <tag k='baz' v='bat'/>
960 <way id='#{way.id}' changeset='#{changeset.id}' version='1'>
962 <nd ref='#{node.id}'/>
964 <relation id='#{relation.id}' changeset='#{changeset.id}' version='1'>
965 <member type='way' role='some' ref='#{way.id}'/>
966 <member type='node' role='some' ref='-1'/>
967 <member type='relation' role='some' ref='#{relation.id}'/>
974 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
975 assert_response :success,
976 "can't upload a complex diff to changeset: #{@response.body}"
978 # check the returned payload
980 assert_dom "diffResult[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1 do
981 assert_dom "> node", 1 do |(node_el)|
982 new_node_id = node_el["new_id"].to_i
984 assert_dom "> way", 1
985 assert_dom "> relation", 1
988 # check that the changes made it into the database
989 assert_equal 2, Node.find(new_node_id).tags.size, "new node should have two tags"
990 assert_equal [new_node_id, node.id], Way.find(way.id).nds, "way nodes should match"
991 Relation.find(relation.id).members.each do |type, id, _role|
992 assert_equal new_node_id, id, "relation should contain new node" if type == "node"
997 # create a diff which references several changesets, which should cause
998 # a rollback and none of the diff gets committed
999 def test_upload_invalid_changesets
1000 changeset = create(:changeset)
1001 other_changeset = create(:changeset, :user => changeset.user)
1002 node = create(:node)
1004 relation = create(:relation)
1005 other_relation = create(:relation)
1007 auth_header = bearer_authorization_header changeset.user
1009 # simple diff to create a node way and relation using placeholders
1013 <node id='#{node.id}' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
1014 <way id='#{way.id}' changeset='#{changeset.id}' version='1'>
1015 <nd ref='#{node.id}'/>
1019 <relation id='#{relation.id}' changeset='#{changeset.id}' version='1'>
1020 <member type='way' role='some' ref='#{way.id}'/>
1021 <member type='node' role='some' ref='#{node.id}'/>
1022 <member type='relation' role='some' ref='#{other_relation.id}'/>
1026 <node id='-1' lon='0' lat='0' changeset='#{other_changeset.id}'>
1027 <tag k='foo' v='bar'/>
1028 <tag k='baz' v='bat'/>
1035 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1036 assert_response :conflict,
1037 "uploading a diff with multiple changesets should have failed"
1039 # check that objects are unmodified
1040 assert_nodes_are_equal(node, Node.find(node.id))
1041 assert_ways_are_equal(way, Way.find(way.id))
1042 assert_relations_are_equal(relation, Relation.find(relation.id))
1046 # upload multiple versions of the same element in the same diff.
1047 def test_upload_multiple_valid
1048 node = create(:node)
1049 changeset = create(:changeset)
1050 auth_header = bearer_authorization_header changeset.user
1052 # change the location of a node multiple times, each time referencing
1053 # the last version. doesn't this depend on version numbers being
1058 <node id='#{node.id}' lon='0.0' lat='0.0' changeset='#{changeset.id}' version='1'/>
1059 <node id='#{node.id}' lon='0.1' lat='0.0' changeset='#{changeset.id}' version='2'/>
1060 <node id='#{node.id}' lon='0.1' lat='0.1' changeset='#{changeset.id}' version='3'/>
1061 <node id='#{node.id}' lon='0.1' lat='0.2' changeset='#{changeset.id}' version='4'/>
1062 <node id='#{node.id}' lon='0.2' lat='0.2' changeset='#{changeset.id}' version='5'/>
1063 <node id='#{node.id}' lon='0.3' lat='0.2' changeset='#{changeset.id}' version='6'/>
1064 <node id='#{node.id}' lon='0.3' lat='0.3' changeset='#{changeset.id}' version='7'/>
1065 <node id='#{node.id}' lon='0.9' lat='0.9' changeset='#{changeset.id}' version='8'/>
1071 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1072 assert_response :success,
1073 "can't upload multiple versions of an element in a diff: #{@response.body}"
1075 # check the response is well-formed. its counter-intuitive, but the
1076 # API will return multiple elements with the same ID and different
1077 # version numbers for each change we made.
1078 assert_select "diffResult>node", 8
1082 # upload multiple versions of the same element in the same diff, but
1083 # keep the version numbers the same.
1084 def test_upload_multiple_duplicate
1085 node = create(:node)
1086 changeset = create(:changeset)
1088 auth_header = bearer_authorization_header changeset.user
1093 <node id='#{node.id}' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
1094 <node id='#{node.id}' lon='1' lat='1' changeset='#{changeset.id}' version='1'/>
1100 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1101 assert_response :conflict,
1102 "shouldn't be able to upload the same element twice in a diff: #{@response.body}"
1106 # try to upload some elements without specifying the version
1107 def test_upload_missing_version
1108 changeset = create(:changeset)
1110 auth_header = bearer_authorization_header changeset.user
1115 <node id='1' lon='1' lat='1' changeset='#{changeset.id}'/>
1121 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1122 assert_response :bad_request,
1123 "shouldn't be able to upload an element without version: #{@response.body}"
1127 # try to upload with commands other than create, modify, or delete
1128 def test_action_upload_invalid
1129 changeset = create(:changeset)
1131 auth_header = bearer_authorization_header changeset.user
1136 <node id='1' lon='1' lat='1' changeset='#{changeset.id}' />
1140 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1141 assert_response :bad_request, "Shouldn't be able to upload a diff with the action ping"
1142 assert_equal("Unknown action ping, choices are create, modify, delete", @response.body)
1146 # upload a valid changeset which has a mixture of whitespace
1147 # to check a bug reported by ivansanchez (#1565).
1148 def test_upload_whitespace_valid
1149 changeset = create(:changeset)
1150 node = create(:node)
1151 way = create(:way_with_nodes, :nodes_count => 2)
1152 relation = create(:relation)
1153 other_relation = create(:relation)
1154 create(:relation_tag, :relation => relation)
1156 auth_header = bearer_authorization_header changeset.user
1160 <modify><node id='#{node.id}' lon='0' lat='0' changeset='#{changeset.id}'
1162 <node id='#{node.id}' lon='1' lat='1' changeset='#{changeset.id}' version='2'><tag k='k' v='v'/></node></modify>
1164 <relation id='#{relation.id}' changeset='#{changeset.id}' version='1'><member
1165 type='way' role='some' ref='#{way.id}'/><member
1166 type='node' role='some' ref='#{node.id}'/>
1167 <member type='relation' role='some' ref='#{other_relation.id}'/>
1169 </modify></osmChange>
1173 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1174 assert_response :success,
1175 "can't upload a valid diff with whitespace variations to changeset: #{@response.body}"
1177 # check the response is well-formed
1178 assert_select "diffResult>node", 2
1179 assert_select "diffResult>relation", 1
1181 # check that the changes made it into the database
1182 assert_equal 1, Node.find(node.id).tags.size, "node #{node.id} should now have one tag"
1183 assert_equal 0, Relation.find(relation.id).tags.size, "relation #{relation.id} should now have no tags"
1187 # test that a placeholder can be reused within the same upload.
1188 def test_upload_reuse_placeholder_valid
1189 changeset = create(:changeset)
1191 auth_header = bearer_authorization_header changeset.user
1196 <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
1197 <tag k="foo" v="bar"/>
1201 <node id='-1' lon='1' lat='1' changeset='#{changeset.id}' version='1'/>
1204 <node id='-1' lon='2' lat='2' changeset='#{changeset.id}' version='2'/>
1210 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1211 assert_response :success,
1212 "can't upload a valid diff with re-used placeholders to changeset: #{@response.body}"
1214 # check the response is well-formed
1215 assert_select "diffResult>node", 3
1216 assert_select "diffResult>node[old_id='-1']", 3
1220 # test what happens if a diff upload re-uses placeholder IDs in an
1222 def test_upload_placeholder_invalid
1223 changeset = create(:changeset)
1225 auth_header = bearer_authorization_header changeset.user
1230 <node id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
1231 <node id='-1' lon='1' lat='1' changeset='#{changeset.id}' version='1'/>
1232 <node id='-1' lon='2' lat='2' changeset='#{changeset.id}' version='2'/>
1238 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1239 assert_response :bad_request,
1240 "shouldn't be able to re-use placeholder IDs"
1242 # placeholder_ids must be unique across all action blocks
1246 <node id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
1249 <node id='-1' lon='1' lat='1' changeset='#{changeset.id}' version='1'/>
1255 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1256 assert_response :bad_request,
1257 "shouldn't be able to re-use placeholder IDs"
1260 def test_upload_process_order
1261 changeset = create(:changeset)
1263 auth_header = bearer_authorization_header changeset.user
1268 <node id="-1" lat="1" lon="2" changeset="#{changeset.id}"/>
1269 <way id="-1" changeset="#{changeset.id}">
1273 <node id="-2" lat="1" lon="2" changeset="#{changeset.id}"/>
1279 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1280 assert_response :bad_request,
1281 "shouldn't refer elements behind it"
1284 def test_upload_duplicate_delete
1285 changeset = create(:changeset)
1287 auth_header = bearer_authorization_header changeset.user
1292 <node id="-1" lat="39" lon="116" changeset="#{changeset.id}" />
1295 <node id="-1" version="1" changeset="#{changeset.id}" />
1296 <node id="-1" version="1" changeset="#{changeset.id}" />
1302 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1303 assert_response :gone,
1304 "transaction should be cancelled by second deletion"
1309 <node id="-1" lat="39" lon="116" changeset="#{changeset.id}" />
1311 <delete if-unused="true">
1312 <node id="-1" version="1" changeset="#{changeset.id}" />
1313 <node id="-1" version="1" changeset="#{changeset.id}" />
1319 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1321 assert_select "diffResult>node", 3
1322 assert_select "diffResult>node[old_id='-1']", 3
1323 assert_select "diffResult>node[new_version='1']", 1
1324 assert_select "diffResult>node[new_version='2']", 1
1328 # test that uploading a way referencing invalid placeholders gives a
1329 # proper error, not a 500.
1330 def test_upload_placeholder_invalid_way
1331 changeset = create(:changeset)
1334 auth_header = bearer_authorization_header changeset.user
1339 <node id="-1" lon="0.0" lat="0.0" changeset="#{changeset.id}" version="1"/>
1340 <node id="-2" lon="0.1" lat="0.1" changeset="#{changeset.id}" version="1"/>
1341 <node id="-3" lon="0.2" lat="0.2" changeset="#{changeset.id}" version="1"/>
1342 <way id="-1" changeset="#{changeset.id}" version="1">
1353 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1354 assert_response :bad_request,
1355 "shouldn't be able to use invalid placeholder IDs"
1356 assert_equal "Placeholder node not found for reference -4 in way -1", @response.body
1358 # the same again, but this time use an existing way
1362 <node id="-1" lon="0.0" lat="0.0" changeset="#{changeset.id}" version="1"/>
1363 <node id="-2" lon="0.1" lat="0.1" changeset="#{changeset.id}" version="1"/>
1364 <node id="-3" lon="0.2" lat="0.2" changeset="#{changeset.id}" version="1"/>
1365 <way id="#{way.id}" changeset="#{changeset.id}" version="1">
1376 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1377 assert_response :bad_request,
1378 "shouldn't be able to use invalid placeholder IDs"
1379 assert_equal "Placeholder node not found for reference -4 in way #{way.id}", @response.body
1383 # test that uploading a relation referencing invalid placeholders gives a
1384 # proper error, not a 500.
1385 def test_upload_placeholder_invalid_relation
1386 changeset = create(:changeset)
1387 relation = create(:relation)
1389 auth_header = bearer_authorization_header changeset.user
1394 <node id="-1" lon="0.0" lat="0.0" changeset="#{changeset.id}" version="1"/>
1395 <node id="-2" lon="0.1" lat="0.1" changeset="#{changeset.id}" version="1"/>
1396 <node id="-3" lon="0.2" lat="0.2" changeset="#{changeset.id}" version="1"/>
1397 <relation id="-1" changeset="#{changeset.id}" version="1">
1398 <member type="node" role="foo" ref="-1"/>
1399 <member type="node" role="foo" ref="-2"/>
1400 <member type="node" role="foo" ref="-3"/>
1401 <member type="node" role="foo" ref="-4"/>
1408 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1409 assert_response :bad_request,
1410 "shouldn't be able to use invalid placeholder IDs"
1411 assert_equal "Placeholder Node not found for reference -4 in relation -1.", @response.body
1413 # the same again, but this time use an existing relation
1417 <node id="-1" lon="0.0" lat="0.0" changeset="#{changeset.id}" version="1"/>
1418 <node id="-2" lon="0.1" lat="0.1" changeset="#{changeset.id}" version="1"/>
1419 <node id="-3" lon="0.2" lat="0.2" changeset="#{changeset.id}" version="1"/>
1420 <relation id="#{relation.id}" changeset="#{changeset.id}" version="1">
1421 <member type="node" role="foo" ref="-1"/>
1422 <member type="node" role="foo" ref="-2"/>
1423 <member type="node" role="foo" ref="-3"/>
1424 <member type="way" role="bar" ref="-1"/>
1431 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1432 assert_response :bad_request,
1433 "shouldn't be able to use invalid placeholder IDs"
1434 assert_equal "Placeholder Way not found for reference -1 in relation #{relation.id}.", @response.body
1438 # test what happens if a diff is uploaded containing only a node
1440 def test_upload_node_move
1441 auth_header = bearer_authorization_header
1443 xml = "<osm><changeset>" \
1444 "<tag k='created_by' v='osm test suite checking changesets'/>" \
1445 "</changeset></osm>"
1446 put changeset_create_path, :params => xml, :headers => auth_header
1447 assert_response :success
1448 changeset_id = @response.body.to_i
1450 old_node = create(:node, :lat => 1, :lon => 1)
1452 diff = XML::Document.new
1453 diff.root = XML::Node.new "osmChange"
1454 modify = XML::Node.new "modify"
1455 xml_old_node = xml_node_for_node(old_node)
1456 xml_old_node["lat"] = 2.0.to_s
1457 xml_old_node["lon"] = 2.0.to_s
1458 xml_old_node["changeset"] = changeset_id.to_s
1459 modify << xml_old_node
1463 post changeset_upload_path(changeset_id), :params => diff.to_s, :headers => auth_header
1464 assert_response :success,
1465 "diff should have uploaded OK"
1468 changeset = Changeset.find(changeset_id)
1469 assert_equal 1 * GeoRecord::SCALE, changeset.min_lon, "min_lon should be 1 degree"
1470 assert_equal 2 * GeoRecord::SCALE, changeset.max_lon, "max_lon should be 2 degrees"
1471 assert_equal 1 * GeoRecord::SCALE, changeset.min_lat, "min_lat should be 1 degree"
1472 assert_equal 2 * GeoRecord::SCALE, changeset.max_lat, "max_lat should be 2 degrees"
1476 # test what happens if a diff is uploaded adding a node to a way.
1477 def test_upload_way_extend
1478 auth_header = bearer_authorization_header
1480 xml = "<osm><changeset>" \
1481 "<tag k='created_by' v='osm test suite checking changesets'/>" \
1482 "</changeset></osm>"
1483 put changeset_create_path, :params => xml, :headers => auth_header
1484 assert_response :success
1485 changeset_id = @response.body.to_i
1487 old_way = create(:way)
1488 create(:way_node, :way => old_way, :node => create(:node, :lat => 0.1, :lon => 0.1))
1490 diff = XML::Document.new
1491 diff.root = XML::Node.new "osmChange"
1492 modify = XML::Node.new "modify"
1493 xml_old_way = xml_node_for_way(old_way)
1494 nd_ref = XML::Node.new "nd"
1495 nd_ref["ref"] = create(:node, :lat => 0.3, :lon => 0.3).id.to_s
1496 xml_old_way << nd_ref
1497 xml_old_way["changeset"] = changeset_id.to_s
1498 modify << xml_old_way
1502 post changeset_upload_path(changeset_id), :params => diff.to_s, :headers => auth_header
1503 assert_response :success,
1504 "diff should have uploaded OK"
1507 changeset = Changeset.find(changeset_id)
1508 assert_equal 0.1 * GeoRecord::SCALE, changeset.min_lon, "min_lon should be 0.1 degree"
1509 assert_equal 0.3 * GeoRecord::SCALE, changeset.max_lon, "max_lon should be 0.3 degrees"
1510 assert_equal 0.1 * GeoRecord::SCALE, changeset.min_lat, "min_lat should be 0.1 degree"
1511 assert_equal 0.3 * GeoRecord::SCALE, changeset.max_lat, "max_lat should be 0.3 degrees"
1515 # test for more issues in #1568
1516 def test_upload_empty_invalid
1517 changeset = create(:changeset)
1519 auth_header = bearer_authorization_header changeset.user
1522 "<osmChange></osmChange>",
1523 "<osmChange><modify/></osmChange>",
1524 "<osmChange><modify></modify></osmChange>"].each do |diff|
1526 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1527 assert_response(:success, "should be able to upload " \
1528 "empty changeset: " + diff)
1533 # test that the X-Error-Format header works to request XML errors
1534 def test_upload_xml_errors
1535 changeset = create(:changeset)
1536 node = create(:node)
1537 create(:relation_member, :member => node)
1539 auth_header = bearer_authorization_header changeset.user
1541 # try and delete a node that is in use
1542 diff = XML::Document.new
1543 diff.root = XML::Node.new "osmChange"
1544 delete = XML::Node.new "delete"
1546 delete << xml_node_for_node(node)
1549 error_header = error_format_header "xml"
1550 post changeset_upload_path(changeset), :params => diff.to_s, :headers => auth_header.merge(error_header)
1551 assert_response :success,
1552 "failed to return error in XML format"
1554 # check the returned payload
1555 assert_select "osmError[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
1556 assert_select "osmError>status", 1
1557 assert_select "osmError>message", 1
1560 def test_upload_not_found
1561 changeset = create(:changeset)
1563 auth_header = bearer_authorization_header changeset.user
1569 <node id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
1575 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1576 assert_response :not_found, "Node should not be found"
1582 <way id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
1588 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1589 assert_response :not_found, "Way should not be found"
1595 <relation id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
1601 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1602 assert_response :not_found, "Relation should not be found"
1608 <node id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
1614 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1615 assert_response :not_found, "Node should not be deleted"
1621 <way id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
1627 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1628 assert_response :not_found, "Way should not be deleted"
1634 <relation id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
1640 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1641 assert_response :not_found, "Relation should not be deleted"
1644 def test_upload_relation_placeholder_not_fix
1645 changeset = create(:changeset)
1647 auth_header = bearer_authorization_header changeset.user
1651 <osmChange version='0.6'>
1653 <relation id='-2' version='0' changeset='#{changeset.id}'>
1654 <member type='relation' role='' ref='-4' />
1655 <tag k='type' v='route' />
1656 <tag k='name' v='AtoB' />
1658 <relation id='-3' version='0' changeset='#{changeset.id}'>
1659 <tag k='type' v='route' />
1660 <tag k='name' v='BtoA' />
1662 <relation id='-4' version='0' changeset='#{changeset.id}'>
1663 <member type='relation' role='' ref='-2' />
1664 <member type='relation' role='' ref='-3' />
1665 <tag k='type' v='route_master' />
1666 <tag k='name' v='master' />
1673 post changeset_upload_path(changeset), :params => diff.to_s, :headers => auth_header
1674 assert_response :bad_request, "shouldn't be able to use reference -4 in relation -2: #{@response.body}"
1675 assert_equal "Placeholder Relation not found for reference -4 in relation -2.", @response.body
1678 def test_upload_multiple_delete_block
1679 changeset = create(:changeset)
1681 auth_header = bearer_authorization_header changeset.user
1683 node = create(:node)
1685 create(:way_node, :way => way, :node => node)
1686 alone_node = create(:node)
1690 <osmChange version='0.6'>
1691 <delete version="0.6">
1692 <node id="#{node.id}" version="#{node.version}" changeset="#{changeset.id}"/>
1694 <delete version="0.6" if-unused="true">
1695 <node id="#{alone_node.id}" version="#{alone_node.version}" changeset="#{changeset.id}"/>
1701 post changeset_upload_path(changeset), :params => diff.to_s, :headers => auth_header
1702 assert_response :precondition_failed,
1703 "shouldn't be able to upload a invalid deletion diff: #{@response.body}"
1704 assert_equal "Precondition failed: Node #{node.id} is still used by ways #{way.id}.", @response.body
1708 # test initial rate limit
1709 def test_upload_initial_rate_limit
1711 user = create(:user)
1713 # create some objects to use
1714 node = create(:node)
1715 way = create(:way_with_nodes, :nodes_count => 2)
1716 relation = create(:relation)
1718 # create a changeset that puts us near the initial rate limit
1719 changeset = create(:changeset, :user => user,
1720 :created_at => Time.now.utc - 5.minutes,
1721 :num_changes => Settings.initial_changes_per_hour - 2)
1723 # create authentication header
1724 auth_header = bearer_authorization_header user
1726 # simple diff to create a node way and relation using placeholders
1730 <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
1731 <tag k='foo' v='bar'/>
1732 <tag k='baz' v='bat'/>
1734 <way id='-1' changeset='#{changeset.id}'>
1735 <nd ref='#{node.id}'/>
1739 <relation id='-1' changeset='#{changeset.id}'>
1740 <member type='way' role='some' ref='#{way.id}'/>
1741 <member type='node' role='some' ref='#{node.id}'/>
1742 <member type='relation' role='some' ref='#{relation.id}'/>
1749 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1750 assert_response :too_many_requests, "upload did not hit rate limit"
1754 # test maximum rate limit
1755 def test_upload_maximum_rate_limit
1757 user = create(:user)
1759 # create some objects to use
1760 node = create(:node)
1761 way = create(:way_with_nodes, :nodes_count => 2)
1762 relation = create(:relation)
1764 # create a changeset to establish our initial edit time
1765 changeset = create(:changeset, :user => user,
1766 :created_at => Time.now.utc - 28.days)
1768 # create changeset to put us near the maximum rate limit
1769 total_changes = Settings.max_changes_per_hour - 2
1770 while total_changes.positive?
1771 changes = [total_changes, Changeset::MAX_ELEMENTS].min
1772 changeset = create(:changeset, :user => user,
1773 :created_at => Time.now.utc - 5.minutes,
1774 :num_changes => changes)
1775 total_changes -= changes
1778 # create authentication header
1779 auth_header = bearer_authorization_header user
1781 # simple diff to create a node way and relation using placeholders
1785 <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
1786 <tag k='foo' v='bar'/>
1787 <tag k='baz' v='bat'/>
1789 <way id='-1' changeset='#{changeset.id}'>
1790 <nd ref='#{node.id}'/>
1794 <relation id='-1' changeset='#{changeset.id}'>
1795 <member type='way' role='some' ref='#{way.id}'/>
1796 <member type='node' role='some' ref='#{node.id}'/>
1797 <member type='relation' role='some' ref='#{relation.id}'/>
1804 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1805 assert_response :too_many_requests, "upload did not hit rate limit"
1809 # test initial size limit
1810 def test_upload_initial_size_limit
1812 user = create(:user)
1814 # create a changeset that puts us near the initial size limit
1815 changeset = create(:changeset, :user => user,
1816 :min_lat => (-0.5 * GeoRecord::SCALE).round, :min_lon => (0.5 * GeoRecord::SCALE).round,
1817 :max_lat => (0.5 * GeoRecord::SCALE).round, :max_lon => (2.5 * GeoRecord::SCALE).round)
1819 # create authentication header
1820 auth_header = bearer_authorization_header user
1822 # simple diff to create a node
1826 <node id='-1' lon='0.9' lat='2.9' changeset='#{changeset.id}'>
1827 <tag k='foo' v='bar'/>
1828 <tag k='baz' v='bat'/>
1835 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1836 assert_response :payload_too_large, "upload did not hit size limit"
1840 # test size limit after one week
1841 def test_upload_week_size_limit
1843 user = create(:user)
1845 # create a changeset to establish our initial edit time
1846 create(:changeset, :user => user, :created_at => Time.now.utc - 7.days)
1848 # create a changeset that puts us near the initial size limit
1849 changeset = create(:changeset, :user => user,
1850 :min_lat => (-0.5 * GeoRecord::SCALE).round, :min_lon => (0.5 * GeoRecord::SCALE).round,
1851 :max_lat => (0.5 * GeoRecord::SCALE).round, :max_lon => (2.5 * GeoRecord::SCALE).round)
1853 # create authentication header
1854 auth_header = bearer_authorization_header user
1856 # simple diff to create a node way and relation using placeholders
1860 <node id='-1' lon='35' lat='35' changeset='#{changeset.id}'>
1861 <tag k='foo' v='bar'/>
1862 <tag k='baz' v='bat'/>
1869 post changeset_upload_path(changeset), :params => diff, :headers => auth_header
1870 assert_response :payload_too_large, "upload did not hit size limit"
1874 # when we make some simple changes we get the same changes back from the
1876 def test_diff_download_simple
1877 node = create(:node)
1879 ## First try with a non-public user, which should get a forbidden
1880 auth_header = bearer_authorization_header create(:user, :data_public => false)
1882 # create a temporary changeset
1883 xml = "<osm><changeset>" \
1884 "<tag k='created_by' v='osm test suite checking changesets'/>" \
1885 "</changeset></osm>"
1886 put changeset_create_path, :params => xml, :headers => auth_header
1887 assert_response :forbidden
1889 ## Now try with a normal user
1890 auth_header = bearer_authorization_header
1892 # create a temporary changeset
1893 xml = "<osm><changeset>" \
1894 "<tag k='created_by' v='osm test suite checking changesets'/>" \
1895 "</changeset></osm>"
1896 put changeset_create_path, :params => xml, :headers => auth_header
1897 assert_response :success
1898 changeset_id = @response.body.to_i
1904 <node id='#{node.id}' lon='0.0' lat='0.0' changeset='#{changeset_id}' version='1'/>
1905 <node id='#{node.id}' lon='0.1' lat='0.0' changeset='#{changeset_id}' version='2'/>
1906 <node id='#{node.id}' lon='0.1' lat='0.1' changeset='#{changeset_id}' version='3'/>
1907 <node id='#{node.id}' lon='0.1' lat='0.2' changeset='#{changeset_id}' version='4'/>
1908 <node id='#{node.id}' lon='0.2' lat='0.2' changeset='#{changeset_id}' version='5'/>
1909 <node id='#{node.id}' lon='0.3' lat='0.2' changeset='#{changeset_id}' version='6'/>
1910 <node id='#{node.id}' lon='0.3' lat='0.3' changeset='#{changeset_id}' version='7'/>
1911 <node id='#{node.id}' lon='0.9' lat='0.9' changeset='#{changeset_id}' version='8'/>
1917 post changeset_upload_path(changeset_id), :params => diff, :headers => auth_header
1918 assert_response :success,
1919 "can't upload multiple versions of an element in a diff: #{@response.body}"
1921 get changeset_download_path(changeset_id)
1922 assert_response :success
1924 assert_select "osmChange", 1
1925 assert_select "osmChange>modify", 8
1926 assert_select "osmChange>modify>node", 8
1930 # culled this from josm to ensure that nothing in the way that josm
1931 # is formatting the request is causing it to fail.
1933 # NOTE: the error turned out to be something else completely!
1934 def test_josm_upload
1935 auth_header = bearer_authorization_header
1937 # create a temporary changeset
1938 xml = "<osm><changeset>" \
1939 "<tag k='created_by' v='osm test suite checking changesets'/>" \
1940 "</changeset></osm>"
1941 put changeset_create_path, :params => xml, :headers => auth_header
1942 assert_response :success
1943 changeset_id = @response.body.to_i
1946 <osmChange version="0.6" generator="JOSM">
1947 <create version="0.6" generator="JOSM">
1948 <node id='-1' visible='true' changeset='#{changeset_id}' lat='51.49619982187321' lon='-0.18722061869438314' />
1949 <node id='-2' visible='true' changeset='#{changeset_id}' lat='51.496359883909605' lon='-0.18653093576241928' />
1950 <node id='-3' visible='true' changeset='#{changeset_id}' lat='51.49598132358285' lon='-0.18719613290981638' />
1951 <node id='-4' visible='true' changeset='#{changeset_id}' lat='51.4961591711078' lon='-0.18629015888084607' />
1952 <node id='-5' visible='true' changeset='#{changeset_id}' lat='51.49582126021711' lon='-0.18708186591517145' />
1953 <node id='-6' visible='true' changeset='#{changeset_id}' lat='51.49591018437858' lon='-0.1861432441734455' />
1954 <node id='-7' visible='true' changeset='#{changeset_id}' lat='51.49560784152179' lon='-0.18694719410005425' />
1955 <node id='-8' visible='true' changeset='#{changeset_id}' lat='51.49567389979617' lon='-0.1860289771788006' />
1956 <node id='-9' visible='true' changeset='#{changeset_id}' lat='51.49543761398892' lon='-0.186820684213126' />
1957 <way id='-10' action='modify' visible='true' changeset='#{changeset_id}'>
1967 <tag k='highway' v='residential' />
1968 <tag k='name' v='Foobar Street' />
1975 post changeset_upload_path(changeset_id), :params => diff, :headers => auth_header
1976 assert_response :success,
1977 "can't upload a diff from JOSM: #{@response.body}"
1979 get changeset_download_path(changeset_id)
1980 assert_response :success
1982 assert_select "osmChange", 1
1983 assert_select "osmChange>create>node", 9
1984 assert_select "osmChange>create>way", 1
1985 assert_select "osmChange>create>way>nd", 9
1986 assert_select "osmChange>create>way>tag", 2
1990 # when we make some complex changes we get the same changes back from the
1992 def test_diff_download_complex
1993 node = create(:node)
1994 node2 = create(:node)
1996 auth_header = bearer_authorization_header
1998 # create a temporary changeset
1999 xml = "<osm><changeset>" \
2000 "<tag k='created_by' v='osm test suite checking changesets'/>" \
2001 "</changeset></osm>"
2002 put changeset_create_path, :params => xml, :headers => auth_header
2003 assert_response :success
2004 changeset_id = @response.body.to_i
2010 <node id='#{node.id}' lon='0.0' lat='0.0' changeset='#{changeset_id}' version='1'/>
2013 <node id='-1' lon='0.9' lat='0.9' changeset='#{changeset_id}' version='0'/>
2014 <node id='-2' lon='0.8' lat='0.9' changeset='#{changeset_id}' version='0'/>
2015 <node id='-3' lon='0.7' lat='0.9' changeset='#{changeset_id}' version='0'/>
2018 <node id='#{node2.id}' lon='2.0' lat='1.5' changeset='#{changeset_id}' version='1'/>
2019 <way id='#{way.id}' changeset='#{changeset_id}' version='1'>
2020 <nd ref='#{node2.id}'/>
2030 post changeset_upload_path(changeset_id), :params => diff, :headers => auth_header
2031 assert_response :success,
2032 "can't upload multiple versions of an element in a diff: #{@response.body}"
2034 get changeset_download_path(changeset_id)
2035 assert_response :success
2037 assert_select "osmChange", 1
2038 assert_select "osmChange>create", 3
2039 assert_select "osmChange>delete", 1
2040 assert_select "osmChange>modify", 2
2041 assert_select "osmChange>create>node", 3
2042 assert_select "osmChange>delete>node", 1
2043 assert_select "osmChange>modify>node", 1
2044 assert_select "osmChange>modify>way", 1
2047 def test_changeset_download
2048 changeset = create(:changeset)
2049 node = create(:node, :with_history, :version => 1, :changeset => changeset)
2050 tag = create(:old_node_tag, :old_node => node.old_nodes.find_by(:version => 1))
2051 node2 = create(:node, :with_history, :version => 1, :changeset => changeset)
2052 _node3 = create(:node, :with_history, :deleted, :version => 1, :changeset => changeset)
2053 _relation = create(:relation, :with_history, :version => 1, :changeset => changeset)
2054 _relation2 = create(:relation, :with_history, :deleted, :version => 1, :changeset => changeset)
2056 get changeset_download_path(changeset)
2058 assert_response :success
2060 # FIXME: needs more assert_select tests
2061 assert_select "osmChange[version='#{Settings.api_version}'][generator='#{Settings.generator}']" do
2062 assert_select "create", :count => 5
2063 assert_select "create>node[id='#{node.id}'][visible='#{node.visible?}'][version='#{node.version}']" do
2064 assert_select "tag[k='#{tag.k}'][v='#{tag.v}']"
2066 assert_select "create>node[id='#{node2.id}']"
2070 test "sorts downloaded elements by timestamp" do
2071 changeset = create(:changeset)
2072 node1 = create(:old_node, :version => 2, :timestamp => "2020-02-01", :changeset => changeset)
2073 node0 = create(:old_node, :version => 2, :timestamp => "2020-01-01", :changeset => changeset)
2075 get changeset_download_path(changeset)
2076 assert_response :success
2077 assert_dom "modify", :count => 2 do |modify|
2078 assert_dom modify[0], ">node", :count => 1 do |node|
2079 assert_dom node, ">@id", node0.node_id.to_s
2081 assert_dom modify[1], ">node", :count => 1 do |node|
2082 assert_dom node, ">@id", node1.node_id.to_s
2087 test "sorts downloaded elements by version" do
2088 changeset = create(:changeset)
2089 node1 = create(:old_node, :version => 3, :timestamp => "2020-01-01", :changeset => changeset)
2090 node0 = create(:old_node, :version => 2, :timestamp => "2020-01-01", :changeset => changeset)
2092 get changeset_download_path(changeset)
2093 assert_response :success
2094 assert_dom "modify", :count => 2 do |modify|
2095 assert_dom modify[0], ">node", :count => 1 do |node|
2096 assert_dom node, ">@id", node0.node_id.to_s
2098 assert_dom modify[1], ">node", :count => 1 do |node|
2099 assert_dom node, ">@id", node1.node_id.to_s
2105 # check that the bounding box of a changeset gets updated correctly
2106 # FIXME: This should really be moded to a integration test due to the with_controller
2107 def test_changeset_bbox
2109 create(:way_node, :way => way, :node => create(:node, :lat => 0.3, :lon => 0.3))
2111 auth_header = bearer_authorization_header
2113 # create a new changeset
2114 xml = "<osm><changeset/></osm>"
2115 put changeset_create_path, :params => xml, :headers => auth_header
2116 assert_response :success, "Creating of changeset failed."
2117 changeset_id = @response.body.to_i
2119 # add a single node to it
2120 with_controller(NodesController.new) do
2121 xml = "<osm><node lon='0.1' lat='0.2' changeset='#{changeset_id}'/></osm>"
2122 post api_nodes_path, :params => xml, :headers => auth_header
2123 assert_response :success, "Couldn't create node."
2126 # get the bounding box back from the changeset
2127 get changeset_show_path(changeset_id)
2128 assert_response :success, "Couldn't read back changeset."
2129 assert_select "osm>changeset[min_lon='0.1000000']", 1
2130 assert_select "osm>changeset[max_lon='0.1000000']", 1
2131 assert_select "osm>changeset[min_lat='0.2000000']", 1
2132 assert_select "osm>changeset[max_lat='0.2000000']", 1
2134 # add another node to it
2135 with_controller(NodesController.new) do
2136 xml = "<osm><node lon='0.2' lat='0.1' changeset='#{changeset_id}'/></osm>"
2137 post api_nodes_path, :params => xml, :headers => auth_header
2138 assert_response :success, "Couldn't create second node."
2141 # get the bounding box back from the changeset
2142 get changeset_show_path(changeset_id)
2143 assert_response :success, "Couldn't read back changeset for the second time."
2144 assert_select "osm>changeset[min_lon='0.1000000']", 1
2145 assert_select "osm>changeset[max_lon='0.2000000']", 1
2146 assert_select "osm>changeset[min_lat='0.1000000']", 1
2147 assert_select "osm>changeset[max_lat='0.2000000']", 1
2149 # add (delete) a way to it, which contains a point at (3,3)
2150 with_controller(WaysController.new) do
2151 xml = update_changeset(xml_for_way(way), changeset_id)
2152 delete api_way_path(way), :params => xml.to_s, :headers => auth_header
2153 assert_response :success, "Couldn't delete a way."
2156 # get the bounding box back from the changeset
2157 get changeset_show_path(changeset_id)
2158 assert_response :success, "Couldn't read back changeset for the third time."
2159 assert_select "osm>changeset[min_lon='0.1000000']", 1
2160 assert_select "osm>changeset[max_lon='0.3000000']", 1
2161 assert_select "osm>changeset[min_lat='0.1000000']", 1
2162 assert_select "osm>changeset[max_lat='0.3000000']", 1
2166 # test the query functionality of changesets
2168 private_user = create(:user, :data_public => false)
2169 private_user_changeset = create(:changeset, :user => private_user)
2170 private_user_closed_changeset = create(:changeset, :closed, :user => private_user)
2171 user = create(:user)
2172 changeset = create(:changeset, :user => user)
2173 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))
2174 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)
2175 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)
2177 get changesets_path(:bbox => "-10,-10, 10, 10")
2178 assert_response :success, "can't get changesets in bbox"
2179 assert_changesets_in_order [changeset3, changeset2]
2181 get changesets_path(:bbox => "4.5,4.5,4.6,4.6")
2182 assert_response :success, "can't get changesets in bbox"
2183 assert_changesets_in_order [changeset3]
2185 # not found when looking for changesets of non-existing users
2186 get changesets_path(:user => User.maximum(:id) + 1)
2187 assert_response :not_found
2188 assert_equal "text/plain", @response.media_type
2189 get changesets_path(:display_name => " ")
2190 assert_response :not_found
2191 assert_equal "text/plain", @response.media_type
2193 # can't get changesets of user 1 without authenticating
2194 get changesets_path(:user => private_user.id)
2195 assert_response :not_found, "shouldn't be able to get changesets by non-public user (ID)"
2196 get changesets_path(:display_name => private_user.display_name)
2197 assert_response :not_found, "shouldn't be able to get changesets by non-public user (name)"
2199 # but this should work
2200 auth_header = bearer_authorization_header private_user
2201 get changesets_path(:user => private_user.id), :headers => auth_header
2202 assert_response :success, "can't get changesets by user ID"
2203 assert_changesets_in_order [private_user_changeset, private_user_closed_changeset]
2205 get changesets_path(:display_name => private_user.display_name), :headers => auth_header
2206 assert_response :success, "can't get changesets by user name"
2207 assert_changesets_in_order [private_user_changeset, private_user_closed_changeset]
2209 # test json endpoint
2210 get changesets_path(:display_name => private_user.display_name), :headers => auth_header, :params => { :format => "json" }
2211 assert_response :success, "can't get changesets by user name"
2213 js = ActiveSupport::JSON.decode(@response.body)
2216 assert_equal Settings.api_version, js["version"]
2217 assert_equal Settings.generator, js["generator"]
2218 assert_equal 2, js["changesets"].count
2220 # check that the correct error is given when we provide both UID and name
2221 get changesets_path(:user => private_user.id,
2222 :display_name => private_user.display_name), :headers => auth_header
2223 assert_response :bad_request, "should be a bad request to have both ID and name specified"
2225 get changesets_path(:user => private_user.id, :open => true), :headers => auth_header
2226 assert_response :success, "can't get changesets by user and open"
2227 assert_changesets_in_order [private_user_changeset]
2229 get changesets_path(:time => "2007-12-31"), :headers => auth_header
2230 assert_response :success, "can't get changesets by time-since"
2231 assert_changesets_in_order [changeset3, changeset2, changeset, private_user_changeset, private_user_closed_changeset, closed_changeset]
2233 get changesets_path(:time => "2008-01-01T12:34Z"), :headers => auth_header
2234 assert_response :success, "can't get changesets by time-since with hour"
2235 assert_changesets_in_order [changeset3, changeset2, changeset, private_user_changeset, private_user_closed_changeset, closed_changeset]
2237 get changesets_path(:time => "2007-12-31T23:59Z,2008-01-02T00:01Z"), :headers => auth_header
2238 assert_response :success, "can't get changesets by time-range"
2239 assert_changesets_in_order [closed_changeset]
2241 get changesets_path(:open => "true"), :headers => auth_header
2242 assert_response :success, "can't get changesets by open-ness"
2243 assert_changesets_in_order [changeset3, changeset2, changeset, private_user_changeset]
2245 get changesets_path(:closed => "true"), :headers => auth_header
2246 assert_response :success, "can't get changesets by closed-ness"
2247 assert_changesets_in_order [private_user_closed_changeset, closed_changeset]
2249 get changesets_path(:closed => "true", :user => private_user.id), :headers => auth_header
2250 assert_response :success, "can't get changesets by closed-ness and user"
2251 assert_changesets_in_order [private_user_closed_changeset]
2253 get changesets_path(:closed => "true", :user => user.id), :headers => auth_header
2254 assert_response :success, "can't get changesets by closed-ness and user"
2255 assert_changesets_in_order [closed_changeset]
2257 get changesets_path(:changesets => "#{private_user_changeset.id},#{changeset.id},#{closed_changeset.id}"), :headers => auth_header
2258 assert_response :success, "can't get changesets by id (as comma-separated string)"
2259 assert_changesets_in_order [changeset, private_user_changeset, closed_changeset]
2261 get changesets_path(:changesets => ""), :headers => auth_header
2262 assert_response :bad_request, "should be a bad request since changesets is empty"
2266 # test the query functionality of changesets with the limit parameter
2267 def test_query_limit
2268 user = create(:user)
2269 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))
2270 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))
2271 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))
2272 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))
2273 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))
2276 assert_response :success
2277 assert_changesets_in_order [changeset5, changeset4, changeset3, changeset2, changeset1]
2279 get changesets_path(:limit => "3")
2280 assert_response :success
2281 assert_changesets_in_order [changeset5, changeset4, changeset3]
2283 get changesets_path(:limit => "0")
2284 assert_response :bad_request
2286 get changesets_path(:limit => Settings.max_changeset_query_limit)
2287 assert_response :success
2288 assert_changesets_in_order [changeset5, changeset4, changeset3, changeset2, changeset1]
2290 get changesets_path(:limit => Settings.max_changeset_query_limit + 1)
2291 assert_response :bad_request
2295 # test the query functionality of sequential changesets with order and time parameters
2296 def test_query_order
2297 user = create(:user)
2298 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))
2299 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))
2300 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))
2301 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))
2302 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))
2303 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))
2306 assert_response :success
2307 assert_changesets_in_order [changeset6, changeset5, changeset4, changeset3, changeset2, changeset1]
2309 get changesets_path(:order => "oldest")
2310 assert_response :success
2311 assert_changesets_in_order [changeset1, changeset2, changeset3, changeset4, changeset5, changeset6]
2314 # lower time bound at the opening time of a changeset
2315 ["2008-02-01T00:00:00Z", "2008-05-15T00:00:00Z", [changeset5, changeset4, changeset3, changeset2], [changeset5, changeset4, changeset3, changeset2]],
2316 # lower time bound in the middle of a changeset
2317 ["2008-02-01T12:00:00Z", "2008-05-15T00:00:00Z", [changeset5, changeset4, changeset3, changeset2], [changeset5, changeset4, changeset3]],
2318 # lower time bound at the closing time of a changeset
2319 ["2008-02-02T00:00:00Z", "2008-05-15T00:00:00Z", [changeset5, changeset4, changeset3, changeset2], [changeset5, changeset4, changeset3]],
2320 # lower time bound after the closing time of a changeset
2321 ["2008-02-02T00:00:01Z", "2008-05-15T00:00:00Z", [changeset5, changeset4, changeset3], [changeset5, changeset4, changeset3]],
2322 # upper time bound in the middle of a changeset
2323 ["2007-09-09T12:00:00Z", "2008-04-01T12:00:00Z", [changeset4, changeset3, changeset2, changeset1], [changeset4, changeset3, changeset2, changeset1]],
2325 ["2009-02-02T00:00:01Z", "2018-05-15T00:00:00Z", [], []]
2326 ].each do |from, to, interval_changesets, point_changesets|
2327 get changesets_path(:time => "#{from},#{to}")
2328 assert_response :success
2329 assert_changesets_in_order interval_changesets
2331 get changesets_path(:from => from, :to => to)
2332 assert_response :success
2333 assert_changesets_in_order point_changesets
2335 get changesets_path(:from => from, :to => to, :order => "oldest")
2336 assert_response :success
2337 assert_changesets_in_order point_changesets.reverse
2342 # test the query functionality of overlapping changesets with order and time parameters
2343 def test_query_order_overlapping
2344 user = create(:user)
2345 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))
2346 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))
2347 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))
2348 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))
2349 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))
2351 get changesets_path(:time => "2015-06-04T00:00:00Z")
2352 assert_response :success
2353 assert_changesets_in_order [changeset1, changeset2, changeset3, changeset4]
2355 get changesets_path(:from => "2015-06-04T00:00:00Z")
2356 assert_response :success
2357 assert_changesets_in_order [changeset1, changeset2, changeset3]
2359 get changesets_path(:from => "2015-06-04T00:00:00Z", :order => "oldest")
2360 assert_response :success
2361 assert_changesets_in_order [changeset3, changeset2, changeset1]
2363 get changesets_path(:time => "2015-06-04T16:00:00Z,2015-06-04T17:30:00Z")
2364 assert_response :success
2365 assert_changesets_in_order [changeset1, changeset2, changeset3, changeset4]
2367 get changesets_path(:from => "2015-06-04T16:00:00Z", :to => "2015-06-04T17:30:00Z")
2368 assert_response :success
2369 assert_changesets_in_order [changeset1, changeset2]
2371 get changesets_path(:from => "2015-06-04T16:00:00Z", :to => "2015-06-04T17:30:00Z", :order => "oldest")
2372 assert_response :success
2373 assert_changesets_in_order [changeset2, changeset1]
2377 # check that errors are returned if garbage is inserted
2378 # into query strings
2379 def test_query_invalid
2382 ";drop table users;"].each do |bbox|
2383 get changesets_path(:bbox => bbox)
2384 assert_response :bad_request, "'#{bbox}' isn't a bbox"
2389 ";drop table users;",
2391 "-,-"].each do |time|
2392 get changesets_path(:time => time)
2393 assert_response :bad_request, "'#{time}' isn't a valid time range"
2400 get changesets_path(:user => uid)
2401 assert_response :bad_request, "'#{uid}' isn't a valid user ID"
2404 get changesets_path(:order => "oldest", :time => "2008-01-01T00:00Z,2018-01-01T00:00Z")
2405 assert_response :bad_request, "cannot use order=oldest with time"
2409 # check updating tags on a changeset
2410 def test_changeset_update
2411 private_user = create(:user, :data_public => false)
2412 private_changeset = create(:changeset, :user => private_user)
2413 user = create(:user)
2414 changeset = create(:changeset, :user => user)
2416 ## First try with a non-public user
2417 new_changeset = create_changeset_xml(:user => private_user)
2418 new_tag = XML::Node.new "tag"
2419 new_tag["k"] = "tagtesting"
2420 new_tag["v"] = "valuetesting"
2421 new_changeset.find("//osm/changeset").first << new_tag
2423 # try without any authorization
2424 put changeset_show_path(private_changeset), :params => new_changeset.to_s
2425 assert_response :unauthorized
2427 # try with the wrong authorization
2428 auth_header = bearer_authorization_header
2429 put changeset_show_path(private_changeset), :params => new_changeset.to_s, :headers => auth_header
2430 assert_response :conflict
2432 # now this should get an unauthorized
2433 auth_header = bearer_authorization_header private_user
2434 put changeset_show_path(private_changeset), :params => new_changeset.to_s, :headers => auth_header
2435 assert_require_public_data "user with their data non-public, shouldn't be able to edit their changeset"
2437 ## Now try with the public user
2438 new_changeset = create_changeset_xml(:id => 1)
2439 new_tag = XML::Node.new "tag"
2440 new_tag["k"] = "tagtesting"
2441 new_tag["v"] = "valuetesting"
2442 new_changeset.find("//osm/changeset").first << new_tag
2444 # try without any authorization
2445 put changeset_show_path(changeset), :params => new_changeset.to_s
2446 assert_response :unauthorized
2448 # try with the wrong authorization
2449 auth_header = bearer_authorization_header
2450 put changeset_show_path(changeset), :params => new_changeset.to_s, :headers => auth_header
2451 assert_response :conflict
2453 # now this should work...
2454 auth_header = bearer_authorization_header user
2455 put changeset_show_path(changeset), :params => new_changeset.to_s, :headers => auth_header
2456 assert_response :success
2458 assert_select "osm>changeset[id='#{changeset.id}']", 1
2459 assert_select "osm>changeset>tag", 1
2460 assert_select "osm>changeset>tag[k='tagtesting'][v='valuetesting']", 1
2464 # check that a user different from the one who opened the changeset
2466 def test_changeset_update_invalid
2467 auth_header = bearer_authorization_header
2469 changeset = create(:changeset)
2470 new_changeset = create_changeset_xml(:user => changeset.user, :id => changeset.id)
2471 new_tag = XML::Node.new "tag"
2472 new_tag["k"] = "testing"
2473 new_tag["v"] = "testing"
2474 new_changeset.find("//osm/changeset").first << new_tag
2476 put changeset_show_path(changeset), :params => new_changeset.to_s, :headers => auth_header
2477 assert_response :conflict
2481 # check that a changeset can contain a certain max number of changes.
2482 ## FIXME should be changed to an integration test due to the with_controller
2483 def test_changeset_limits
2484 user = create(:user)
2485 auth_header = bearer_authorization_header user
2487 # create an old changeset to ensure we have the maximum rate limit
2488 create(:changeset, :user => user, :created_at => Time.now.utc - 28.days)
2490 # open a new changeset
2491 xml = "<osm><changeset/></osm>"
2492 put changeset_create_path, :params => xml, :headers => auth_header
2493 assert_response :success, "can't create a new changeset"
2494 cs_id = @response.body.to_i
2496 # start the counter just short of where the changeset should finish.
2498 # alter the database to set the counter on the changeset directly,
2499 # otherwise it takes about 6 minutes to fill all of them.
2500 changeset = Changeset.find(cs_id)
2501 changeset.num_changes = Changeset::MAX_ELEMENTS - offset
2504 with_controller(NodesController.new) do
2506 xml = "<osm><node changeset='#{cs_id}' lat='0.0' lon='0.0'/></osm>"
2507 post api_nodes_path, :params => xml, :headers => auth_header
2508 assert_response :success, "can't create a new node"
2509 node_id = @response.body.to_i
2511 get api_node_path(node_id)
2512 assert_response :success, "can't read back new node"
2513 node_doc = XML::Parser.string(@response.body).parse
2514 node_xml = node_doc.find("//osm/node").first
2516 # loop until we fill the changeset with nodes
2518 node_xml["lat"] = rand.to_s
2519 node_xml["lon"] = rand.to_s
2520 node_xml["version"] = (i + 1).to_s
2522 put api_node_path(node_id), :params => node_doc.to_s, :headers => auth_header
2523 assert_response :success, "attempt #{i} should have succeeded"
2526 # trying again should fail
2527 node_xml["lat"] = rand.to_s
2528 node_xml["lon"] = rand.to_s
2529 node_xml["version"] = offset.to_s
2531 put api_node_path(node_id), :params => node_doc.to_s, :headers => auth_header
2532 assert_response :conflict, "final attempt should have failed"
2535 changeset = Changeset.find(cs_id)
2536 assert_equal Changeset::MAX_ELEMENTS + 1, changeset.num_changes
2538 # check that the changeset is now closed as well
2539 assert_not(changeset.open?,
2540 "changeset should have been auto-closed by exceeding " \
2545 # check that the changeset download for a changeset with a redacted
2546 # element in it doesn't contain that element.
2547 def test_diff_download_redacted
2548 changeset = create(:changeset)
2549 node = create(:node, :with_history, :version => 2, :changeset => changeset)
2550 node_v1 = node.old_nodes.find_by(:version => 1)
2551 node_v1.redact!(create(:redaction))
2553 get changeset_download_path(changeset)
2554 assert_response :success
2556 assert_select "osmChange", 1
2557 # this changeset contains the node in versions 1 & 2, but 1 should
2559 assert_select "osmChange node[id='#{node.id}']", 1
2560 assert_select "osmChange node[id='#{node.id}'][version='1']", 0
2564 # test subscribe success
2565 def test_subscribe_success
2566 auth_header = bearer_authorization_header
2567 changeset = create(:changeset, :closed)
2569 assert_difference "changeset.subscribers.count", 1 do
2570 post api_changeset_subscribe_path(changeset), :headers => auth_header
2572 assert_response :success
2574 # not closed changeset
2575 changeset = create(:changeset)
2576 assert_difference "changeset.subscribers.count", 1 do
2577 post api_changeset_subscribe_path(changeset), :headers => auth_header
2579 assert_response :success
2583 # test subscribe fail
2584 def test_subscribe_fail
2585 user = create(:user)
2588 changeset = create(:changeset, :closed)
2589 assert_no_difference "changeset.subscribers.count" do
2590 post api_changeset_subscribe_path(changeset)
2592 assert_response :unauthorized
2594 auth_header = bearer_authorization_header user
2597 assert_no_difference "changeset.subscribers.count" do
2598 post api_changeset_subscribe_path(999111), :headers => auth_header
2600 assert_response :not_found
2602 # trying to subscribe when already subscribed
2603 changeset = create(:changeset, :closed)
2604 changeset.subscribers.push(user)
2605 assert_no_difference "changeset.subscribers.count" do
2606 post api_changeset_subscribe_path(changeset), :headers => auth_header
2608 assert_response :conflict
2612 # test unsubscribe success
2613 def test_unsubscribe_success
2614 user = create(:user)
2615 auth_header = bearer_authorization_header user
2616 changeset = create(:changeset, :closed)
2617 changeset.subscribers.push(user)
2619 assert_difference "changeset.subscribers.count", -1 do
2620 post api_changeset_unsubscribe_path(changeset), :headers => auth_header
2622 assert_response :success
2624 # not closed changeset
2625 changeset = create(:changeset)
2626 changeset.subscribers.push(user)
2628 assert_difference "changeset.subscribers.count", -1 do
2629 post api_changeset_unsubscribe_path(changeset), :headers => auth_header
2631 assert_response :success
2635 # test unsubscribe fail
2636 def test_unsubscribe_fail
2638 changeset = create(:changeset, :closed)
2639 assert_no_difference "changeset.subscribers.count" do
2640 post api_changeset_unsubscribe_path(changeset)
2642 assert_response :unauthorized
2644 auth_header = bearer_authorization_header
2647 assert_no_difference "changeset.subscribers.count" do
2648 post api_changeset_unsubscribe_path(999111), :headers => auth_header
2650 assert_response :not_found
2652 # trying to unsubscribe when not subscribed
2653 changeset = create(:changeset, :closed)
2654 assert_no_difference "changeset.subscribers.count" do
2655 post api_changeset_unsubscribe_path(changeset), :headers => auth_header
2657 assert_response :not_found
2663 # check that the output consists of one specific changeset
2664 def assert_single_changeset(changeset, &)
2665 assert_dom "> changeset", 1 do
2666 assert_dom "> @id", changeset.id.to_s
2667 assert_dom "> @created_at", changeset.created_at.xmlschema
2669 assert_dom "> @open", "true"
2670 assert_dom "> @closed_at", 0
2672 assert_dom "> @open", "false"
2673 assert_dom "> @closed_at", changeset.closed_at.xmlschema
2675 assert_dom "> @comments_count", changeset.comments.length.to_s
2676 assert_dom "> @changes_count", changeset.num_changes.to_s
2677 yield if block_given?
2681 def assert_single_changeset_json(changeset, js)
2682 assert_equal changeset.id, js["changeset"]["id"]
2683 assert_equal changeset.created_at.xmlschema, js["changeset"]["created_at"]
2685 assert js["changeset"]["open"]
2686 assert_nil js["changeset"]["closed_at"]
2688 assert_not js["changeset"]["open"]
2689 assert_equal changeset.closed_at.xmlschema, js["changeset"]["closed_at"]
2691 assert_equal changeset.comments.length, js["changeset"]["comments_count"]
2692 assert_equal changeset.num_changes, js["changeset"]["changes_count"]
2696 # check that certain changesets exist in the output in the specified order
2697 def assert_changesets_in_order(changesets)
2698 assert_select "osm>changeset", changesets.size
2699 changesets.each_with_index do |changeset, index|
2700 assert_select "osm>changeset:nth-child(#{index + 1})[id='#{changeset.id}']", 1
2705 # update the changeset_id of a way element
2706 def update_changeset(xml, changeset_id)
2707 xml_attr_rewrite(xml, "changeset", changeset_id)
2711 # update an attribute in a way element
2712 def xml_attr_rewrite(xml, name, value)
2713 xml.find("//osm/way").first[name] = value.to_s
2718 # build XML for changesets
2719 def create_changeset_xml(user: nil, id: nil)
2720 root = XML::Document.new
2721 root.root = XML::Node.new "osm"
2722 cs = XML::Node.new "changeset"
2724 cs["user"] = user.display_name
2725 cs["uid"] = user.id.to_s
2727 cs["id"] = id.to_s if id