5     class UploadsControllerTest < ActionDispatch::IntegrationTest
 
   7       # test all routes which lead to this controller
 
  10           { :path => "/api/0.6/changeset/1/upload", :method => :post },
 
  11           { :controller => "api/changesets/uploads", :action => "create", :changeset_id => "1" }
 
  15       def test_upload_when_unauthorized
 
  16         changeset = create(:changeset)
 
  17         node = create(:node, :latitude => 0, :longitude => 0)
 
  22               <node id='#{node.id}' lon='1' lat='2' changeset='#{changeset.id}' version='1'/>
 
  27         post api_changeset_upload_path(changeset), :params => diff
 
  29         assert_response :unauthorized
 
  32         assert_equal 0, changeset.num_changes
 
  34         assert_equal 1, node.version
 
  35         assert_equal 0, node.latitude
 
  36         assert_equal 0, node.longitude
 
  39       def test_upload_by_private_user
 
  40         user = create(:user, :data_public => false)
 
  41         changeset = create(:changeset, :user => user)
 
  42         node = create(:node, :latitude => 0, :longitude => 0)
 
  47               <node id='#{node.id}' lon='1' lat='2' changeset='#{changeset.id}' version='1'/>
 
  52         auth_header = bearer_authorization_header user
 
  54         post api_changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
  56         assert_response :forbidden
 
  59         assert_equal 0, changeset.num_changes
 
  61         assert_equal 1, node.version
 
  62         assert_equal 0, node.latitude
 
  63         assert_equal 0, node.longitude
 
  66       def test_upload_without_required_scope
 
  68         changeset = create(:changeset, :user => user)
 
  69         node = create(:node, :latitude => 0, :longitude => 0)
 
  74               <node id='#{node.id}' lon='1' lat='2' changeset='#{changeset.id}' version='1'/>
 
  79         auth_header = bearer_authorization_header user, :scopes => %w[read_prefs]
 
  81         post api_changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
  83         assert_response :forbidden
 
  86         assert_equal 0, changeset.num_changes
 
  88         assert_equal 1, node.version
 
  89         assert_equal 0, node.latitude
 
  90         assert_equal 0, node.longitude
 
  93       def test_upload_with_required_scope
 
  95         changeset = create(:changeset, :user => user)
 
  96         node = create(:node, :latitude => 0, :longitude => 0)
 
 101               <node id='#{node.id}' lon='1' lat='2' changeset='#{changeset.id}' version='1'/>
 
 106         auth_header = bearer_authorization_header user, :scopes => %w[write_api]
 
 108         post api_changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
 110         assert_response :success
 
 112         assert_dom "diffResult[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1 do
 
 113           assert_dom "> node", 1 do
 
 114             assert_dom "> @old_id", node.id.to_s
 
 115             assert_dom "> @new_id", node.id.to_s
 
 116             assert_dom "> @new_version", "2"
 
 121         assert_equal 1, changeset.num_changes
 
 123         assert_equal 2, node.version
 
 124         assert_equal 2 * GeoRecord::SCALE, node.latitude
 
 125         assert_equal 1 * GeoRecord::SCALE, node.longitude
 
 129       # try to upload with commands other than create, modify, or delete
 
 130       def test_upload_unknown_action
 
 131         changeset = create(:changeset)
 
 136               <node id='1' lon='1' lat='1' changeset='#{changeset.id}' />
 
 141         auth_header = bearer_authorization_header changeset.user
 
 143         post api_changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
 145         assert_response :bad_request
 
 146         assert_equal "Unknown action ping, choices are create, modify, delete", @response.body
 
 150       # test for issues in https://github.com/openstreetmap/trac-tickets/issues/1568
 
 151       def test_upload_empty_changeset
 
 152         changeset = create(:changeset)
 
 154         auth_header = bearer_authorization_header changeset.user
 
 157          "<osmChange></osmChange>",
 
 158          "<osmChange><modify/></osmChange>",
 
 159          "<osmChange><modify></modify></osmChange>"].each do |diff|
 
 160           post api_changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
 162           assert_response :success
 
 167       # test that the X-Error-Format header works to request XML errors
 
 168       def test_upload_xml_errors
 
 169         changeset = create(:changeset)
 
 171         create(:relation_member, :member => node)
 
 173         # try and delete a node that is in use
 
 174         diff = XML::Document.new
 
 175         diff.root = XML::Node.new "osmChange"
 
 176         delete = XML::Node.new "delete"
 
 178         delete << xml_node_for_node(node)
 
 180         auth_header = bearer_authorization_header changeset.user
 
 181         error_header = error_format_header "xml"
 
 183         post api_changeset_upload_path(changeset), :params => diff.to_s, :headers => auth_header.merge(error_header)
 
 185         assert_response :success
 
 187         assert_dom "osmError[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
 
 188         assert_dom "osmError>status", 1
 
 189         assert_dom "osmError>message", 1
 
 192       # -------------------------------------
 
 193       # Test creating elements.
 
 194       # -------------------------------------
 
 196       def test_upload_create_elements
 
 198         changeset = create(:changeset, :user => user)
 
 200         way = create(:way_with_nodes, :nodes_count => 2)
 
 201         relation = create(:relation)
 
 206               <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
 
 207                 <tag k='foo' v='bar'/>
 
 208                 <tag k='baz' v='bat'/>
 
 210               <way id='-1' changeset='#{changeset.id}'>
 
 211                 <nd ref='#{node.id}'/>
 
 215               <relation id='-1' changeset='#{changeset.id}'>
 
 216                 <member type='way' role='some' ref='#{way.id}'/>
 
 217                 <member type='node' role='some' ref='#{node.id}'/>
 
 218                 <member type='relation' role='some' ref='#{relation.id}'/>
 
 224         auth_header = bearer_authorization_header user
 
 226         post api_changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
 228         assert_response :success
 
 230         new_node_id, new_way_id, new_rel_id = nil
 
 231         assert_dom "diffResult[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1 do
 
 232           # inspect the response to find out what the new element IDs are
 
 233           # check the old IDs are all present and negative one
 
 234           # check the versions are present and equal one
 
 235           assert_dom "> node", 1 do |(node_el)|
 
 236             new_node_id = node_el["new_id"].to_i
 
 237             assert_dom "> @old_id", "-1"
 
 238             assert_dom "> @new_version", "1"
 
 240           assert_dom "> way", 1 do |(way_el)|
 
 241             new_way_id = way_el["new_id"].to_i
 
 242             assert_dom "> @old_id", "-1"
 
 243             assert_dom "> @new_version", "1"
 
 245           assert_dom "> relation", 1 do |(rel_el)|
 
 246             new_rel_id = rel_el["new_id"].to_i
 
 247             assert_dom "> @old_id", "-1"
 
 248             assert_dom "> @new_version", "1"
 
 252         assert_equal 2, Node.find(new_node_id).tags.size, "new node should have two tags"
 
 253         assert_equal 0, Way.find(new_way_id).tags.size, "new way should have no tags"
 
 254         assert_equal 0, Relation.find(new_rel_id).tags.size, "new relation should have no tags"
 
 258       # upload an element with a really long tag value
 
 259       def test_upload_create_node_with_tag_too_long
 
 260         changeset = create(:changeset)
 
 265               <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
 
 266                 <tag k='foo' v='#{'x' * 256}'/>
 
 272         auth_header = bearer_authorization_header changeset.user
 
 274         assert_no_difference "Node.count" do
 
 275           post api_changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
 277           assert_response :bad_request
 
 281       def test_upload_create_nodes_with_invalid_placeholder_reuse_in_one_action_block
 
 282         changeset = create(:changeset)
 
 287               <node id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
 
 288               <node id='-1' lon='1' lat='1' changeset='#{changeset.id}' version='1'/>
 
 293         auth_header = bearer_authorization_header changeset.user
 
 295         assert_no_difference "Node.count" do
 
 296           post api_changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
 298           assert_response :bad_request
 
 302       def test_upload_create_nodes_with_invalid_placeholder_reuse_in_two_action_blocks
 
 303         changeset = create(:changeset)
 
 308               <node id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
 
 311               <node id='-1' lon='1' lat='1' changeset='#{changeset.id}' version='1'/>
 
 316         auth_header = bearer_authorization_header changeset.user
 
 318         assert_no_difference "Node.count" do
 
 319           post api_changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
 321           assert_response :bad_request
 
 325       def test_upload_create_way_referring_node_placeholder_defined_later
 
 326         changeset = create(:changeset)
 
 331               <way id="-1" changeset="#{changeset.id}">
 
 334               <node id="-1" lat="1" lon="2" changeset="#{changeset.id}"/>
 
 339         auth_header = bearer_authorization_header changeset.user
 
 341         assert_no_difference "Node.count" do
 
 342           assert_no_difference "Way.count" do
 
 343             post api_changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
 345             assert_response :bad_request
 
 348         assert_equal "Placeholder node not found for reference -1 in way -1", @response.body
 
 351       def test_upload_create_way_referring_undefined_node_placeholder
 
 352         changeset = create(:changeset)
 
 357               <way id="-1" changeset="#{changeset.id}">
 
 364         auth_header = bearer_authorization_header changeset.user
 
 366         assert_no_difference "Way.count" do
 
 367           post api_changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
 369           assert_response :bad_request
 
 371         assert_equal "Placeholder node not found for reference -1 in way -1", @response.body
 
 374       def test_upload_create_existing_way_referring_undefined_node_placeholder
 
 375         changeset = create(:changeset)
 
 381               <way id="#{way.id}" changeset="#{changeset.id}" version="1">
 
 388         auth_header = bearer_authorization_header changeset.user
 
 390         post api_changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
 392         assert_response :bad_request
 
 393         assert_equal "Placeholder node not found for reference -1 in way #{way.id}", @response.body
 
 396         assert_equal 1, way.version
 
 399       def test_upload_create_relation_referring_undefined_node_placeholder
 
 400         changeset = create(:changeset)
 
 405               <relation id="-1" changeset="#{changeset.id}" version="1">
 
 406                 <member type="node" role="foo" ref="-1"/>
 
 412         auth_header = bearer_authorization_header changeset.user
 
 414         assert_no_difference "Relation.count" do
 
 415           post api_changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
 417           assert_response :bad_request
 
 419         assert_equal "Placeholder Node not found for reference -1 in relation -1.", @response.body
 
 422       def test_upload_create_existing_relation_referring_undefined_way_placeholder
 
 423         changeset = create(:changeset)
 
 424         relation = create(:relation)
 
 429               <relation id="#{relation.id}" changeset="#{changeset.id}" version="1">
 
 430                 <member type="way" role="bar" ref="-1"/>
 
 436         auth_header = bearer_authorization_header changeset.user
 
 438         post api_changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
 440         assert_response :bad_request
 
 441         assert_equal "Placeholder Way not found for reference -1 in relation #{relation.id}.", @response.body
 
 444         assert_equal 1, relation.version
 
 447       def test_upload_create_relations_with_circular_references
 
 448         changeset = create(:changeset)
 
 451           <osmChange version='0.6'>
 
 453               <relation id='-2' version='0' changeset='#{changeset.id}'>
 
 454                 <member type='relation' role='' ref='-4' />
 
 455                 <tag k='type' v='route' />
 
 456                 <tag k='name' v='AtoB' />
 
 458               <relation id='-3' version='0' changeset='#{changeset.id}'>
 
 459                 <tag k='type' v='route' />
 
 460                 <tag k='name' v='BtoA' />
 
 462               <relation id='-4' version='0' changeset='#{changeset.id}'>
 
 463                 <member type='relation' role='' ref='-2' />
 
 464                 <member type='relation' role='' ref='-3' />
 
 465                 <tag k='type' v='route_master' />
 
 466                 <tag k='name' v='master' />
 
 472         auth_header = bearer_authorization_header changeset.user
 
 474         post api_changeset_upload_path(changeset), :params => diff.to_s, :headers => auth_header
 
 476         assert_response :bad_request
 
 477         assert_equal "Placeholder Relation not found for reference -4 in relation -2.", @response.body
 
 480       # -------------------------------------
 
 481       # Test modifying elements.
 
 482       # -------------------------------------
 
 484       def test_upload_modify_elements
 
 486         changeset = create(:changeset, :user => user)
 
 487         node = create(:node, :latitude => 0, :longitude => 0)
 
 489         relation = create(:relation)
 
 490         other_relation = create(:relation)
 
 492         # create some tags, since we test that they are removed later
 
 493         create(:node_tag, :node => node)
 
 494         create(:way_tag, :way => way)
 
 495         create(:relation_tag, :relation => relation)
 
 497         # simple diff to change a node, way and relation by removing their tags
 
 501               <node id='#{node.id}' lon='1' lat='2' changeset='#{changeset.id}' version='1'/>
 
 502               <way id='#{way.id}' changeset='#{changeset.id}' version='1'>
 
 503                 <nd ref='#{node.id}'/>
 
 507               <relation id='#{relation.id}' changeset='#{changeset.id}' version='1'>
 
 508                 <member type='way' role='some' ref='#{way.id}'/>
 
 509                 <member type='node' role='some' ref='#{node.id}'/>
 
 510                 <member type='relation' role='some' ref='#{other_relation.id}'/>
 
 516         auth_header = bearer_authorization_header user
 
 518         post api_changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
 520         assert_response :success
 
 522         assert_dom "diffResult[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1 do
 
 523           assert_dom "> node", 1 do
 
 524             assert_dom "> @old_id", node.id.to_s
 
 525             assert_dom "> @new_id", node.id.to_s
 
 526             assert_dom "> @new_version", "2"
 
 528           assert_dom "> way", 1 do
 
 529             assert_dom "> @old_id", way.id.to_s
 
 530             assert_dom "> @new_id", way.id.to_s
 
 531             assert_dom "> @new_version", "2"
 
 533           assert_dom "> relation", 1 do
 
 534             assert_dom "> @old_id", relation.id.to_s
 
 535             assert_dom "> @new_id", relation.id.to_s
 
 536             assert_dom "> @new_version", "2"
 
 541         assert_equal 3, changeset.num_changes
 
 543         assert_equal 2, node.version
 
 544         assert_equal 2 * GeoRecord::SCALE, node.latitude
 
 545         assert_equal 1 * GeoRecord::SCALE, node.longitude
 
 546         assert_equal 0, node.tags.size, "node #{node.id} should now have no tags"
 
 548         assert_equal 2, way.version
 
 549         assert_equal 0, way.tags.size, "way #{way.id} should now have no tags"
 
 550         assert_equal [node], way.nodes
 
 552         assert_equal 2, relation.version
 
 553         assert_equal 0, relation.tags.size, "relation #{relation.id} should now have no tags"
 
 554         assert_equal [["Way", way.id, "some"], ["Node", node.id, "some"], ["Relation", other_relation.id, "some"]], relation.members
 
 558       # upload multiple versions of the same element in the same diff.
 
 559       def test_upload_modify_multiple_node_versions
 
 561         changeset = create(:changeset)
 
 563         # change the location of a node multiple times, each time referencing
 
 564         # the last version. doesn't this depend on version numbers being
 
 569               <node id='#{node.id}' lon='0.0' lat='0.0' changeset='#{changeset.id}' version='1'/>
 
 570               <node id='#{node.id}' lon='0.1' lat='0.0' changeset='#{changeset.id}' version='2'/>
 
 571               <node id='#{node.id}' lon='0.1' lat='0.1' changeset='#{changeset.id}' version='3'/>
 
 572               <node id='#{node.id}' lon='0.1' lat='0.2' changeset='#{changeset.id}' version='4'/>
 
 573               <node id='#{node.id}' lon='0.2' lat='0.2' changeset='#{changeset.id}' version='5'/>
 
 574               <node id='#{node.id}' lon='0.3' lat='0.2' changeset='#{changeset.id}' version='6'/>
 
 575               <node id='#{node.id}' lon='0.3' lat='0.3' changeset='#{changeset.id}' version='7'/>
 
 576               <node id='#{node.id}' lon='0.9' lat='0.9' changeset='#{changeset.id}' version='8'/>
 
 581         auth_header = bearer_authorization_header changeset.user
 
 583         post api_changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
 585         assert_response :success
 
 587         assert_dom "diffResult>node", 8
 
 590         assert_equal 9, node.version
 
 591         assert_equal 0.9 * GeoRecord::SCALE, node.latitude
 
 592         assert_equal 0.9 * GeoRecord::SCALE, node.longitude
 
 596       # upload multiple versions of the same element in the same diff, but
 
 597       # keep the version numbers the same.
 
 598       def test_upload_modify_duplicate_node_versions
 
 599         node = create(:node, :latitude => 0, :longitude => 0)
 
 600         changeset = create(:changeset)
 
 605               <node id='#{node.id}' lon='1' lat='1' changeset='#{changeset.id}' version='1'/>
 
 606               <node id='#{node.id}' lon='2' lat='2' changeset='#{changeset.id}' version='1'/>
 
 611         auth_header = bearer_authorization_header changeset.user
 
 613         post api_changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
 615         assert_response :conflict
 
 618         assert_equal 1, node.version
 
 619         assert_equal 0, node.latitude
 
 620         assert_equal 0, node.longitude
 
 624       # try to upload some elements without specifying the version
 
 625       def test_upload_modify_missing_node_version
 
 626         node = create(:node, :latitude => 0, :longitude => 0)
 
 627         changeset = create(:changeset)
 
 632               <node id='#{node.id}' lon='1' lat='1' changeset='#{changeset.id}'/>
 
 637         auth_header = bearer_authorization_header changeset.user
 
 639         post api_changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
 641         assert_response :bad_request
 
 644         assert_equal 1, node.version
 
 645         assert_equal 0, node.latitude
 
 646         assert_equal 0, node.longitude
 
 650       # create a diff which references several changesets, which should cause
 
 651       # a rollback and none of the diff gets committed
 
 652       def test_upload_modify_with_references_to_different_changesets
 
 653         changeset1 = create(:changeset)
 
 654         changeset2 = create(:changeset, :user => changeset1.user)
 
 655         node1 = create(:node)
 
 656         node2 = create(:node)
 
 658         # simple diff to create a node way and relation using placeholders
 
 662               <node id='#{node1.id}' lon='0' lat='0' changeset='#{changeset1.id}' version='1'/>
 
 665               <node id='#{node2.id}' lon='0' lat='0' changeset='#{changeset2.id}' version='1'/>
 
 670         auth_header = bearer_authorization_header changeset1.user
 
 672         post api_changeset_upload_path(changeset1), :params => diff, :headers => auth_header
 
 674         assert_response :conflict
 
 676         assert_nodes_are_equal(node1, Node.find(node1.id))
 
 677         assert_nodes_are_equal(node2, Node.find(node2.id))
 
 681       # upload a valid changeset which has a mixture of whitespace
 
 682       # to check a bug https://github.com/openstreetmap/trac-tickets/issues/1565
 
 683       def test_upload_modify_with_mixed_whitespace
 
 684         changeset = create(:changeset)
 
 686         way = create(:way_with_nodes, :nodes_count => 2)
 
 687         relation = create(:relation)
 
 688         other_relation = create(:relation)
 
 689         create(:relation_tag, :relation => relation)
 
 693           <modify><node id='#{node.id}' lon='0' lat='0' changeset='#{changeset.id}'
 
 695             <node id='#{node.id}' lon='1' lat='1' changeset='#{changeset.id}' version='2'><tag k='k' v='v'/></node></modify>
 
 697           <relation id='#{relation.id}' changeset='#{changeset.id}' version='1'><member
 
 698             type='way' role='some' ref='#{way.id}'/><member
 
 699               type='node' role='some' ref='#{node.id}'/>
 
 700             <member type='relation' role='some' ref='#{other_relation.id}'/>
 
 702           </modify></osmChange>
 
 705         auth_header = bearer_authorization_header changeset.user
 
 707         post api_changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
 709         assert_response :success
 
 711         assert_dom "diffResult>node", 2
 
 712         assert_dom "diffResult>relation", 1
 
 714         assert_equal 1, Node.find(node.id).tags.size, "node #{node.id} should now have one tag"
 
 715         assert_equal 0, Relation.find(relation.id).tags.size, "relation #{relation.id} should now have no tags"
 
 718       def test_upload_modify_unknown_node_placeholder
 
 719         check_upload_results_in_not_found do |changeset|
 
 720           "<modify><node id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/></modify>"
 
 724       def test_upload_modify_unknown_way_placeholder
 
 725         check_upload_results_in_not_found do |changeset|
 
 726           "<modify><way id='-1' changeset='#{changeset.id}' version='1'/></modify>"
 
 730       def test_upload_modify_unknown_relation_placeholder
 
 731         check_upload_results_in_not_found do |changeset|
 
 732           "<modify><relation id='-1' changeset='#{changeset.id}' version='1'/></modify>"
 
 736       # -------------------------------------
 
 737       # Test deleting elements.
 
 738       # -------------------------------------
 
 741       # test a complex delete where we delete elements which rely on each other
 
 742       # in the same transaction.
 
 743       def test_upload_delete_elements
 
 744         changeset = create(:changeset)
 
 745         super_relation = create(:relation)
 
 746         used_relation = create(:relation)
 
 747         used_way = create(:way)
 
 748         used_node = create(:node)
 
 749         create(:relation_member, :relation => super_relation, :member => used_relation)
 
 750         create(:relation_member, :relation => super_relation, :member => used_way)
 
 751         create(:relation_member, :relation => super_relation, :member => used_node)
 
 753         diff = XML::Document.new
 
 754         diff.root = XML::Node.new "osmChange"
 
 755         delete = XML::Node.new "delete"
 
 757         delete << xml_node_for_relation(super_relation)
 
 758         delete << xml_node_for_relation(used_relation)
 
 759         delete << xml_node_for_way(used_way)
 
 760         delete << xml_node_for_node(used_node)
 
 761         %w[node way relation].each do |type|
 
 762           delete.find("//osmChange/delete/#{type}").each do |n|
 
 763             n["changeset"] = changeset.id.to_s
 
 767         auth_header = bearer_authorization_header changeset.user
 
 769         post api_changeset_upload_path(changeset), :params => diff.to_s, :headers => auth_header
 
 771         assert_response :success
 
 773         assert_dom "diffResult", 1 do
 
 774           assert_dom "> node", 1
 
 775           assert_dom "> way", 1
 
 776           assert_dom "> relation", 2
 
 779         assert_not Node.find(used_node.id).visible
 
 780         assert_not Way.find(used_way.id).visible
 
 781         assert_not Relation.find(super_relation.id).visible
 
 782         assert_not Relation.find(used_relation.id).visible
 
 786       # test uploading a delete with no lat/lon, as they are optional in the osmChange spec.
 
 787       def test_upload_delete_node_without_latlon
 
 789         changeset = create(:changeset)
 
 791         diff = "<osmChange><delete><node id='#{node.id}' version='#{node.version}' changeset='#{changeset.id}'/></delete></osmChange>"
 
 793         auth_header = bearer_authorization_header changeset.user
 
 795         post api_changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
 797         assert_response :success
 
 799         assert_dom "diffResult", 1 do
 
 800           assert_dom "> node", 1 do
 
 801             assert_dom "> @old_id", node.id.to_s
 
 802             assert_dom "> @new_id", 0
 
 803             assert_dom "> @new_version", 0
 
 808         assert_not node.visible
 
 812       # test that deleting stuff in a transaction doesn't bypass the checks
 
 813       # to ensure that used elements are not deleted.
 
 814       def test_upload_delete_referenced_elements
 
 815         changeset = create(:changeset)
 
 816         relation = create(:relation)
 
 817         other_relation = create(:relation)
 
 818         used_way = create(:way)
 
 819         used_node = create(:node)
 
 820         create(:relation_member, :relation => relation, :member => used_way)
 
 821         create(:relation_member, :relation => relation, :member => used_node)
 
 823         diff = XML::Document.new
 
 824         diff.root = XML::Node.new "osmChange"
 
 825         delete = XML::Node.new "delete"
 
 827         delete << xml_node_for_relation(other_relation)
 
 828         delete << xml_node_for_way(used_way)
 
 829         delete << xml_node_for_node(used_node)
 
 830         %w[node way relation].each do |type|
 
 831           delete.find("//osmChange/delete/#{type}").each do |n|
 
 832             n["changeset"] = changeset.id.to_s
 
 836         auth_header = bearer_authorization_header changeset.user
 
 838         post api_changeset_upload_path(changeset), :params => diff.to_s, :headers => auth_header
 
 840         assert_response :precondition_failed
 
 841         assert_equal "Precondition failed: Way #{used_way.id} is still used by relations #{relation.id}.", @response.body
 
 843         assert Node.find(used_node.id).visible
 
 844         assert Way.find(used_way.id).visible
 
 845         assert Relation.find(relation.id).visible
 
 846         assert Relation.find(other_relation.id).visible
 
 850       # test that a conditional delete of an in use object works.
 
 851       def test_upload_delete_if_unused
 
 852         changeset = create(:changeset)
 
 853         super_relation = create(:relation)
 
 854         used_relation = create(:relation)
 
 855         used_way = create(:way)
 
 856         used_node = create(:node)
 
 857         create(:relation_member, :relation => super_relation, :member => used_relation)
 
 858         create(:relation_member, :relation => super_relation, :member => used_way)
 
 859         create(:relation_member, :relation => super_relation, :member => used_node)
 
 861         diff = XML::Document.new
 
 862         diff.root = XML::Node.new "osmChange"
 
 863         delete = XML::Node.new "delete"
 
 865         delete["if-unused"] = ""
 
 866         delete << xml_node_for_relation(used_relation)
 
 867         delete << xml_node_for_way(used_way)
 
 868         delete << xml_node_for_node(used_node)
 
 869         %w[node way relation].each do |type|
 
 870           delete.find("//osmChange/delete/#{type}").each do |n|
 
 871             n["changeset"] = changeset.id.to_s
 
 875         auth_header = bearer_authorization_header changeset.user
 
 877         post api_changeset_upload_path(changeset), :params => diff.to_s, :headers => auth_header
 
 879         assert_response :success
 
 881         assert_dom "diffResult[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1 do
 
 882           assert_dom "> node", 1 do
 
 883             assert_dom "> @old_id", used_node.id.to_s
 
 884             assert_dom "> @new_id", used_node.id.to_s
 
 885             assert_dom "> @new_version", used_node.version.to_s
 
 887           assert_dom "> way", 1 do
 
 888             assert_dom "> @old_id", used_way.id.to_s
 
 889             assert_dom "> @new_id", used_way.id.to_s
 
 890             assert_dom "> @new_version", used_way.version.to_s
 
 892           assert_dom "> relation", 1 do
 
 893             assert_dom "> @old_id", used_relation.id.to_s
 
 894             assert_dom "> @new_id", used_relation.id.to_s
 
 895             assert_dom "> @new_version", used_relation.version.to_s
 
 899         assert Node.find(used_node.id).visible
 
 900         assert Way.find(used_way.id).visible
 
 901         assert Relation.find(used_relation.id).visible
 
 904       def test_upload_delete_with_multiple_blocks_and_if_unused
 
 905         changeset = create(:changeset)
 
 908         create(:way_node, :way => way, :node => node)
 
 909         alone_node = create(:node)
 
 912           <osmChange version='0.6'>
 
 913             <delete version="0.6">
 
 914               <node id="#{node.id}" version="#{node.version}" changeset="#{changeset.id}"/>
 
 916             <delete version="0.6" if-unused="true">
 
 917               <node id="#{alone_node.id}" version="#{alone_node.version}" changeset="#{changeset.id}"/>
 
 922         auth_header = bearer_authorization_header changeset.user
 
 924         post api_changeset_upload_path(changeset), :params => diff.to_s, :headers => auth_header
 
 926         assert_response :precondition_failed
 
 928         assert_equal "Precondition failed: Node #{node.id} is still used by ways #{way.id}.", @response.body
 
 931       def test_upload_delete_unknown_node_placeholder
 
 932         check_upload_results_in_not_found do |changeset|
 
 933           "<delete><node id='-1' changeset='#{changeset.id}' version='1'/></delete>"
 
 937       def test_upload_delete_unknown_way_placeholder
 
 938         check_upload_results_in_not_found do |changeset|
 
 939           "<delete><way id='-1' changeset='#{changeset.id}' version='1'/></delete>"
 
 943       def test_upload_delete_unknown_relation_placeholder
 
 944         check_upload_results_in_not_found do |changeset|
 
 945           "<delete><relation id='-1' changeset='#{changeset.id}' version='1'/></delete>"
 
 949       # -------------------------------------
 
 950       # Test combined element changes.
 
 951       # -------------------------------------
 
 954       # upload something which creates new objects and inserts them into
 
 955       # existing containers using placeholders.
 
 956       def test_upload_create_and_insert_elements
 
 959         relation = create(:relation)
 
 960         create(:way_node, :way => way, :node => node)
 
 961         changeset = create(:changeset)
 
 966               <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
 
 967                 <tag k='foo' v='bar'/>
 
 968                 <tag k='baz' v='bat'/>
 
 972               <way id='#{way.id}' changeset='#{changeset.id}' version='1'>
 
 974                 <nd ref='#{node.id}'/>
 
 976               <relation id='#{relation.id}' changeset='#{changeset.id}' version='1'>
 
 977                 <member type='way' role='some' ref='#{way.id}'/>
 
 978                 <member type='node' role='some' ref='-1'/>
 
 979                 <member type='relation' role='some' ref='#{relation.id}'/>
 
 985         auth_header = bearer_authorization_header changeset.user
 
 987         post api_changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
 989         assert_response :success
 
 992         assert_dom "diffResult[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1 do
 
 993           assert_dom "> node", 1 do |(node_el)|
 
 994             new_node_id = node_el["new_id"].to_i
 
 996           assert_dom "> way", 1
 
 997           assert_dom "> relation", 1
 
