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/subscribe", :method => :post },
 
  26         { :controller => "api/changesets", :action => "subscribe", :id => "1" }
 
  29         { :path => "/api/0.6/changeset/1/unsubscribe", :method => :post },
 
  30         { :controller => "api/changesets", :action => "unsubscribe", :id => "1" }
 
  33         { :path => "/api/0.6/changeset/1", :method => :put },
 
  34         { :controller => "api/changesets", :action => "update", :id => "1" }
 
  37         { :path => "/api/0.6/changeset/1/close", :method => :put },
 
  38         { :controller => "api/changesets", :action => "close", :id => "1" }
 
  41         { :path => "/api/0.6/changesets", :method => :get },
 
  42         { :controller => "api/changesets", :action => "query" }
 
  46     # -----------------------
 
  47     # Test simple changeset creation
 
  48     # -----------------------
 
  51       auth_header = basic_authorization_header create(:user, :data_public => false).email, "test"
 
  52       # Create the first user's changeset
 
  53       xml = "<osm><changeset>" \
 
  54             "<tag k='created_by' v='osm test suite checking changesets'/>" \
 
  56       put changeset_create_path, :params => xml, :headers => auth_header
 
  57       assert_require_public_data
 
  59       auth_header = basic_authorization_header create(:user).email, "test"
 
  60       # Create the first user's changeset
 
  61       xml = "<osm><changeset>" \
 
  62             "<tag k='created_by' v='osm test suite checking changesets'/>" \
 
  64       put changeset_create_path, :params => xml, :headers => auth_header
 
  66       assert_response :success, "Creation of changeset did not return sucess status"
 
  67       newid = @response.body.to_i
 
  69       # check end time, should be an hour ahead of creation time
 
  70       cs = Changeset.find(newid)
 
  71       duration = cs.closed_at - cs.created_at
 
  72       # the difference can either be a rational, or a floating point number
 
  73       # of seconds, depending on the code path taken :-(
 
  74       if duration.instance_of?(Rational)
 
  75         assert_equal Rational(1, 24), duration, "initial idle timeout should be an hour (#{cs.created_at} -> #{cs.closed_at})"
 
  77         # must be number of seconds...
 
  78         assert_equal 3600, duration.round, "initial idle timeout should be an hour (#{cs.created_at} -> #{cs.closed_at})"
 
  81       # checks if uploader was subscribed
 
  82       assert_equal 1, cs.subscribers.length
 
  85     def test_create_invalid
 
  86       auth_header = basic_authorization_header create(:user, :data_public => false).email, "test"
 
  87       xml = "<osm><changeset></osm>"
 
  88       put changeset_create_path, :params => xml, :headers => auth_header
 
  89       assert_require_public_data
 
  91       ## Try the public user
 
  92       auth_header = basic_authorization_header create(:user).email, "test"
 
  93       xml = "<osm><changeset></osm>"
 
  94       put changeset_create_path, :params => xml, :headers => auth_header
 
  95       assert_response :bad_request, "creating a invalid changeset should fail"
 
  98     def test_create_invalid_no_content
 
  99       ## First check with no auth
 
 100       put changeset_create_path
 
 101       assert_response :unauthorized, "shouldn't be able to create a changeset with no auth"
 
 103       ## Now try to with a non-public user
 
 104       auth_header = basic_authorization_header create(:user, :data_public => false).email, "test"
 
 105       put changeset_create_path, :headers => auth_header
 
 106       assert_require_public_data
 
 108       ## Try an inactive user
 
 109       auth_header = basic_authorization_header create(:user, :pending).email, "test"
 
 110       put changeset_create_path, :headers => auth_header
 
 113       ## Now try to use a normal user
 
 114       auth_header = basic_authorization_header create(:user).email, "test"
 
 115       put changeset_create_path, :headers => auth_header
 
 116       assert_response :bad_request, "creating a changeset with no content should fail"
 
 119     def test_create_wrong_method
 
 120       auth_header = basic_authorization_header create(:user).email, "test"
 
 121       assert_raise ActionController::RoutingError do
 
 122         get changeset_create_path, :headers => auth_header
 
 124       assert_raise ActionController::RoutingError do
 
 125         post changeset_create_path, :headers => auth_header
 
 130     # check that the changeset can be shown and returns the correct
 
 131     # document structure.
 
 133       changeset = create(:changeset)
 
 135       get changeset_show_path(changeset)
 
 136       assert_response :success, "cannot get first changeset"
 
 138       assert_select "osm[version='#{Settings.api_version}'][generator='OpenStreetMap server']", 1
 
 139       assert_select "osm>changeset[id='#{changeset.id}']", 1
 
 140       assert_select "osm>changeset>@open", "true"
 
 141       assert_select "osm>changeset>@created_at", changeset.created_at.xmlschema
 
 142       assert_select "osm>changeset>@closed_at", 0
 
 143       assert_select "osm>changeset>discussion", 0
 
 145       get changeset_show_path(changeset), :params => { :include_discussion => true }
 
 146       assert_response :success, "cannot get first changeset with comments"
 
 148       assert_select "osm[version='#{Settings.api_version}'][generator='OpenStreetMap server']", 1
 
 149       assert_select "osm>changeset[id='#{changeset.id}']", 1
 
 150       assert_select "osm>changeset>@open", "true"
 
 151       assert_select "osm>changeset>@created_at", changeset.created_at.xmlschema
 
 152       assert_select "osm>changeset>@closed_at", 0
 
 153       assert_select "osm>changeset>discussion", 1
 
 154       assert_select "osm>changeset>discussion>comment", 0
 
 156       changeset = create(:changeset, :closed)
 
 157       create_list(:changeset_comment, 3, :changeset_id => changeset.id)
 
 159       get changeset_show_path(changeset), :params => { :include_discussion => true }
 
 160       assert_response :success, "cannot get closed changeset with comments"
 
 162       assert_select "osm[version='#{Settings.api_version}'][generator='OpenStreetMap server']", 1
 
 163       assert_select "osm>changeset[id='#{changeset.id}']", 1
 
 164       assert_select "osm>changeset>@open", "false"
 
 165       assert_select "osm>changeset>@created_at", changeset.created_at.xmlschema
 
 166       assert_select "osm>changeset>@closed_at", changeset.closed_at.xmlschema
 
 167       assert_select "osm>changeset>discussion", 1
 
 168       assert_select "osm>changeset>discussion>comment", 3
 
 172     # check that a changeset that doesn't exist returns an appropriate message
 
 173     def test_show_not_found
 
 174       [0, -32, 233455644, "afg", "213"].each do |id|
 
 175         get changeset_show_path(:id => id)
 
 176         assert_response :not_found, "should get a not found"
 
 177       rescue ActionController::UrlGenerationError => e
 
 178         assert_match(/No route matches/, e.to_s)
 
 183     # test that the user who opened a change can close it
 
 185       private_user = create(:user, :data_public => false)
 
 186       private_changeset = create(:changeset, :user => private_user)
 
 188       changeset = create(:changeset, :user => user)
 
 190       ## Try without authentication
 
 191       put changeset_close_path(changeset)
 
 192       assert_response :unauthorized
 
 194       ## Try using the non-public user
 
 195       auth_header = basic_authorization_header private_user.email, "test"
 
 196       put changeset_close_path(private_changeset), :headers => auth_header
 
 197       assert_require_public_data
 
 199       ## The try with the public user
 
 200       auth_header = basic_authorization_header user.email, "test"
 
 203       put changeset_close_path(:id => cs_id), :headers => auth_header
 
 204       assert_response :success
 
 206       # test that it really is closed now
 
 207       cs = Changeset.find(changeset.id)
 
 208       assert_not(cs.is_open?,
 
 209                  "changeset should be closed now (#{cs.closed_at} > #{Time.now.getutc}.")
 
 213     # test that a different user can't close another user's changeset
 
 214     def test_close_invalid
 
 216       changeset = create(:changeset)
 
 218       auth_header = basic_authorization_header user.email, "test"
 
 220       put changeset_close_path(changeset), :headers => auth_header
 
 221       assert_response :conflict
 
 222       assert_equal "The user doesn't own that changeset", @response.body
 
 226     # test that you can't close using another method
 
 227     def test_close_method_invalid
 
 229       changeset = create(:changeset, :user => user)
 
 231       auth_header = basic_authorization_header user.email, "test"
 
 233       assert_raise ActionController::RoutingError do
 
 234         get changeset_close_path(changeset), :headers => auth_header
 
 237       assert_raise ActionController::RoutingError do
 
 238         post changeset_close_path(changeset), :headers => auth_header
 
 243     # check that you can't close a changeset that isn't found
 
 244     def test_close_not_found
 
 245       cs_ids = [0, -132, "123"]
 
 247       # First try to do it with no auth
 
 249         put changeset_close_path(:id => id)
 
 250         assert_response :unauthorized, "Shouldn't be able close the non-existant changeset #{id}, when not authorized"
 
 251       rescue ActionController::UrlGenerationError => e
 
 252         assert_match(/No route matches/, e.to_s)
 
 256       auth_header = basic_authorization_header create(:user).email, "test"
 
 258         put changeset_close_path(:id => id), :headers => auth_header
 
 259         assert_response :not_found, "The changeset #{id} doesn't exist, so can't be closed"
 
 260       rescue ActionController::UrlGenerationError => e
 
 261         assert_match(/No route matches/, e.to_s)
 
 266     # upload something simple, but valid and check that it can
 
 268     # Also try without auth and another user.
 
 269     def test_upload_simple_valid
 
 270       private_user = create(:user, :data_public => false)
 
 271       private_changeset = create(:changeset, :user => private_user)
 
 273       changeset = create(:changeset, :user => user)
 
 277       relation = create(:relation)
 
 278       other_relation = create(:relation)
 
 279       # create some tags, since we test that they are removed later
 
 280       create(:node_tag, :node => node)
 
 281       create(:way_tag, :way => way)
 
 282       create(:relation_tag, :relation => relation)
 
 285       changeset_id = changeset.id
 
 287       # simple diff to change a node, way and relation by removing
 
 292           <node id='#{node.id}' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
 
 293           <way id='#{way.id}' changeset='#{changeset_id}' version='1'>
 
 294            <nd ref='#{node.id}'/>
 
 298           <relation id='#{relation.id}' changeset='#{changeset_id}' version='1'>
 
 299            <member type='way' role='some' ref='#{way.id}'/>
 
 300            <member type='node' role='some' ref='#{node.id}'/>
 
 301            <member type='relation' role='some' ref='#{other_relation.id}'/>
 
 308       post changeset_upload_path(changeset), :params => diff
 
 309       assert_response :unauthorized,
 
 310                       "shouldn't be able to upload a simple valid diff to changeset: #{@response.body}"
 
 312       ## Now try with a private user
 
 313       auth_header = basic_authorization_header private_user.email, "test"
 
 314       changeset_id = private_changeset.id
 
 316       # simple diff to change a node, way and relation by removing
 
 321           <node id='#{node.id}' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
 
 322           <way id='#{way.id}' changeset='#{changeset_id}' version='1'>
 
 323            <nd ref='#{node.id}'/>
 
 327           <relation id='#{relation.id}' changeset='#{changeset_id}' version='1'>
 
 328            <member type='way' role='some' ref='#{way.id}'/>
 
 329            <member type='node' role='some' ref='#{node.id}'/>
 
 330            <member type='relation' role='some' ref='#{other_relation.id}'/>
 
 337       post changeset_upload_path(private_changeset), :params => diff, :headers => auth_header
 
 338       assert_response :forbidden,
 
 339                       "can't upload a simple valid diff to changeset: #{@response.body}"
 
 341       ## Now try with the public user
 
 342       auth_header = basic_authorization_header user.email, "test"
 
 343       changeset_id = changeset.id
 
 345       # simple diff to change a node, way and relation by removing
 
 350           <node id='#{node.id}' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
 
 351           <way id='#{way.id}' changeset='#{changeset_id}' version='1'>
 
 352            <nd ref='#{node.id}'/>
 
 356           <relation id='#{relation.id}' changeset='#{changeset_id}' version='1'>
 
 357            <member type='way' role='some' ref='#{way.id}'/>
 
 358            <member type='node' role='some' ref='#{node.id}'/>
 
 359            <member type='relation' role='some' ref='#{other_relation.id}'/>
 
 366       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
 367       assert_response :success,
 
 368                       "can't upload a simple valid diff to changeset: #{@response.body}"
 
 370       # check that the changes made it into the database
 
 371       assert_equal 0, Node.find(node.id).tags.size, "node #{node.id} should now have no tags"
 
 372       assert_equal 0, Way.find(way.id).tags.size, "way #{way.id} should now have no tags"
 
 373       assert_equal 0, Relation.find(relation.id).tags.size, "relation #{relation.id} should now have no tags"
 
 377     # upload something which creates new objects using placeholders
 
 378     def test_upload_create_valid
 
 380       changeset = create(:changeset, :user => user)
 
 382       way = create(:way_with_nodes, :nodes_count => 2)
 
 383       relation = create(:relation)
 
 385       auth_header = basic_authorization_header user.email, "test"
 
 387       # simple diff to create a node way and relation using placeholders
 
 391           <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
 
 392            <tag k='foo' v='bar'/>
 
 393            <tag k='baz' v='bat'/>
 
 395           <way id='-1' changeset='#{changeset.id}'>
 
 396            <nd ref='#{node.id}'/>
 
 400           <relation id='-1' changeset='#{changeset.id}'>
 
 401            <member type='way' role='some' ref='#{way.id}'/>
 
 402            <member type='node' role='some' ref='#{node.id}'/>
 
 403            <member type='relation' role='some' ref='#{relation.id}'/>
 
 410       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
 411       assert_response :success,
 
 412                       "can't upload a simple valid creation to changeset: #{@response.body}"
 
 414       # check the returned payload
 
 415       assert_select "diffResult[version='#{Settings.api_version}'][generator='OpenStreetMap server']", 1
 
 416       assert_select "diffResult>node", 1
 
 417       assert_select "diffResult>way", 1
 
 418       assert_select "diffResult>relation", 1
 
 420       # inspect the response to find out what the new element IDs are
 
 421       doc = XML::Parser.string(@response.body).parse
 
 422       new_node_id = doc.find("//diffResult/node").first["new_id"].to_i
 
 423       new_way_id = doc.find("//diffResult/way").first["new_id"].to_i
 
 424       new_rel_id = doc.find("//diffResult/relation").first["new_id"].to_i
 
 426       # check the old IDs are all present and negative one
 
 427       assert_equal(-1, doc.find("//diffResult/node").first["old_id"].to_i)
 
 428       assert_equal(-1, doc.find("//diffResult/way").first["old_id"].to_i)
 
 429       assert_equal(-1, doc.find("//diffResult/relation").first["old_id"].to_i)
 
 431       # check the versions are present and equal one
 
 432       assert_equal 1, doc.find("//diffResult/node").first["new_version"].to_i
 
 433       assert_equal 1, doc.find("//diffResult/way").first["new_version"].to_i
 
 434       assert_equal 1, doc.find("//diffResult/relation").first["new_version"].to_i
 
 436       # check that the changes made it into the database
 
 437       assert_equal 2, Node.find(new_node_id).tags.size, "new node should have two tags"
 
 438       assert_equal 0, Way.find(new_way_id).tags.size, "new way should have no tags"
 
 439       assert_equal 0, Relation.find(new_rel_id).tags.size, "new relation should have no tags"
 
 443     # test a complex delete where we delete elements which rely on eachother
 
 444     # in the same transaction.
 
 445     def test_upload_delete
 
 446       changeset = create(:changeset)
 
 447       super_relation = create(:relation)
 
 448       used_relation = create(:relation)
 
 449       used_way = create(:way)
 
 450       used_node = create(:node)
 
 451       create(:relation_member, :relation => super_relation, :member => used_relation)
 
 452       create(:relation_member, :relation => super_relation, :member => used_way)
 
 453       create(:relation_member, :relation => super_relation, :member => used_node)
 
 455       auth_header = basic_authorization_header changeset.user.display_name, "test"
 
 457       diff = XML::Document.new
 
 458       diff.root = XML::Node.new "osmChange"
 
 459       delete = XML::Node.new "delete"
 
 461       delete << xml_node_for_relation(super_relation)
 
 462       delete << xml_node_for_relation(used_relation)
 
 463       delete << xml_node_for_way(used_way)
 
 464       delete << xml_node_for_node(used_node)
 
 466       # update the changeset to one that this user owns
 
 467       %w[node way relation].each do |type|
 
 468         delete.find("//osmChange/delete/#{type}").each do |n|
 
 469           n["changeset"] = changeset.id.to_s
 
 474       post changeset_upload_path(changeset), :params => diff.to_s, :headers => auth_header
 
 475       assert_response :success,
 
 476                       "can't upload a deletion diff to changeset: #{@response.body}"
 
 478       # check the response is well-formed
 
 479       assert_select "diffResult>node", 1
 
 480       assert_select "diffResult>way", 1
 
 481       assert_select "diffResult>relation", 2
 
 483       # check that everything was deleted
 
 484       assert_not Node.find(used_node.id).visible
 
 485       assert_not Way.find(used_way.id).visible
 
 486       assert_not Relation.find(super_relation.id).visible
 
 487       assert_not Relation.find(used_relation.id).visible
 
 491     # test uploading a delete with no lat/lon, as they are optional in
 
 492     # the osmChange spec.
 
 493     def test_upload_nolatlon_delete
 
 495       changeset = create(:changeset)
 
 497       auth_header = basic_authorization_header changeset.user.display_name, "test"
 
 498       diff = "<osmChange><delete><node id='#{node.id}' version='#{node.version}' changeset='#{changeset.id}'/></delete></osmChange>"
 
 501       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
 502       assert_response :success,
 
 503                       "can't upload a deletion diff to changeset: #{@response.body}"
 
 505       # check the response is well-formed
 
 506       assert_select "diffResult>node", 1
 
 508       # check that everything was deleted
 
 509       assert_not Node.find(node.id).visible
 
 512     def test_repeated_changeset_create
 
 514         auth_header = basic_authorization_header create(:user).email, "test"
 
 516         # create a temporary changeset
 
 517         xml = "<osm><changeset>" \
 
 518               "<tag k='created_by' v='osm test suite checking changesets'/>" \
 
 520         assert_difference "Changeset.count", 1 do
 
 521           put changeset_create_path, :params => xml, :headers => auth_header
 
 523         assert_response :success
 
 527     def test_upload_large_changeset
 
 528       auth_header = basic_authorization_header create(:user).email, "test"
 
 531       put changeset_create_path, :params => "<osm><changeset/></osm>", :headers => auth_header
 
 532       assert_response :success, "Should be able to create a changeset: #{@response.body}"
 
 533       changeset_id = @response.body.to_i
 
 535       # upload some widely-spaced nodes, spiralling positive and negative
 
 539           <node id='-1' lon='-20' lat='-10' changeset='#{changeset_id}'/>
 
 540           <node id='-10' lon='20'  lat='10' changeset='#{changeset_id}'/>
 
 541           <node id='-2' lon='-40' lat='-20' changeset='#{changeset_id}'/>
 
 542           <node id='-11' lon='40'  lat='20' changeset='#{changeset_id}'/>
 
 543           <node id='-3' lon='-60' lat='-30' changeset='#{changeset_id}'/>
 
 544           <node id='-12' lon='60'  lat='30' changeset='#{changeset_id}'/>
 
 545           <node id='-4' lon='-80' lat='-40' changeset='#{changeset_id}'/>
 
 546           <node id='-13' lon='80'  lat='40' changeset='#{changeset_id}'/>
 
 547           <node id='-5' lon='-100' lat='-50' changeset='#{changeset_id}'/>
 
 548           <node id='-14' lon='100'  lat='50' changeset='#{changeset_id}'/>
 
 549           <node id='-6' lon='-120' lat='-60' changeset='#{changeset_id}'/>
 
 550           <node id='-15' lon='120'  lat='60' changeset='#{changeset_id}'/>
 
 551           <node id='-7' lon='-140' lat='-70' changeset='#{changeset_id}'/>
 
 552           <node id='-16' lon='140'  lat='70' changeset='#{changeset_id}'/>
 
 553           <node id='-8' lon='-160' lat='-80' changeset='#{changeset_id}'/>
 
 554           <node id='-17' lon='160'  lat='80' changeset='#{changeset_id}'/>
 
 555           <node id='-9' lon='-179.9' lat='-89.9' changeset='#{changeset_id}'/>
 
 556           <node id='-18' lon='179.9'  lat='89.9' changeset='#{changeset_id}'/>
 
 561       # upload it, which used to cause an error like "PGError: ERROR:
 
 562       # integer out of range" (bug #2152). but shouldn't any more.
 
 563       post changeset_upload_path(:id => changeset_id), :params => diff, :headers => auth_header
 
 564       assert_response :success,
 
 565                       "can't upload a spatially-large diff to changeset: #{@response.body}"
 
 567       # check that the changeset bbox is within bounds
 
 568       cs = Changeset.find(changeset_id)
 
 569       assert cs.min_lon >= -180 * GeoRecord::SCALE, "Minimum longitude (#{cs.min_lon / GeoRecord::SCALE}) should be >= -180 to be valid."
 
 570       assert cs.max_lon <= 180 * GeoRecord::SCALE, "Maximum longitude (#{cs.max_lon / GeoRecord::SCALE}) should be <= 180 to be valid."
 
 571       assert cs.min_lat >= -90 * GeoRecord::SCALE, "Minimum latitude (#{cs.min_lat / GeoRecord::SCALE}) should be >= -90 to be valid."
 
 572       assert cs.max_lat <= 90 * GeoRecord::SCALE, "Maximum latitude (#{cs.max_lat / GeoRecord::SCALE}) should be <= 90 to be valid."
 
 576     # test that deleting stuff in a transaction doesn't bypass the checks
 
 577     # to ensure that used elements are not deleted.
 
 578     def test_upload_delete_invalid
 
 579       changeset = create(:changeset)
 
 580       relation = create(:relation)
 
 581       other_relation = create(:relation)
 
 582       used_way = create(:way)
 
 583       used_node = create(:node)
 
 584       create(:relation_member, :relation => relation, :member => used_way)
 
 585       create(:relation_member, :relation => relation, :member => used_node)
 
 587       auth_header = basic_authorization_header changeset.user.email, "test"
 
 589       diff = XML::Document.new
 
 590       diff.root = XML::Node.new "osmChange"
 
 591       delete = XML::Node.new "delete"
 
 593       delete << xml_node_for_relation(other_relation)
 
 594       delete << xml_node_for_way(used_way)
 
 595       delete << xml_node_for_node(used_node)
 
 597       # update the changeset to one that this user owns
 
 598       %w[node way relation].each do |type|
 
 599         delete.find("//osmChange/delete/#{type}").each do |n|
 
 600           n["changeset"] = changeset.id.to_s
 
 605       post changeset_upload_path(changeset), :params => diff.to_s, :headers => auth_header
 
 606       assert_response :precondition_failed,
 
 607                       "shouldn't be able to upload a invalid deletion diff: #{@response.body}"
 
 608       assert_equal "Precondition failed: Way #{used_way.id} is still used by relations #{relation.id}.", @response.body
 
 610       # check that nothing was, in fact, deleted
 
 611       assert Node.find(used_node.id).visible
 
 612       assert Way.find(used_way.id).visible
 
 613       assert Relation.find(relation.id).visible
 
 614       assert Relation.find(other_relation.id).visible
 
 618     # test that a conditional delete of an in use object works.
 
 619     def test_upload_delete_if_unused
 
 620       changeset = create(:changeset)
 
 621       super_relation = create(:relation)
 
 622       used_relation = create(:relation)
 
 623       used_way = create(:way)
 
 624       used_node = create(:node)
 
 625       create(:relation_member, :relation => super_relation, :member => used_relation)
 
 626       create(:relation_member, :relation => super_relation, :member => used_way)
 
 627       create(:relation_member, :relation => super_relation, :member => used_node)
 
 629       auth_header = basic_authorization_header changeset.user.email, "test"
 
 631       diff = XML::Document.new
 
 632       diff.root = XML::Node.new "osmChange"
 
 633       delete = XML::Node.new "delete"
 
 635       delete["if-unused"] = ""
 
 636       delete << xml_node_for_relation(used_relation)
 
 637       delete << xml_node_for_way(used_way)
 
 638       delete << xml_node_for_node(used_node)
 
 640       # update the changeset to one that this user owns
 
 641       %w[node way relation].each do |type|
 
 642         delete.find("//osmChange/delete/#{type}").each do |n|
 
 643           n["changeset"] = changeset.id.to_s
 
 648       post changeset_upload_path(changeset), :params => diff.to_s, :headers => auth_header
 
 649       assert_response :success,
 
 650                       "can't do a conditional delete of in use objects: #{@response.body}"
 
 652       # check the returned payload
 
 653       assert_select "diffResult[version='#{Settings.api_version}'][generator='OpenStreetMap server']", 1
 
 654       assert_select "diffResult>node", 1
 
 655       assert_select "diffResult>way", 1
 
 656       assert_select "diffResult>relation", 1
 
 659       doc = XML::Parser.string(@response.body).parse
 
 661       # check the old IDs are all present and what we expect
 
 662       assert_equal used_node.id, doc.find("//diffResult/node").first["old_id"].to_i
 
 663       assert_equal used_way.id, doc.find("//diffResult/way").first["old_id"].to_i
 
 664       assert_equal used_relation.id, doc.find("//diffResult/relation").first["old_id"].to_i
 
 666       # check the new IDs are all present and unchanged
 
 667       assert_equal used_node.id, doc.find("//diffResult/node").first["new_id"].to_i
 
 668       assert_equal used_way.id, doc.find("//diffResult/way").first["new_id"].to_i
 
 669       assert_equal used_relation.id, doc.find("//diffResult/relation").first["new_id"].to_i
 
 671       # check the new versions are all present and unchanged
 
 672       assert_equal used_node.version, doc.find("//diffResult/node").first["new_version"].to_i
 
 673       assert_equal used_way.version, doc.find("//diffResult/way").first["new_version"].to_i
 
 674       assert_equal used_relation.version, doc.find("//diffResult/relation").first["new_version"].to_i
 
 676       # check that nothing was, in fact, deleted
 
 677       assert Node.find(used_node.id).visible
 
 678       assert Way.find(used_way.id).visible
 
 679       assert Relation.find(used_relation.id).visible
 
 683     # upload an element with a really long tag value
 
 684     def test_upload_invalid_too_long_tag
 
 685       changeset = create(:changeset)
 
 687       auth_header = basic_authorization_header changeset.user.email, "test"
 
 689       # simple diff to create a node way and relation using placeholders
 
 693           <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
 
 694            <tag k='foo' v='#{'x' * 256}'/>
 
 701       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
 702       assert_response :bad_request,
 
 703                       "shoudln't be able to upload too long a tag to changeset: #{@response.body}"
 
 707     # upload something which creates new objects and inserts them into
 
 708     # existing containers using placeholders.
 
 709     def test_upload_complex
 
 712       relation = create(:relation)
 
 713       create(:way_node, :way => way, :node => node)
 
 715       changeset = create(:changeset)
 
 717       auth_header = basic_authorization_header changeset.user.email, "test"
 
 719       # simple diff to create a node way and relation using placeholders
 
 723           <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
 
 724            <tag k='foo' v='bar'/>
 
 725            <tag k='baz' v='bat'/>
 
 729           <way id='#{way.id}' changeset='#{changeset.id}' version='1'>
 
 731            <nd ref='#{node.id}'/>
 
 733           <relation id='#{relation.id}' changeset='#{changeset.id}' version='1'>
 
 734            <member type='way' role='some' ref='#{way.id}'/>
 
 735            <member type='node' role='some' ref='-1'/>
 
 736            <member type='relation' role='some' ref='#{relation.id}'/>
 
 743       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
 744       assert_response :success,
 
 745                       "can't upload a complex diff to changeset: #{@response.body}"
 
 747       # check the returned payload
 
 748       assert_select "diffResult[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
 
 749       assert_select "diffResult>node", 1
 
 750       assert_select "diffResult>way", 1
 
 751       assert_select "diffResult>relation", 1
 
 753       # inspect the response to find out what the new element IDs are
 
 754       doc = XML::Parser.string(@response.body).parse
 
 755       new_node_id = doc.find("//diffResult/node").first["new_id"].to_i
 
 757       # check that the changes made it into the database
 
 758       assert_equal 2, Node.find(new_node_id).tags.size, "new node should have two tags"
 
 759       assert_equal [new_node_id, node.id], Way.find(way.id).nds, "way nodes should match"
 
 760       Relation.find(relation.id).members.each do |type, id, _role|
 
 761         assert_equal new_node_id, id, "relation should contain new node" if type == "node"
 
 766     # create a diff which references several changesets, which should cause
 
 767     # a rollback and none of the diff gets committed
 
 768     def test_upload_invalid_changesets
 
 769       changeset = create(:changeset)
 
 770       other_changeset = create(:changeset, :user => changeset.user)
 
 773       relation = create(:relation)
 
 774       other_relation = create(:relation)
 
 776       auth_header = basic_authorization_header changeset.user.email, "test"
 
 778       # simple diff to create a node way and relation using placeholders
 
 782           <node id='#{node.id}' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
 
 783           <way id='#{way.id}' changeset='#{changeset.id}' version='1'>
 
 784            <nd ref='#{node.id}'/>
 
 788           <relation id='#{relation.id}' changeset='#{changeset.id}' version='1'>
 
 789            <member type='way' role='some' ref='#{way.id}'/>
 
 790            <member type='node' role='some' ref='#{node.id}'/>
 
 791            <member type='relation' role='some' ref='#{other_relation.id}'/>
 
 795           <node id='-1' lon='0' lat='0' changeset='#{other_changeset.id}'>
 
 796            <tag k='foo' v='bar'/>
 
 797            <tag k='baz' v='bat'/>
 
 804       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
 805       assert_response :conflict,
 
 806                       "uploading a diff with multiple changesets should have failed"
 
 808       # check that objects are unmodified
 
 809       assert_nodes_are_equal(node, Node.find(node.id))
 
 810       assert_ways_are_equal(way, Way.find(way.id))
 
 811       assert_relations_are_equal(relation, Relation.find(relation.id))
 
 815     # upload multiple versions of the same element in the same diff.
 
 816     def test_upload_multiple_valid
 
 818       changeset = create(:changeset)
 
 819       auth_header = basic_authorization_header changeset.user.email, "test"
 
 821       # change the location of a node multiple times, each time referencing
 
 822       # the last version. doesn't this depend on version numbers being
 
 827           <node id='#{node.id}' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
 
 828           <node id='#{node.id}' lon='1' lat='0' changeset='#{changeset.id}' version='2'/>
 
 829           <node id='#{node.id}' lon='1' lat='1' changeset='#{changeset.id}' version='3'/>
 
 830           <node id='#{node.id}' lon='1' lat='2' changeset='#{changeset.id}' version='4'/>
 
 831           <node id='#{node.id}' lon='2' lat='2' changeset='#{changeset.id}' version='5'/>
 
 832           <node id='#{node.id}' lon='3' lat='2' changeset='#{changeset.id}' version='6'/>
 
 833           <node id='#{node.id}' lon='3' lat='3' changeset='#{changeset.id}' version='7'/>
 
 834           <node id='#{node.id}' lon='9' lat='9' changeset='#{changeset.id}' version='8'/>
 
 840       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
 841       assert_response :success,
 
 842                       "can't upload multiple versions of an element in a diff: #{@response.body}"
 
 844       # check the response is well-formed. its counter-intuitive, but the
 
 845       # API will return multiple elements with the same ID and different
 
 846       # version numbers for each change we made.
 
 847       assert_select "diffResult>node", 8
 
 851     # upload multiple versions of the same element in the same diff, but
 
 852     # keep the version numbers the same.
 
 853     def test_upload_multiple_duplicate
 
 855       changeset = create(:changeset)
 
 857       auth_header = basic_authorization_header changeset.user.email, "test"
 
 862           <node id='#{node.id}' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
 
 863           <node id='#{node.id}' lon='1' lat='1' changeset='#{changeset.id}' version='1'/>
 
 869       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
 870       assert_response :conflict,
 
 871                       "shouldn't be able to upload the same element twice in a diff: #{@response.body}"
 
 875     # try to upload some elements without specifying the version
 
 876     def test_upload_missing_version
 
 877       changeset = create(:changeset)
 
 879       auth_header = basic_authorization_header changeset.user.email, "test"
 
 884          <node id='1' lon='1' lat='1' changeset='#{changeset.id}'/>
 
 890       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
 891       assert_response :bad_request,
 
 892                       "shouldn't be able to upload an element without version: #{@response.body}"
 
 896     # try to upload with commands other than create, modify, or delete
 
 897     def test_action_upload_invalid
 
 898       changeset = create(:changeset)
 
 900       auth_header = basic_authorization_header changeset.user.email, "test"
 
 905            <node id='1' lon='1' lat='1' changeset='#{changeset.id}' />
 
 909       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
 910       assert_response :bad_request, "Shouldn't be able to upload a diff with the action ping"
 
 911       assert_equal("Unknown action ping, choices are create, modify, delete", @response.body)
 
 915     # upload a valid changeset which has a mixture of whitespace
 
 916     # to check a bug reported by ivansanchez (#1565).
 
 917     def test_upload_whitespace_valid
 
 918       changeset = create(:changeset)
 
 920       way = create(:way_with_nodes, :nodes_count => 2)
 
 921       relation = create(:relation)
 
 922       other_relation = create(:relation)
 
 923       create(:relation_tag, :relation => relation)
 
 925       auth_header = basic_authorization_header changeset.user.email, "test"
 
 929          <modify><node id='#{node.id}' lon='0' lat='0' changeset='#{changeset.id}'
 
 931           <node id='#{node.id}' lon='1' lat='1' changeset='#{changeset.id}' version='2'><tag k='k' v='v'/></node></modify>
 
 933          <relation id='#{relation.id}' changeset='#{changeset.id}' version='1'><member
 
 934            type='way' role='some' ref='#{way.id}'/><member
 
 935             type='node' role='some' ref='#{node.id}'/>
 
 936            <member type='relation' role='some' ref='#{other_relation.id}'/>
 
 938          </modify></osmChange>
 
 942       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
 943       assert_response :success,
 
 944                       "can't upload a valid diff with whitespace variations to changeset: #{@response.body}"
 
 946       # check the response is well-formed
 
 947       assert_select "diffResult>node", 2
 
 948       assert_select "diffResult>relation", 1
 
 950       # check that the changes made it into the database
 
 951       assert_equal 1, Node.find(node.id).tags.size, "node #{node.id} should now have one tag"
 
 952       assert_equal 0, Relation.find(relation.id).tags.size, "relation #{relation.id} should now have no tags"
 
 956     # test that a placeholder can be reused within the same upload.
 
 957     def test_upload_reuse_placeholder_valid
 
 958       changeset = create(:changeset)
 
 960       auth_header = basic_authorization_header changeset.user.email, "test"
 
 965           <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
 
 966            <tag k="foo" v="bar"/>
 
 970           <node id='-1' lon='1' lat='1' changeset='#{changeset.id}' version='1'/>
 
 973           <node id='-1' lon='2' lat='2' changeset='#{changeset.id}' version='2'/>
 
 979       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
 980       assert_response :success,
 
 981                       "can't upload a valid diff with re-used placeholders to changeset: #{@response.body}"
 
 983       # check the response is well-formed
 
 984       assert_select "diffResult>node", 3
 
 985       assert_select "diffResult>node[old_id='-1']", 3
 
 989     # test what happens if a diff upload re-uses placeholder IDs in an
 
 991     def test_upload_placeholder_invalid
 
 992       changeset = create(:changeset)
 
 994       auth_header = basic_authorization_header changeset.user.email, "test"
 
 999           <node id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
 