1000         assert_equal 2, Node.find(new_node_id).tags.size, "new node should have two tags"
 
1001         assert_equal [new_node_id, node.id], Way.find(way.id).nds, "way nodes should match"
 
1002         Relation.find(relation.id).members.each do |type, id, _role|
 
1003           assert_equal new_node_id, id, "relation should contain new node" if type == "node"
 
1008       # test that a placeholder can be reused within the same upload.
 
1009       def test_upload_create_modify_delete_node_reusing_placeholder
 
1010         changeset = create(:changeset)
 
1015               <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
 
1016                 <tag k="foo" v="bar"/>
 
1020               <node id='-1' lon='1' lat='1' changeset='#{changeset.id}' version='1'/>
 
1023               <node id='-1' lon='2' lat='2' changeset='#{changeset.id}' version='2'/>
 
1028         auth_header = bearer_authorization_header changeset.user
 
1030         assert_difference "Node.count", 1 do
 
1031           post api_changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
1033           assert_response :success
 
1036         assert_dom "diffResult>node", 3
 
1037         assert_dom "diffResult>node[old_id='-1']", 3
 
1040         assert_equal 3, node.version
 
1041         assert_not node.visible
 
1044       def test_upload_create_and_duplicate_delete
 
1045         changeset = create(:changeset)
 
1050               <node id="-1" lat="39" lon="116" changeset="#{changeset.id}" />
 
1053               <node id="-1" version="1" changeset="#{changeset.id}" />
 
1054               <node id="-1" version="1" changeset="#{changeset.id}" />
 
1059         auth_header = bearer_authorization_header changeset.user
 
1061         assert_no_difference "Node.count" do
 
1062           post api_changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
1064           assert_response :gone
 
1068       def test_upload_create_and_duplicate_delete_if_unused
 
1069         changeset = create(:changeset)
 
1074               <node id="-1" lat="39" lon="116" changeset="#{changeset.id}" />
 
1076             <delete if-unused="true">
 
1077               <node id="-1" version="1" changeset="#{changeset.id}" />
 
1078               <node id="-1" version="1" changeset="#{changeset.id}" />
 
1083         auth_header = bearer_authorization_header changeset.user
 
1085         assert_difference "Node.count", 1 do
 
1086           post api_changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
1088           assert_response :success
 
1091         assert_dom "diffResult>node", 3
 
1092         assert_dom "diffResult>node[old_id='-1']", 3
 
1093         assert_dom "diffResult>node[new_version='1']", 1
 
1094         assert_dom "diffResult>node[new_version='2']", 1
 
1097         assert_equal 2, node.version
 