1000           <node id='-1' lon='1' lat='1' changeset='#{changeset.id}' version='1'/>
 
1001           <node id='-1' lon='2' lat='2' changeset='#{changeset.id}' version='2'/>
 
1007       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
1008       assert_response :bad_request,
 
1009                       "shouldn't be able to re-use placeholder IDs"
 
1013     # test that uploading a way referencing invalid placeholders gives a
 
1014     # proper error, not a 500.
 
1015     def test_upload_placeholder_invalid_way
 
1016       changeset = create(:changeset)
 
1019       auth_header = basic_authorization_header changeset.user.email, "test"
 
1024           <node id="-1" lon="0" lat="0" changeset="#{changeset.id}" version="1"/>
 
1025           <node id="-2" lon="1" lat="1" changeset="#{changeset.id}" version="1"/>
 
1026           <node id="-3" lon="2" lat="2" changeset="#{changeset.id}" version="1"/>
 
1027           <way id="-1" changeset="#{changeset.id}" version="1">
 
1038       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
1039       assert_response :bad_request,
 
1040                       "shouldn't be able to use invalid placeholder IDs"
 
1041       assert_equal "Placeholder node not found for reference -4 in way -1", @response.body
 
1043       # the same again, but this time use an existing way
 
1047           <node id="-1" lon="0" lat="0" changeset="#{changeset.id}" version="1"/>
 