1098         assert_not node.visible
 
1101       # -------------------------------------
 
1102       # Test bounding boxes.
 
1103       # -------------------------------------
 
1105       def test_upload_bbox_of_widely_spaced_nodes
 
1106         user = create(:user)
 
1108         # create an old changeset to ensure we have the maximum rate limit
 
1109         create(:changeset, :user => user, :created_at => Time.now.utc - 28.days)
 
1111         changeset = create(:changeset, :user => user)
 
1113         # upload some widely-spaced nodes, spiralling positive and negative
 
1117               <node id='-1' lon='-20' lat='-10' changeset='#{changeset.id}'/>
 
1118               <node id='-10' lon='20'  lat='10' changeset='#{changeset.id}'/>
 
1119               <node id='-2' lon='-40' lat='-20' changeset='#{changeset.id}'/>
 
1120               <node id='-11' lon='40'  lat='20' changeset='#{changeset.id}'/>
 
1121               <node id='-3' lon='-60' lat='-30' changeset='#{changeset.id}'/>
 
1122               <node id='-12' lon='60'  lat='30' changeset='#{changeset.id}'/>
 
1123               <node id='-4' lon='-80' lat='-40' changeset='#{changeset.id}'/>
 
1124               <node id='-13' lon='80'  lat='40' changeset='#{changeset.id}'/>
 
1125               <node id='-5' lon='-100' lat='-50' changeset='#{changeset.id}'/>
 
1126               <node id='-14' lon='100'  lat='50' changeset='#{changeset.id}'/>
 
1127               <node id='-6' lon='-120' lat='-60' changeset='#{changeset.id}'/>
 
1128               <node id='-15' lon='120'  lat='60' changeset='#{changeset.id}'/>
 
1129               <node id='-7' lon='-140' lat='-70' changeset='#{changeset.id}'/>
 
1130               <node id='-16' lon='140'  lat='70' changeset='#{changeset.id}'/>
 
1131               <node id='-8' lon='-160' lat='-80' changeset='#{changeset.id}'/>
 
1132               <node id='-17' lon='160'  lat='80' changeset='#{changeset.id}'/>
 
1133               <node id='-9' lon='-179.9' lat='-89.9' changeset='#{changeset.id}'/>
 
1134               <node id='-18' lon='179.9'  lat='89.9' changeset='#{changeset.id}'/>
 
1139         auth_header = bearer_authorization_header user
 
1141         post api_changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
1143         assert_response :success
 
1145         # check that the changeset bbox is within bounds
 
1147         assert_operator changeset.min_lon, :>=, -180 * GeoRecord::SCALE, "Minimum longitude (#{changeset.min_lon / GeoRecord::SCALE}) should be >= -180 to be valid."
 