1048           <node id="-2" lon="1" lat="1" changeset="#{changeset.id}" version="1"/>
 
1049           <node id="-3" lon="2" lat="2" changeset="#{changeset.id}" version="1"/>
 
1050           <way id="#{way.id}" changeset="#{changeset.id}" version="1">
 
1061       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
1062       assert_response :bad_request,
 
1063                       "shouldn't be able to use invalid placeholder IDs"
 
1064       assert_equal "Placeholder node not found for reference -4 in way #{way.id}", @response.body
 
1068     # test that uploading a relation referencing invalid placeholders gives a
 
1069     # proper error, not a 500.
 
1070     def test_upload_placeholder_invalid_relation
 
1071       changeset = create(:changeset)
 
1072       relation = create(:relation)
 
1074       auth_header = basic_authorization_header changeset.user.email, "test"
 
1079           <node id="-1" lon="0" lat="0" changeset="#{changeset.id}" version="1"/>
 
1080           <node id="-2" lon="1" lat="1" changeset="#{changeset.id}" version="1"/>
 
1081           <node id="-3" lon="2" lat="2" changeset="#{changeset.id}" version="1"/>
 
1082           <relation id="-1" changeset="#{changeset.id}" version="1">
 
1083            <member type="node" role="foo" ref="-1"/>
 