1148         assert_operator changeset.max_lon, :<=, 180 * GeoRecord::SCALE, "Maximum longitude (#{changeset.max_lon / GeoRecord::SCALE}) should be <= 180 to be valid."
 
1149         assert_operator changeset.min_lat, :>=, -90 * GeoRecord::SCALE, "Minimum latitude (#{changeset.min_lat / GeoRecord::SCALE}) should be >= -90 to be valid."
 
1150         assert_operator changeset.max_lat, :<=, 90 * GeoRecord::SCALE, "Maximum latitude (#{changeset.max_lat / GeoRecord::SCALE}) should be <= 90 to be valid."
 
1153       def test_upload_bbox_of_moved_node
 
1154         changeset = create(:changeset)
 
1155         node = create(:node, :lat => 1.0, :lon => 2.0)
 
1160               <node id='#{node.id}' lat='1.1' lon='2.1' changeset='#{changeset.id}' version='1'/>
 
1165         auth_header = bearer_authorization_header changeset.user
 
1167         post api_changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
1169         assert_response :success
 
1173         assert_equal 1.0 * GeoRecord::SCALE, changeset.min_lat, "min_lat should be 1.0 degrees"
 
1174         assert_equal 2.0 * GeoRecord::SCALE, changeset.min_lon, "min_lon should be 2.0 degrees"
 