1084            <member type="node" role="foo" ref="-2"/>
 
1085            <member type="node" role="foo" ref="-3"/>
 
1086            <member type="node" role="foo" ref="-4"/>
 
1093       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
1094       assert_response :bad_request,
 
1095                       "shouldn't be able to use invalid placeholder IDs"
 
1096       assert_equal "Placeholder Node not found for reference -4 in relation -1.", @response.body
 
1098       # the same again, but this time use an existing relation
 
1102           <node id="-1" lon="0" lat="0" changeset="#{changeset.id}" version="1"/>
 
1103           <node id="-2" lon="1" lat="1" changeset="#{changeset.id}" version="1"/>
 
1104           <node id="-3" lon="2" lat="2" changeset="#{changeset.id}" version="1"/>
 
1105           <relation id="#{relation.id}" changeset="#{changeset.id}" version="1">
 
1106            <member type="node" role="foo" ref="-1"/>
 
1107            <member type="node" role="foo" ref="-2"/>
 
1108            <member type="node" role="foo" ref="-3"/>
 
1109            <member type="way" role="bar" ref="-1"/>
 
1116       post changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
1117       assert_response :bad_request,
 
1118                       "shouldn't be able to use invalid placeholder IDs"
 
1119       assert_equal "Placeholder Way not found for reference -1 in relation #{relation.id}.", @response.body
 
1123     # test what happens if a diff is uploaded containing only a node
 
1125     def test_upload_node_move
 
1126       auth_header = basic_authorization_header create(:user).email, "test"
 
1128       xml = "<osm><changeset>" \
 
1129             "<tag k='created_by' v='osm test suite checking changesets'/>" \
 
1130             "</changeset></osm>"
 
1131       put changeset_create_path, :params => xml, :headers => auth_header
 
1132       assert_response :success
 
1133       changeset_id = @response.body.to_i
 
1135       old_node = create(:node, :lat => 1, :lon => 1)
 
1137       diff = XML::Document.new
 
1138       diff.root = XML::Node.new "osmChange"
 
1139       modify = XML::Node.new "modify"
 
1140       xml_old_node = xml_node_for_node(old_node)
 
1141       xml_old_node["lat"] = 2.0.to_s
 
1142       xml_old_node["lon"] = 2.0.to_s
 
1143       xml_old_node["changeset"] = changeset_id.to_s
 
1144       modify << xml_old_node
 
1148       post changeset_upload_path(:id => changeset_id), :params => diff.to_s, :headers => auth_header
 
1149       assert_response :success,
 
1150                       "diff should have uploaded OK"
 
1153       changeset = Changeset.find(changeset_id)
 
1154       assert_equal 1 * GeoRecord::SCALE, changeset.min_lon, "min_lon should be 1 degree"
 
1155       assert_equal 2 * GeoRecord::SCALE, changeset.max_lon, "max_lon should be 2 degrees"
 
1156       assert_equal 1 * GeoRecord::SCALE, changeset.min_lat, "min_lat should be 1 degree"
 
1157       assert_equal 2 * GeoRecord::SCALE, changeset.max_lat, "max_lat should be 2 degrees"
 
1161     # test what happens if a diff is uploaded adding a node to a way.
 
1162     def test_upload_way_extend
 
1163       auth_header = basic_authorization_header create(:user).email, "test"
 
1165       xml = "<osm><changeset>" \
 
1166             "<tag k='created_by' v='osm test suite checking changesets'/>" \
 
1167             "</changeset></osm>"
 
1168       put changeset_create_path, :params => xml, :headers => auth_header
 
1169       assert_response :success
 
1170       changeset_id = @response.body.to_i
 
1172       old_way = create(:way)
 
1173       create(:way_node, :way => old_way, :node => create(:node, :lat => 1, :lon => 1))
 
1175       diff = XML::Document.new
 
1176       diff.root = XML::Node.new "osmChange"
 
1177       modify = XML::Node.new "modify"
 
1178       xml_old_way = xml_node_for_way(old_way)
 
1179       nd_ref = XML::Node.new "nd"
 
1180       nd_ref["ref"] = create(:node, :lat => 3, :lon => 3).id.to_s
 
1181       xml_old_way << nd_ref
 
1182       xml_old_way["changeset"] = changeset_id.to_s
 
1183       modify << xml_old_way
 
1187       post changeset_upload_path(:id => changeset_id), :params => diff.to_s, :headers => auth_header
 
1188       assert_response :success,
 
1189                       "diff should have uploaded OK"
 
1192       changeset = Changeset.find(changeset_id)
 
1193       assert_equal 1 * GeoRecord::SCALE, changeset.min_lon, "min_lon should be 1 degree"
 
1194       assert_equal 3 * GeoRecord::SCALE, changeset.max_lon, "max_lon should be 3 degrees"
 
1195       assert_equal 1 * GeoRecord::SCALE, changeset.min_lat, "min_lat should be 1 degree"
 
1196       assert_equal 3 * GeoRecord::SCALE, changeset.max_lat, "max_lat should be 3 degrees"
 
1200     # test for more issues in #1568
 
1201     def test_upload_empty_invalid
 
1202       changeset = create(:changeset)
 
1204       auth_header = basic_authorization_header changeset.user.email, "test"
 
1207        "<osmChange></osmChange>",
 
1208        "<osmChange><modify/></osmChange>",
 
1209        "<osmChange><modify></modify></osmChange>"].each do |diff|
 
1211         post changeset_upload_path(changeset), :params => diff, :headers => auth_header
 
1212         assert_response(:success, "should be able to upload " \
 
1213                         "empty changeset: " + diff)
 
1218     # test that the X-Error-Format header works to request XML errors
 
1219     def test_upload_xml_errors
 
1220       changeset = create(:changeset)
 
1221       node = create(:node)
 
1222       create(:relation_member, :member => node)
 
1224       auth_header = basic_authorization_header changeset.user.email, "test"
 
1226       # try and delete a node that is in use
 
1227       diff = XML::Document.new
 
1228       diff.root = XML::Node.new "osmChange"
 
1229       delete = XML::Node.new "delete"
 
1231       delete << xml_node_for_node(node)
 
1234       error_header = error_format_header "xml"
 
1235       post changeset_upload_path(changeset), :params => diff.to_s, :headers => auth_header.merge(error_header)
 
1236       assert_response :success,
 