1175         assert_equal 1.1 * GeoRecord::SCALE, changeset.max_lat, "max_lat should be 1.1 degrees"
 
1176         assert_equal 2.1 * GeoRecord::SCALE, changeset.max_lon, "max_lon should be 2.1 degrees"
 
1179       def test_upload_bbox_of_extended_way
 
1181         initial_node = create(:node, :lat => 1.1, :lon => 2.1)
 
1182         create(:way_node, :way => way, :node => initial_node)
 
1183         added_node = create(:node, :lat => 1.3, :lon => 2.3)
 
1184         changeset = create(:changeset)
 
1189               <way id='#{way.id}' changeset='#{changeset.id}' version='1'>
 
1190                 <nd ref='#{initial_node.id}'/>
 
1191                 <nd ref='#{added_node.id}'/>
 
1197         auth_header = bearer_authorization_header changeset.user
 
1199         post api_changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
1201         assert_response :success
 
1205         assert_equal 1.1 * GeoRecord::SCALE, changeset.min_lat, "min_lat should be 1.1 degrees"
 
1206         assert_equal 2.1 * GeoRecord::SCALE, changeset.min_lon, "min_lon should be 2.1 degrees"
 
1207         assert_equal 1.3 * GeoRecord::SCALE, changeset.max_lat, "max_lat should be 1.3 degrees"
 