1237                       "failed to return error in XML format"
 
1239       # check the returned payload
 
1240       assert_select "osmError[version='#{Settings.api_version}'][generator='OpenStreetMap server']", 1
 
1241       assert_select "osmError>status", 1
 
1242       assert_select "osmError>message", 1
 
1246     # when we make some simple changes we get the same changes back from the
 
1248     def test_diff_download_simple
 
1249       node = create(:node)
 
1251       ## First try with a non-public user, which should get a forbidden
 
1252       auth_header = basic_authorization_header create(:user, :data_public => false).email, "test"
 
1254       # create a temporary changeset
 
1255       xml = "<osm><changeset>" \
 
1256             "<tag k='created_by' v='osm test suite checking changesets'/>" \
 
1257             "</changeset></osm>"
 
1258       put changeset_create_path, :params => xml, :headers => auth_header
 
1259       assert_response :forbidden
 
1261       ## Now try with a normal user
 
1262       auth_header = basic_authorization_header create(:user).email, "test"
 
1264       # create a temporary changeset
 
1265       xml = "<osm><changeset>" \
 
1266             "<tag k='created_by' v='osm test suite checking changesets'/>" \
 
1267             "</changeset></osm>"
 
1268       put changeset_create_path, :params => xml, :headers => auth_header
 
1269       assert_response :success
 
1270       changeset_id = @response.body.to_i
 
1276           <node id='#{node.id}' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
 
1277           <node id='#{node.id}' lon='1' lat='0' changeset='#{changeset_id}' version='2'/>
 
1278           <node id='#{node.id}' lon='1' lat='1' changeset='#{changeset_id}' version='3'/>
 
1279           <node id='#{node.id}' lon='1' lat='2' changeset='#{changeset_id}' version='4'/>
 
1280           <node id='#{node.id}' lon='2' lat='2' changeset='#{changeset_id}' version='5'/>
 
1281           <node id='#{node.id}' lon='3' lat='2' changeset='#{changeset_id}' version='6'/>
 
1282           <node id='#{node.id}' lon='3' lat='3' changeset='#{changeset_id}' version='7'/>
 
1283           <node id='#{node.id}' lon='9' lat='9' changeset='#{changeset_id}' version='8'/>
 
1289       post changeset_upload_path(:id => changeset_id), :params => diff, :headers => auth_header
 
1290       assert_response :success,
 
1291                       "can't upload multiple versions of an element in a diff: #{@response.body}"
 
1293       get changeset_download_path(:id => changeset_id)
 
1294       assert_response :success
 
1296       assert_select "osmChange", 1
 
1297       assert_select "osmChange>modify", 8
 
1298       assert_select "osmChange>modify>node", 8
 
1302     # culled this from josm to ensure that nothing in the way that josm
 
1303     # is formatting the request is causing it to fail.
 
1305     # NOTE: the error turned out to be something else completely!
 
1306     def test_josm_upload
 
1307       auth_header = basic_authorization_header create(:user).email, "test"
 
1309       # create a temporary changeset
 
1310       xml = "<osm><changeset>" \
 
1311             "<tag k='created_by' v='osm test suite checking changesets'/>" \
 
1312             "</changeset></osm>"
 
1313       put changeset_create_path, :params => xml, :headers => auth_header
 
1314       assert_response :success
 
1315       changeset_id = @response.body.to_i
 
1318         <osmChange version="0.6" generator="JOSM">
 
1319         <create version="0.6" generator="JOSM">
 
1320           <node id='-1' visible='true' changeset='#{changeset_id}' lat='51.49619982187321' lon='-0.18722061869438314' />
 
1321           <node id='-2' visible='true' changeset='#{changeset_id}' lat='51.496359883909605' lon='-0.18653093576241928' />
 
1322           <node id='-3' visible='true' changeset='#{changeset_id}' lat='51.49598132358285' lon='-0.18719613290981638' />
 
1323           <node id='-4' visible='true' changeset='#{changeset_id}' lat='51.4961591711078' lon='-0.18629015888084607' />
 
1324           <node id='-5' visible='true' changeset='#{changeset_id}' lat='51.49582126021711' lon='-0.18708186591517145' />
 
1325           <node id='-6' visible='true' changeset='#{changeset_id}' lat='51.49591018437858' lon='-0.1861432441734455' />
 
1326           <node id='-7' visible='true' changeset='#{changeset_id}' lat='51.49560784152179' lon='-0.18694719410005425' />
 
1327           <node id='-8' visible='true' changeset='#{changeset_id}' lat='51.49567389979617' lon='-0.1860289771788006' />
 
1328           <node id='-9' visible='true' changeset='#{changeset_id}' lat='51.49543761398892' lon='-0.186820684213126' />
 
1329           <way id='-10' action='modiy' visible='true' changeset='#{changeset_id}'>
 
1339             <tag k='highway' v='residential' />
 
1340             <tag k='name' v='Foobar Street' />
 
1347       post changeset_upload_path(:id => changeset_id), :params => diff, :headers => auth_header
 
1348       assert_response :success,
 
1349                       "can't upload a diff from JOSM: #{@response.body}"
 
1351       get changeset_download_path(:id => changeset_id)
 
1352       assert_response :success
 
1354       assert_select "osmChange", 1
 
1355       assert_select "osmChange>create>node", 9
 
1356       assert_select "osmChange>create>way", 1
 
1357       assert_select "osmChange>create>way>nd", 9
 
1358       assert_select "osmChange>create>way>tag", 2
 
1362     # when we make some complex changes we get the same changes back from the
 
1364     def test_diff_download_complex
 
1365       node = create(:node)
 
1366       node2 = create(:node)
 
1368       auth_header = basic_authorization_header create(:user).email, "test"
 
1370       # create a temporary changeset
 
1371       xml = "<osm><changeset>" \
 
1372             "<tag k='created_by' v='osm test suite checking changesets'/>" \
 
1373             "</changeset></osm>"
 
1374       put changeset_create_path, :params => xml, :headers => auth_header
 
1375       assert_response :success
 
1376       changeset_id = @response.body.to_i
 
1382           <node id='#{node.id}' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
 
1385           <node id='-1' lon='9' lat='9' changeset='#{changeset_id}' version='0'/>
 
1386           <node id='-2' lon='8' lat='9' changeset='#{changeset_id}' version='0'/>
 
1387           <node id='-3' lon='7' lat='9' changeset='#{changeset_id}' version='0'/>
 
1390           <node id='#{node2.id}' lon='20' lat='15' changeset='#{changeset_id}' version='1'/>
 
1391           <way id='#{way.id}' changeset='#{changeset_id}' version='1'>
 
1392            <nd ref='#{node2.id}'/>
 
1402       post changeset_upload_path(:id => changeset_id), :params => diff, :headers => auth_header
 
1403       assert_response :success,
 
1404                       "can't upload multiple versions of an element in a diff: #{@response.body}"
 
1406       get changeset_download_path(:id => changeset_id)
 
1407       assert_response :success
 
1409       assert_select "osmChange", 1
 
1410       assert_select "osmChange>create", 3
 
1411       assert_select "osmChange>delete", 1
 
1412       assert_select "osmChange>modify", 2
 
1413       assert_select "osmChange>create>node", 3
 
1414       assert_select "osmChange>delete>node", 1
 
1415       assert_select "osmChange>modify>node", 1
 
1416       assert_select "osmChange>modify>way", 1
 
1419     def test_changeset_download
 
1420       changeset = create(:changeset)
 
1421       node = create(:node, :with_history, :version => 1, :changeset => changeset)
 
1422       tag = create(:old_node_tag, :old_node => node.old_nodes.find_by(:version => 1))
 
1423       node2 = create(:node, :with_history, :version => 1, :changeset => changeset)
 
1424       _node3 = create(:node, :with_history, :deleted, :version => 1, :changeset => changeset)
 
1425       _relation = create(:relation, :with_history, :version => 1, :changeset => changeset)
 
1426       _relation2 = create(:relation, :with_history, :deleted, :version => 1, :changeset => changeset)
 
1428       get changeset_download_path(changeset)
 
1430       assert_response :success
 
1432       # print @response.body
 
1433       # FIXME: needs more assert_select tests
 
1434       assert_select "osmChange[version='#{Settings.api_version}'][generator='#{Settings.generator}']" do
 
1435         assert_select "create", :count => 5
 
1436         assert_select "create>node[id='#{node.id}'][visible='#{node.visible?}'][version='#{node.version}']" do
 
1437           assert_select "tag[k='#{tag.k}'][v='#{tag.v}']"
 
1439         assert_select "create>node[id='#{node2.id}']"
 
1444     # check that the bounding box of a changeset gets updated correctly
 
1445     # FIXME: This should really be moded to a integration test due to the with_controller
 
1446     def test_changeset_bbox
 
1448       create(:way_node, :way => way, :node => create(:node, :lat => 3, :lon => 3))
 
1450       auth_header = basic_authorization_header create(:user).email, "test"
 
1452       # create a new changeset
 
1453       xml = "<osm><changeset/></osm>"
 
1454       put changeset_create_path, :params => xml, :headers => auth_header
 
1455       assert_response :success, "Creating of changeset failed."
 
1456       changeset_id = @response.body.to_i
 
1458       # add a single node to it
 
1459       with_controller(NodesController.new) do
 
1460         xml = "<osm><node lon='1' lat='2' changeset='#{changeset_id}'/></osm>"
 
1461         put node_create_path, :params => xml, :headers => auth_header
 
1462         assert_response :success, "Couldn't create node."
 
1465       # get the bounding box back from the changeset
 
1466       get changeset_show_path(:id => changeset_id)
 
1467       assert_response :success, "Couldn't read back changeset."
 
1468       assert_select "osm>changeset[min_lon='1.0000000']", 1
 
1469       assert_select "osm>changeset[max_lon='1.0000000']", 1
 
1470       assert_select "osm>changeset[min_lat='2.0000000']", 1
 
1471       assert_select "osm>changeset[max_lat='2.0000000']", 1
 
1473       # add another node to it
 
1474       with_controller(NodesController.new) do
 
1475         xml = "<osm><node lon='2' lat='1' changeset='#{changeset_id}'/></osm>"
 
1476         put node_create_path, :params => xml, :headers => auth_header
 
1477         assert_response :success, "Couldn't create second node."
 
1480       # get the bounding box back from the changeset
 
1481       get changeset_show_path(:id => changeset_id)
 
1482       assert_response :success, "Couldn't read back changeset for the second time."
 
1483       assert_select "osm>changeset[min_lon='1.0000000']", 1
 
1484       assert_select "osm>changeset[max_lon='2.0000000']", 1
 
1485       assert_select "osm>changeset[min_lat='1.0000000']", 1
 
1486       assert_select "osm>changeset[max_lat='2.0000000']", 1
 
1488       # add (delete) a way to it, which contains a point at (3,3)
 
1489       with_controller(WaysController.new) do
 
1490         xml = update_changeset(xml_for_way(way), changeset_id)
 
1491         delete api_way_path(way), :params => xml.to_s, :headers => auth_header
 
1492         assert_response :success, "Couldn't delete a way."
 
1495       # get the bounding box back from the changeset
 
1496       get changeset_show_path(:id => changeset_id)
 
1497       assert_response :success, "Couldn't read back changeset for the third time."
 
1498       assert_select "osm>changeset[min_lon='1.0000000']", 1
 
1499       assert_select "osm>changeset[max_lon='3.0000000']", 1
 
1500       assert_select "osm>changeset[min_lat='1.0000000']", 1
 
1501       assert_select "osm>changeset[max_lat='3.0000000']", 1
 
1505     # test the query functionality of changesets
 
1507       private_user = create(:user, :data_public => false)
 
1508       private_user_changeset = create(:changeset, :user => private_user)
 
1509       private_user_closed_changeset = create(:changeset, :closed, :user => private_user)
 
1510       user = create(:user)
 
1511       changeset = create(:changeset, :user => user)
 
1512       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))
 
1513       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)
 
1514       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)
 
1516       get changesets_path(:bbox => "-10,-10, 10, 10")
 
1517       assert_response :success, "can't get changesets in bbox"
 
1518       assert_changesets [changeset2, changeset3]
 
1520       get changesets_path(:bbox => "4.5,4.5,4.6,4.6")
 
1521       assert_response :success, "can't get changesets in bbox"
 
1522       assert_changesets [changeset3]
 
1524       # not found when looking for changesets of non-existing users
 
1525       get changesets_path(:user => User.maximum(:id) + 1)
 
1526       assert_response :not_found
 
1527       get changesets_path(:display_name => " ")
 
1528       assert_response :not_found
 
1530       # can't get changesets of user 1 without authenticating
 
1531       get changesets_path(:user => private_user.id)
 
1532       assert_response :not_found, "shouldn't be able to get changesets by non-public user (ID)"
 
1533       get changesets_path(:display_name => private_user.display_name)
 
1534       assert_response :not_found, "shouldn't be able to get changesets by non-public user (name)"
 
1536       # but this should work
 
1537       auth_header = basic_authorization_header private_user.email, "test"
 
1538       get changesets_path(:user => private_user.id), :headers => auth_header
 
1539       assert_response :success, "can't get changesets by user ID"
 
1540       assert_changesets [private_user_changeset, private_user_closed_changeset]
 
1542       get changesets_path(:display_name => private_user.display_name), :headers => auth_header
 
1543       assert_response :success, "can't get changesets by user name"
 
1544       assert_changesets [private_user_changeset, private_user_closed_changeset]
 
1546       # check that the correct error is given when we provide both UID and name
 
1547       get changesets_path(:user => private_user.id,
 
1548                           :display_name => private_user.display_name), :headers => auth_header
 
1549       assert_response :bad_request, "should be a bad request to have both ID and name specified"
 
1551       get changesets_path(:user => private_user.id, :open => true), :headers => auth_header
 
1552       assert_response :success, "can't get changesets by user and open"
 
1553       assert_changesets [private_user_changeset]
 
1555       get changesets_path(:time => "2007-12-31"), :headers => auth_header
 
1556       assert_response :success, "can't get changesets by time-since"
 
1557       assert_changesets [private_user_changeset, private_user_closed_changeset, changeset, closed_changeset, changeset2, changeset3]
 
1559       get changesets_path(:time => "2008-01-01T12:34Z"), :headers => auth_header
 
1560       assert_response :success, "can't get changesets by time-since with hour"
 
1561       assert_changesets [private_user_changeset, private_user_closed_changeset, changeset, closed_changeset, changeset2, changeset3]
 
1563       get changesets_path(:time => "2007-12-31T23:59Z,2008-01-02T00:01Z"), :headers => auth_header
 
1564       assert_response :success, "can't get changesets by time-range"
 
1565       assert_changesets [closed_changeset]
 
1567       get changesets_path(:open => "true"), :headers => auth_header
 
1568       assert_response :success, "can't get changesets by open-ness"
 
1569       assert_changesets [private_user_changeset, changeset, changeset2, changeset3]
 
1571       get changesets_path(:closed => "true"), :headers => auth_header
 
1572       assert_response :success, "can't get changesets by closed-ness"
 
1573       assert_changesets [private_user_closed_changeset, closed_changeset]
 
1575       get changesets_path(:closed => "true", :user => private_user.id), :headers => auth_header
 
1576       assert_response :success, "can't get changesets by closed-ness and user"
 
1577       assert_changesets [private_user_closed_changeset]
 
1579       get changesets_path(:closed => "true", :user => user.id), :headers => auth_header
 
1580       assert_response :success, "can't get changesets by closed-ness and user"
 
1581       assert_changesets [closed_changeset]
 
1583       get changesets_path(:changesets => "#{private_user_changeset.id},#{changeset.id},#{closed_changeset.id}"), :headers => auth_header
 
1584       assert_response :success, "can't get changesets by id (as comma-separated string)"
 
1585       assert_changesets [private_user_changeset, changeset, closed_changeset]
 
1587       get changesets_path(:changesets => ""), :headers => auth_header
 
1588       assert_response :bad_request, "should be a bad request since changesets is empty"
 
1592     # check that errors are returned if garbage is inserted
 
1593     # into query strings
 
1594     def test_query_invalid
 
1597        ";drop table users;"].each do |bbox|
 
1598         get changesets_path(:bbox => bbox)
 
1599         assert_response :bad_request, "'#{bbox}' isn't a bbox"
 
1604        ";drop table users;",
 
1606        "-,-"].each do |time|
 
1607         get changesets_path(:time => time)
 
1608         assert_response :bad_request, "'#{time}' isn't a valid time range"
 
1615         get changesets_path(:user => uid)
 
1616         assert_response :bad_request, "'#{uid}' isn't a valid user ID"
 
1621     # check updating tags on a changeset
 
1622     def test_changeset_update
 
1623       private_user = create(:user, :data_public => false)
 
1624       private_changeset = create(:changeset, :user => private_user)
 
1625       user = create(:user)
 
1626       changeset = create(:changeset, :user => user)
 
1628       ## First try with a non-public user
 
1629       new_changeset = create_changeset_xml(:user => private_user)
 
1630       new_tag = XML::Node.new "tag"
 
1631       new_tag["k"] = "tagtesting"
 
1632       new_tag["v"] = "valuetesting"
 
1633       new_changeset.find("//osm/changeset").first << new_tag
 
1635       # try without any authorization
 
1636       put changeset_show_path(private_changeset), :params => new_changeset.to_s
 
1637       assert_response :unauthorized
 
1639       # try with the wrong authorization
 
1640       auth_header = basic_authorization_header create(:user).email, "test"
 
1641       put changeset_show_path(private_changeset), :params => new_changeset.to_s, :headers => auth_header
 
1642       assert_response :conflict
 
1644       # now this should get an unauthorized
 
1645       auth_header = basic_authorization_header private_user.email, "test"
 
1646       put changeset_show_path(private_changeset), :params => new_changeset.to_s, :headers => auth_header
 
1647       assert_require_public_data "user with their data non-public, shouldn't be able to edit their changeset"
 
1649       ## Now try with the public user
 