1208         assert_equal 2.3 * GeoRecord::SCALE, changeset.max_lon, "max_lon should be 2.3 degrees"
 
1211       # -------------------------------------
 
1212       # Test upload rate/size limits.
 
1213       # -------------------------------------
 
1215       def test_upload_initial_rate_limit
 
1216         user = create(:user)
 
1217         node = create(:node)
 
1218         way = create(:way_with_nodes, :nodes_count => 2)
 
1219         relation = create(:relation)
 
1221         # create a changeset that puts us near the initial rate limit
 
1222         changeset = create(:changeset, :user => user,
 
1223                                        :created_at => Time.now.utc - 5.minutes,
 
1224                                        :num_changes => Settings.initial_changes_per_hour - 2)
 
1229               <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
 
1230                 <tag k='foo' v='bar'/>
 
1231                 <tag k='baz' v='bat'/>
 
1233               <way id='-1' changeset='#{changeset.id}'>
 
1234                 <nd ref='#{node.id}'/>
 
1238               <relation id='-1' changeset='#{changeset.id}'>
 
1239                 <member type='way' role='some' ref='#{way.id}'/>
 
1240                 <member type='node' role='some' ref='#{node.id}'/>
 
1241                 <member type='relation' role='some' ref='#{relation.id}'/>
 
1247         auth_header = bearer_authorization_header user
 
1249         post api_changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
1251         assert_response :too_many_requests, "upload did not hit rate limit"
 
1254       def test_upload_maximum_rate_limit
 
1255         user = create(:user)
 
1256         node = create(:node)
 
1257         way = create(:way_with_nodes, :nodes_count => 2)
 
1258         relation = create(:relation)
 
1260         # create a changeset to establish our initial edit time
 
1261         changeset = create(:changeset, :user => user,
 
1262                                        :created_at => Time.now.utc - 28.days)
 
1264         # create changeset to put us near the maximum rate limit
 
1265         total_changes = Settings.max_changes_per_hour - 2
 
1266         while total_changes.positive?
 
1267           changes = [total_changes, Changeset::MAX_ELEMENTS].min
 
1268           changeset = create(:changeset, :user => user,
 
1269                                          :created_at => Time.now.utc - 5.minutes,
 
1270                                          :num_changes => changes)
 
1271           total_changes -= changes
 
1277               <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
 
1278                 <tag k='foo' v='bar'/>
 
1279                 <tag k='baz' v='bat'/>
 
1281               <way id='-1' changeset='#{changeset.id}'>
 
1282                 <nd ref='#{node.id}'/>
 
1286               <relation id='-1' changeset='#{changeset.id}'>
 
1287                 <member type='way' role='some' ref='#{way.id}'/>
 
1288                 <member type='node' role='some' ref='#{node.id}'/>
 
1289                 <member type='relation' role='some' ref='#{relation.id}'/>
 
1295         auth_header = bearer_authorization_header user
 
1297         post api_changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
1299         assert_response :too_many_requests, "upload did not hit rate limit"
 
1302       def test_upload_initial_size_limit
 
1303         user = create(:user)
 
1305         # create a changeset that puts us near the initial size limit
 
1306         changeset = create(:changeset, :user => user,
 
1307                                        :min_lat => (-0.5 * GeoRecord::SCALE).round, :min_lon => (0.5 * GeoRecord::SCALE).round,
 
1308                                        :max_lat => (0.5 * GeoRecord::SCALE).round, :max_lon => (2.5 * GeoRecord::SCALE).round)
 
1313               <node id='-1' lon='0.9' lat='2.9' changeset='#{changeset.id}'>
 
1314                 <tag k='foo' v='bar'/>
 
1315                 <tag k='baz' v='bat'/>
 
1321         auth_header = bearer_authorization_header user
 
1323         post api_changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
1325         assert_response :payload_too_large, "upload did not hit size limit"
 
1328       def test_upload_size_limit_after_one_week
 
1329         user = create(:user)
 
1331         # create a changeset to establish our initial edit time
 
1332         create(:changeset, :user => user, :created_at => Time.now.utc - 7.days)
 
1334         # create a changeset that puts us near the initial size limit
 
1335         changeset = create(:changeset, :user => user, :bbox => [0.5, -0.5, 2.5, 0.5])
 
1340               <node id='-1' lon='35' lat='35' changeset='#{changeset.id}'>
 
1341                 <tag k='foo' v='bar'/>
 
1342                 <tag k='baz' v='bat'/>
 
1348         auth_header = bearer_authorization_header user
 
1350         post api_changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
1352         assert_response :payload_too_large, "upload did not hit size limit"
 
1357       def check_upload_results_in_not_found(&)
 
1358         changeset = create(:changeset)
 
1359         diff = "<osmChange>#{yield changeset}</osmChange>"
 
1360         auth_header = bearer_authorization_header changeset.user
 
1362         post api_changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
1364         assert_response :not_found
 
1366         assert_equal 0, changeset.num_changes