1650       new_changeset = create_changeset_xml(:id => 1)
 
1651       new_tag = XML::Node.new "tag"
 
1652       new_tag["k"] = "tagtesting"
 
1653       new_tag["v"] = "valuetesting"
 
1654       new_changeset.find("//osm/changeset").first << new_tag
 
1656       # try without any authorization
 
1657       put changeset_show_path(changeset), :params => new_changeset.to_s
 
1658       assert_response :unauthorized
 
1660       # try with the wrong authorization
 
1661       auth_header = basic_authorization_header create(:user).email, "test"
 
1662       put changeset_show_path(changeset), :params => new_changeset.to_s, :headers => auth_header
 
1663       assert_response :conflict
 
1665       # now this should work...
 
1666       auth_header = basic_authorization_header user.email, "test"
 
1667       put changeset_show_path(changeset), :params => new_changeset.to_s, :headers => auth_header
 
1668       assert_response :success
 
1670       assert_select "osm>changeset[id='#{changeset.id}']", 1
 
1671       assert_select "osm>changeset>tag", 1
 
1672       assert_select "osm>changeset>tag[k='tagtesting'][v='valuetesting']", 1
 
1676     # check that a user different from the one who opened the changeset
 
1678     def test_changeset_update_invalid
 
1679       auth_header = basic_authorization_header create(:user).email, "test"
 
1681       changeset = create(:changeset)
 
1682       new_changeset = create_changeset_xml(:user => changeset.user, :id => changeset.id)
 
1683       new_tag = XML::Node.new "tag"
 
1684       new_tag["k"] = "testing"
 
1685       new_tag["v"] = "testing"
 
1686       new_changeset.find("//osm/changeset").first << new_tag
 
1688       put changeset_show_path(changeset), :params => new_changeset.to_s, :headers => auth_header
 
1689       assert_response :conflict
 
1693     # check that a changeset can contain a certain max number of changes.
 
1694     ## FIXME should be changed to an integration test due to the with_controller
 
1695     def test_changeset_limits
 
1696       auth_header = basic_authorization_header create(:user).email, "test"
 
1698       # open a new changeset
 
1699       xml = "<osm><changeset/></osm>"
 
1700       put changeset_create_path, :params => xml, :headers => auth_header
 
1701       assert_response :success, "can't create a new changeset"
 
1702       cs_id = @response.body.to_i
 
1704       # start the counter just short of where the changeset should finish.
 
1706       # alter the database to set the counter on the changeset directly,
 
1707       # otherwise it takes about 6 minutes to fill all of them.
 
1708       changeset = Changeset.find(cs_id)
 
1709       changeset.num_changes = Changeset::MAX_ELEMENTS - offset
 
1712       with_controller(NodesController.new) do
 
1714         xml = "<osm><node changeset='#{cs_id}' lat='0.0' lon='0.0'/></osm>"
 
1715         put node_create_path, :params => xml, :headers => auth_header
 
1716         assert_response :success, "can't create a new node"
 
1717         node_id = @response.body.to_i
 
1719         get api_node_path(:id => node_id)
 
1720         assert_response :success, "can't read back new node"
 
1721         node_doc = XML::Parser.string(@response.body).parse
 
1722         node_xml = node_doc.find("//osm/node").first
 
1724         # loop until we fill the changeset with nodes
 
1726           node_xml["lat"] = rand.to_s
 
1727           node_xml["lon"] = rand.to_s
 
1728           node_xml["version"] = (i + 1).to_s
 
1730           put api_node_path(:id => node_id), :params => node_doc.to_s, :headers => auth_header
 
1731           assert_response :success, "attempt #{i} should have succeeded"
 
1734         # trying again should fail
 
1735         node_xml["lat"] = rand.to_s
 
1736         node_xml["lon"] = rand.to_s
 
1737         node_xml["version"] = offset.to_s
 
1739         put api_node_path(:id => node_id), :params => node_doc.to_s, :headers => auth_header
 
1740         assert_response :conflict, "final attempt should have failed"
 
1743       changeset = Changeset.find(cs_id)
 
1744       assert_equal Changeset::MAX_ELEMENTS + 1, changeset.num_changes
 
1746       # check that the changeset is now closed as well
 
1747       assert_not(changeset.is_open?,
 
1748                  "changeset should have been auto-closed by exceeding " \
 
1753     # check that the changeset download for a changeset with a redacted
 
1754     # element in it doesn't contain that element.
 
1755     def test_diff_download_redacted
 
1756       changeset = create(:changeset)
 
1757       node = create(:node, :with_history, :version => 2, :changeset => changeset)
 
1758       node_v1 = node.old_nodes.find_by(:version => 1)
 
1759       node_v1.redact!(create(:redaction))
 
1761       get changeset_download_path(changeset)
 
1762       assert_response :success
 
1764       assert_select "osmChange", 1
 
1765       # this changeset contains the node in versions 1 & 2, but 1 should
 
1767       assert_select "osmChange node[id='#{node.id}']", 1
 
1768       assert_select "osmChange node[id='#{node.id}'][version='1']", 0
 
1772     # test subscribe success
 
1773     def test_subscribe_success
 
1774       auth_header = basic_authorization_header create(:user).email, "test"
 
1775       changeset = create(:changeset, :closed)
 
1777       assert_difference "changeset.subscribers.count", 1 do
 
1778         post changeset_subscribe_path(changeset), :headers => auth_header
 
1780       assert_response :success
 
1782       # not closed changeset
 
1783       changeset = create(:changeset)
 
1784       assert_difference "changeset.subscribers.count", 1 do
 
1785         post changeset_subscribe_path(changeset), :headers => auth_header
 
1787       assert_response :success
 
1791     # test subscribe fail
 
1792     def test_subscribe_fail
 
1793       user = create(:user)
 
1796       changeset = create(:changeset, :closed)
 
1797       assert_no_difference "changeset.subscribers.count" do
 
1798         post changeset_subscribe_path(changeset)
 
1800       assert_response :unauthorized
 
1802       auth_header = basic_authorization_header user.email, "test"
 
1805       assert_no_difference "changeset.subscribers.count" do
 
1806         post changeset_subscribe_path(:id => 999111), :headers => auth_header
 
1808       assert_response :not_found
 
1810       # trying to subscribe when already subscribed
 
1811       changeset = create(:changeset, :closed)
 
1812       changeset.subscribers.push(user)
 
1813       assert_no_difference "changeset.subscribers.count" do
 
1814         post changeset_subscribe_path(changeset), :headers => auth_header
 
1816       assert_response :conflict
 
1820     # test unsubscribe success
 
1821     def test_unsubscribe_success
 
1822       user = create(:user)
 
1823       auth_header = basic_authorization_header user.email, "test"
 
1824       changeset = create(:changeset, :closed)
 
1825       changeset.subscribers.push(user)
 
1827       assert_difference "changeset.subscribers.count", -1 do
 
1828         post changeset_unsubscribe_path(changeset), :headers => auth_header
 
1830       assert_response :success
 
1832       # not closed changeset
 
1833       changeset = create(:changeset)
 
1834       changeset.subscribers.push(user)
 
1836       assert_difference "changeset.subscribers.count", -1 do
 
1837         post changeset_unsubscribe_path(changeset), :headers => auth_header
 
1839       assert_response :success
 
1843     # test unsubscribe fail
 
1844     def test_unsubscribe_fail
 
1846       changeset = create(:changeset, :closed)
 
1847       assert_no_difference "changeset.subscribers.count" do
 
1848         post changeset_unsubscribe_path(changeset)
 
1850       assert_response :unauthorized
 
1852       auth_header = basic_authorization_header create(:user).email, "test"
 
1855       assert_no_difference "changeset.subscribers.count" do
 
1856         post changeset_unsubscribe_path(:id => 999111), :headers => auth_header
 
1858       assert_response :not_found
 
1860       # trying to unsubscribe when not subscribed
 
1861       changeset = create(:changeset, :closed)
 
1862       assert_no_difference "changeset.subscribers.count" do
 
1863         post changeset_unsubscribe_path(changeset), :headers => auth_header
 
1865       assert_response :not_found
 
1871     # boilerplate for checking that certain changesets exist in the
 
1873     def assert_changesets(changesets)
 
1874       assert_select "osm>changeset", changesets.size
 
1875       changesets.each do |changeset|
 
1876         assert_select "osm>changeset[id='#{changeset.id}']", 1
 
1881     # update the changeset_id of a way element
 
1882     def update_changeset(xml, changeset_id)
 
1883       xml_attr_rewrite(xml, "changeset", changeset_id)
 
1887     # update an attribute in a way element
 
1888     def xml_attr_rewrite(xml, name, value)
 
1889       xml.find("//osm/way").first[name] = value.to_s
 
1894     # build XML for changesets
 
1895     def create_changeset_xml(user: nil, id: nil)
 
1896       root = XML::Document.new
 
1897       root.root = XML::Node.new "osm"
 
1898       cs = XML::Node.new "changeset"
 
1900         cs["user"] = user.display_name
 
1901         cs["uid"] = user.id.to_s
 
1903       cs["id"] = id.to_s if id