end
end
- def test_upload_large_changeset
- user = create(:user)
- auth_header = bearer_authorization_header user
-
- # create an old changeset to ensure we have the maximum rate limit
- create(:changeset, :user => user, :created_at => Time.now.utc - 28.days)
-
- # create a changeset
- post api_changesets_path, :params => "<osm><changeset/></osm>", :headers => auth_header
- assert_response :success, "Should be able to create a changeset: #{@response.body}"
- changeset_id = @response.body.to_i
-
- # upload some widely-spaced nodes, spiralling positive and negative
- diff = <<~CHANGESET
- <osmChange>
- <create>
- <node id='-1' lon='-20' lat='-10' changeset='#{changeset_id}'/>
- <node id='-10' lon='20' lat='10' changeset='#{changeset_id}'/>
- <node id='-2' lon='-40' lat='-20' changeset='#{changeset_id}'/>
- <node id='-11' lon='40' lat='20' changeset='#{changeset_id}'/>
- <node id='-3' lon='-60' lat='-30' changeset='#{changeset_id}'/>
- <node id='-12' lon='60' lat='30' changeset='#{changeset_id}'/>
- <node id='-4' lon='-80' lat='-40' changeset='#{changeset_id}'/>
- <node id='-13' lon='80' lat='40' changeset='#{changeset_id}'/>
- <node id='-5' lon='-100' lat='-50' changeset='#{changeset_id}'/>
- <node id='-14' lon='100' lat='50' changeset='#{changeset_id}'/>
- <node id='-6' lon='-120' lat='-60' changeset='#{changeset_id}'/>
- <node id='-15' lon='120' lat='60' changeset='#{changeset_id}'/>
- <node id='-7' lon='-140' lat='-70' changeset='#{changeset_id}'/>
- <node id='-16' lon='140' lat='70' changeset='#{changeset_id}'/>
- <node id='-8' lon='-160' lat='-80' changeset='#{changeset_id}'/>
- <node id='-17' lon='160' lat='80' changeset='#{changeset_id}'/>
- <node id='-9' lon='-179.9' lat='-89.9' changeset='#{changeset_id}'/>
- <node id='-18' lon='179.9' lat='89.9' changeset='#{changeset_id}'/>
- </create>
- </osmChange>
- CHANGESET
-
- # upload it, which used to cause an error like "PGError: ERROR:
- # integer out of range" (bug #2152). but shouldn't any more.
- post api_changeset_upload_path(changeset_id), :params => diff, :headers => auth_header
- assert_response :success,
- "can't upload a spatially-large diff to changeset: #{@response.body}"
-
- # check that the changeset bbox is within bounds
- cs = Changeset.find(changeset_id)
- assert_operator cs.min_lon, :>=, -180 * GeoRecord::SCALE, "Minimum longitude (#{cs.min_lon / GeoRecord::SCALE}) should be >= -180 to be valid."
- assert_operator cs.max_lon, :<=, 180 * GeoRecord::SCALE, "Maximum longitude (#{cs.max_lon / GeoRecord::SCALE}) should be <= 180 to be valid."
- assert_operator cs.min_lat, :>=, -90 * GeoRecord::SCALE, "Minimum latitude (#{cs.min_lat / GeoRecord::SCALE}) should be >= -90 to be valid."
- assert_operator cs.max_lat, :<=, 90 * GeoRecord::SCALE, "Maximum latitude (#{cs.max_lat / GeoRecord::SCALE}) should be <= 90 to be valid."
- end
-
- ##
- # test what happens if a diff is uploaded containing only a node
- # move.
- def test_upload_node_move
- auth_header = bearer_authorization_header
-
- xml = "<osm><changeset>" \
- "<tag k='created_by' v='osm test suite checking changesets'/>" \
- "</changeset></osm>"
- post api_changesets_path, :params => xml, :headers => auth_header
- assert_response :success
- changeset_id = @response.body.to_i
-
- old_node = create(:node, :lat => 1, :lon => 1)
-
- diff = XML::Document.new
- diff.root = XML::Node.new "osmChange"
- modify = XML::Node.new "modify"
- xml_old_node = xml_node_for_node(old_node)
- xml_old_node["lat"] = 2.0.to_s
- xml_old_node["lon"] = 2.0.to_s
- xml_old_node["changeset"] = changeset_id.to_s
- modify << xml_old_node
- diff.root << modify
-
- # upload it
- post api_changeset_upload_path(changeset_id), :params => diff.to_s, :headers => auth_header
- assert_response :success,
- "diff should have uploaded OK"
-
- # check the bbox
- changeset = Changeset.find(changeset_id)
- assert_equal 1 * GeoRecord::SCALE, changeset.min_lon, "min_lon should be 1 degree"
- assert_equal 2 * GeoRecord::SCALE, changeset.max_lon, "max_lon should be 2 degrees"
- assert_equal 1 * GeoRecord::SCALE, changeset.min_lat, "min_lat should be 1 degree"
- assert_equal 2 * GeoRecord::SCALE, changeset.max_lat, "max_lat should be 2 degrees"
- end
-
- ##
- # test what happens if a diff is uploaded adding a node to a way.
- def test_upload_way_extend
- auth_header = bearer_authorization_header
-
- xml = "<osm><changeset>" \
- "<tag k='created_by' v='osm test suite checking changesets'/>" \
- "</changeset></osm>"
- post api_changesets_path, :params => xml, :headers => auth_header
- assert_response :success
- changeset_id = @response.body.to_i
-
- old_way = create(:way)
- create(:way_node, :way => old_way, :node => create(:node, :lat => 0.1, :lon => 0.1))
-
- diff = XML::Document.new
- diff.root = XML::Node.new "osmChange"
- modify = XML::Node.new "modify"
- xml_old_way = xml_node_for_way(old_way)
- nd_ref = XML::Node.new "nd"
- nd_ref["ref"] = create(:node, :lat => 0.3, :lon => 0.3).id.to_s
- xml_old_way << nd_ref
- xml_old_way["changeset"] = changeset_id.to_s
- modify << xml_old_way
- diff.root << modify
-
- # upload it
- post api_changeset_upload_path(changeset_id), :params => diff.to_s, :headers => auth_header
- assert_response :success,
- "diff should have uploaded OK"
-
- # check the bbox
- changeset = Changeset.find(changeset_id)
- assert_equal 0.1 * GeoRecord::SCALE, changeset.min_lon, "min_lon should be 0.1 degree"
- assert_equal 0.3 * GeoRecord::SCALE, changeset.max_lon, "max_lon should be 0.3 degrees"
- assert_equal 0.1 * GeoRecord::SCALE, changeset.min_lat, "min_lat should be 0.1 degree"
- assert_equal 0.3 * GeoRecord::SCALE, changeset.max_lat, "max_lat should be 0.3 degrees"
- end
-
- ##
- # test for more issues in #1568
- def test_upload_empty_invalid
- changeset = create(:changeset)
-
- auth_header = bearer_authorization_header changeset.user
-
- ["<osmChange/>",
- "<osmChange></osmChange>",
- "<osmChange><modify/></osmChange>",
- "<osmChange><modify></modify></osmChange>"].each do |diff|
- # upload it
- post api_changeset_upload_path(changeset), :params => diff, :headers => auth_header
- assert_response(:success, "should be able to upload " \
- "empty changeset: " + diff)
- end
- end
-
- ##
- # test that the X-Error-Format header works to request XML errors
- def test_upload_xml_errors
- changeset = create(:changeset)
- node = create(:node)
- create(:relation_member, :member => node)
-
- auth_header = bearer_authorization_header changeset.user
-
- # try and delete a node that is in use
- diff = XML::Document.new
- diff.root = XML::Node.new "osmChange"
- delete = XML::Node.new "delete"
- diff.root << delete
- delete << xml_node_for_node(node)
-
- # upload it
- error_header = error_format_header "xml"
- post api_changeset_upload_path(changeset), :params => diff.to_s, :headers => auth_header.merge(error_header)
- assert_response :success,
- "failed to return error in XML format"
-
- # check the returned payload
- assert_select "osmError[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
- assert_select "osmError>status", 1
- assert_select "osmError>message", 1
- end
-
- def test_upload_not_found
- changeset = create(:changeset)
-
- auth_header = bearer_authorization_header changeset.user
-
- # modify node
- diff = <<~CHANGESET
- <osmChange>
- <modify>
- <node id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
- </modify>
- </osmChange>
- CHANGESET
-
- # upload it
- post api_changeset_upload_path(changeset), :params => diff, :headers => auth_header
- assert_response :not_found, "Node should not be found"
-
- # modify way
- diff = <<~CHANGESET
- <osmChange>
- <modify>
- <way id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
- </modify>
- </osmChange>
- CHANGESET
-
- # upload it
- post api_changeset_upload_path(changeset), :params => diff, :headers => auth_header
- assert_response :not_found, "Way should not be found"
-
- # modify relation
- diff = <<~CHANGESET
- <osmChange>
- <modify>
- <relation id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
- </modify>
- </osmChange>
- CHANGESET
-
- # upload it
- post api_changeset_upload_path(changeset), :params => diff, :headers => auth_header
- assert_response :not_found, "Relation should not be found"
-
- # delete node
- diff = <<~CHANGESET
- <osmChange>
- <delete>
- <node id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
- </delete>
- </osmChange>
- CHANGESET
-
- # upload it
- post api_changeset_upload_path(changeset), :params => diff, :headers => auth_header
- assert_response :not_found, "Node should not be deleted"
-
- # delete way
- diff = <<~CHANGESET
- <osmChange>
- <delete>
- <way id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
- </delete>
- </osmChange>
- CHANGESET
-
- # upload it
- post api_changeset_upload_path(changeset), :params => diff, :headers => auth_header
- assert_response :not_found, "Way should not be deleted"
-
- # delete relation
- diff = <<~CHANGESET
- <osmChange>
- <delete>
- <relation id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
- </delete>
- </osmChange>
- CHANGESET
-
- # upload it
- post api_changeset_upload_path(changeset), :params => diff, :headers => auth_header
- assert_response :not_found, "Relation should not be deleted"
- end
-
- ##
- # test initial rate limit
- def test_upload_initial_rate_limit
- # create a user
- user = create(:user)
-
- # create some objects to use
- node = create(:node)
- way = create(:way_with_nodes, :nodes_count => 2)
- relation = create(:relation)
-
- # create a changeset that puts us near the initial rate limit
- changeset = create(:changeset, :user => user,
- :created_at => Time.now.utc - 5.minutes,
- :num_changes => Settings.initial_changes_per_hour - 2)
-
- # create authentication header
- auth_header = bearer_authorization_header user
-
- # simple diff to create a node way and relation using placeholders
- diff = <<~CHANGESET
- <osmChange>
- <create>
- <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
- <tag k='foo' v='bar'/>
- <tag k='baz' v='bat'/>
- </node>
- <way id='-1' changeset='#{changeset.id}'>
- <nd ref='#{node.id}'/>
- </way>
- </create>
- <create>
- <relation id='-1' changeset='#{changeset.id}'>
- <member type='way' role='some' ref='#{way.id}'/>
- <member type='node' role='some' ref='#{node.id}'/>
- <member type='relation' role='some' ref='#{relation.id}'/>
- </relation>
- </create>
- </osmChange>
- CHANGESET
-
- # upload it
- post api_changeset_upload_path(changeset), :params => diff, :headers => auth_header
- assert_response :too_many_requests, "upload did not hit rate limit"
- end
-
- ##
- # test maximum rate limit
- def test_upload_maximum_rate_limit
- # create a user
- user = create(:user)
-
- # create some objects to use
- node = create(:node)
- way = create(:way_with_nodes, :nodes_count => 2)
- relation = create(:relation)
-
- # create a changeset to establish our initial edit time
- changeset = create(:changeset, :user => user,
- :created_at => Time.now.utc - 28.days)
-
- # create changeset to put us near the maximum rate limit
- total_changes = Settings.max_changes_per_hour - 2
- while total_changes.positive?
- changes = [total_changes, Changeset::MAX_ELEMENTS].min
- changeset = create(:changeset, :user => user,
- :created_at => Time.now.utc - 5.minutes,
- :num_changes => changes)
- total_changes -= changes
- end
-
- # create authentication header
- auth_header = bearer_authorization_header user
-
- # simple diff to create a node way and relation using placeholders
- diff = <<~CHANGESET
- <osmChange>
- <create>
- <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
- <tag k='foo' v='bar'/>
- <tag k='baz' v='bat'/>
- </node>
- <way id='-1' changeset='#{changeset.id}'>
- <nd ref='#{node.id}'/>
- </way>
- </create>
- <create>
- <relation id='-1' changeset='#{changeset.id}'>
- <member type='way' role='some' ref='#{way.id}'/>
- <member type='node' role='some' ref='#{node.id}'/>
- <member type='relation' role='some' ref='#{relation.id}'/>
- </relation>
- </create>
- </osmChange>
- CHANGESET
-
- # upload it
- post api_changeset_upload_path(changeset), :params => diff, :headers => auth_header
- assert_response :too_many_requests, "upload did not hit rate limit"
- end
-
- ##
- # test initial size limit
- def test_upload_initial_size_limit
- # create a user
- user = create(:user)
-
- # create a changeset that puts us near the initial size limit
- changeset = create(:changeset, :user => user,
- :min_lat => (-0.5 * GeoRecord::SCALE).round, :min_lon => (0.5 * GeoRecord::SCALE).round,
- :max_lat => (0.5 * GeoRecord::SCALE).round, :max_lon => (2.5 * GeoRecord::SCALE).round)
-
- # create authentication header
- auth_header = bearer_authorization_header user
-
- # simple diff to create a node
- diff = <<~CHANGESET
- <osmChange>
- <create>
- <node id='-1' lon='0.9' lat='2.9' changeset='#{changeset.id}'>
- <tag k='foo' v='bar'/>
- <tag k='baz' v='bat'/>
- </node>
- </create>
- </osmChange>
- CHANGESET
-
- # upload it
- post api_changeset_upload_path(changeset), :params => diff, :headers => auth_header
- assert_response :payload_too_large, "upload did not hit size limit"
- end
-
- ##
- # test size limit after one week
- def test_upload_week_size_limit
- # create a user
- user = create(:user)
-
- # create a changeset to establish our initial edit time
- create(:changeset, :user => user, :created_at => Time.now.utc - 7.days)
-
- # create a changeset that puts us near the initial size limit
- changeset = create(:changeset, :user => user, :bbox => [0.5, -0.5, 2.5, 0.5])
-
- # create authentication header
- auth_header = bearer_authorization_header user
-
- # simple diff to create a node way and relation using placeholders
- diff = <<~CHANGESET
- <osmChange>
- <create>
- <node id='-1' lon='35' lat='35' changeset='#{changeset.id}'>
- <tag k='foo' v='bar'/>
- <tag k='baz' v='bat'/>
- </node>
- </create>
- </osmChange>
- CHANGESET
-
- # upload it
- post api_changeset_upload_path(changeset), :params => diff, :headers => auth_header
- assert_response :payload_too_large, "upload did not hit size limit"
- end
-
- ##
- # when we make some simple changes we get the same changes back from the
- # diff download.
- def test_diff_download_simple
- node = create(:node)
-
- ## First try with a non-public user, which should get a forbidden
- auth_header = bearer_authorization_header create(:user, :data_public => false)
-
- # create a temporary changeset
- xml = "<osm><changeset>" \
- "<tag k='created_by' v='osm test suite checking changesets'/>" \
- "</changeset></osm>"
- post api_changesets_path, :params => xml, :headers => auth_header
- assert_response :forbidden
-
- ## Now try with a normal user
- auth_header = bearer_authorization_header
-
- # create a temporary changeset
- xml = "<osm><changeset>" \
- "<tag k='created_by' v='osm test suite checking changesets'/>" \
- "</changeset></osm>"
- post api_changesets_path, :params => xml, :headers => auth_header
- assert_response :success
- changeset_id = @response.body.to_i
-
- # add a diff to it
- diff = <<~CHANGESET
- <osmChange>
- <modify>
- <node id='#{node.id}' lon='0.0' lat='0.0' changeset='#{changeset_id}' version='1'/>
- <node id='#{node.id}' lon='0.1' lat='0.0' changeset='#{changeset_id}' version='2'/>
- <node id='#{node.id}' lon='0.1' lat='0.1' changeset='#{changeset_id}' version='3'/>
- <node id='#{node.id}' lon='0.1' lat='0.2' changeset='#{changeset_id}' version='4'/>
- <node id='#{node.id}' lon='0.2' lat='0.2' changeset='#{changeset_id}' version='5'/>
- <node id='#{node.id}' lon='0.3' lat='0.2' changeset='#{changeset_id}' version='6'/>
- <node id='#{node.id}' lon='0.3' lat='0.3' changeset='#{changeset_id}' version='7'/>
- <node id='#{node.id}' lon='0.9' lat='0.9' changeset='#{changeset_id}' version='8'/>
- </modify>
- </osmChange>
- CHANGESET
-
- # upload it
- post api_changeset_upload_path(changeset_id), :params => diff, :headers => auth_header
- assert_response :success,
- "can't upload multiple versions of an element in a diff: #{@response.body}"
-
- get api_changeset_download_path(changeset_id)
- assert_response :success
-
- assert_select "osmChange", 1
- assert_select "osmChange>modify", 8
- assert_select "osmChange>modify>node", 8
- end
-
- ##
- # culled this from josm to ensure that nothing in the way that josm
- # is formatting the request is causing it to fail.
- #
- # NOTE: the error turned out to be something else completely!
- def test_josm_upload
- auth_header = bearer_authorization_header
-
- # create a temporary changeset
- xml = "<osm><changeset>" \
- "<tag k='created_by' v='osm test suite checking changesets'/>" \
- "</changeset></osm>"
- post api_changesets_path, :params => xml, :headers => auth_header
- assert_response :success
- changeset_id = @response.body.to_i
-
- diff = <<~OSMFILE
- <osmChange version="0.6" generator="JOSM">
- <create version="0.6" generator="JOSM">
- <node id='-1' visible='true' changeset='#{changeset_id}' lat='51.49619982187321' lon='-0.18722061869438314' />
- <node id='-2' visible='true' changeset='#{changeset_id}' lat='51.496359883909605' lon='-0.18653093576241928' />
- <node id='-3' visible='true' changeset='#{changeset_id}' lat='51.49598132358285' lon='-0.18719613290981638' />
- <node id='-4' visible='true' changeset='#{changeset_id}' lat='51.4961591711078' lon='-0.18629015888084607' />
- <node id='-5' visible='true' changeset='#{changeset_id}' lat='51.49582126021711' lon='-0.18708186591517145' />
- <node id='-6' visible='true' changeset='#{changeset_id}' lat='51.49591018437858' lon='-0.1861432441734455' />
- <node id='-7' visible='true' changeset='#{changeset_id}' lat='51.49560784152179' lon='-0.18694719410005425' />
- <node id='-8' visible='true' changeset='#{changeset_id}' lat='51.49567389979617' lon='-0.1860289771788006' />
- <node id='-9' visible='true' changeset='#{changeset_id}' lat='51.49543761398892' lon='-0.186820684213126' />
- <way id='-10' action='modify' visible='true' changeset='#{changeset_id}'>
- <nd ref='-1' />
- <nd ref='-2' />
- <nd ref='-3' />
- <nd ref='-4' />
- <nd ref='-5' />
- <nd ref='-6' />
- <nd ref='-7' />
- <nd ref='-8' />
- <nd ref='-9' />
- <tag k='highway' v='residential' />
- <tag k='name' v='Foobar Street' />
- </way>
- </create>
- </osmChange>
- OSMFILE
-
- # upload it
- post api_changeset_upload_path(changeset_id), :params => diff, :headers => auth_header
- assert_response :success,
- "can't upload a diff from JOSM: #{@response.body}"
-
- get api_changeset_download_path(changeset_id)
- assert_response :success
-
- assert_select "osmChange", 1
- assert_select "osmChange>create>node", 9
- assert_select "osmChange>create>way", 1
- assert_select "osmChange>create>way>nd", 9
- assert_select "osmChange>create>way>tag", 2
- end
-
- ##
- # when we make some complex changes we get the same changes back from the
- # diff download.
- def test_diff_download_complex
- node = create(:node)
- node2 = create(:node)
- way = create(:way)
- auth_header = bearer_authorization_header
-
- # create a temporary changeset
- xml = "<osm><changeset>" \
- "<tag k='created_by' v='osm test suite checking changesets'/>" \
- "</changeset></osm>"
- post api_changesets_path, :params => xml, :headers => auth_header
- assert_response :success
- changeset_id = @response.body.to_i
-
- # add a diff to it
- diff = <<~CHANGESET
- <osmChange>
- <delete>
- <node id='#{node.id}' lon='0.0' lat='0.0' changeset='#{changeset_id}' version='1'/>
- </delete>
- <create>
- <node id='-1' lon='0.9' lat='0.9' changeset='#{changeset_id}' version='0'/>
- <node id='-2' lon='0.8' lat='0.9' changeset='#{changeset_id}' version='0'/>
- <node id='-3' lon='0.7' lat='0.9' changeset='#{changeset_id}' version='0'/>
- </create>
- <modify>
- <node id='#{node2.id}' lon='2.0' lat='1.5' changeset='#{changeset_id}' version='1'/>
- <way id='#{way.id}' changeset='#{changeset_id}' version='1'>
- <nd ref='#{node2.id}'/>
- <nd ref='-1'/>
- <nd ref='-2'/>
- <nd ref='-3'/>
- </way>
- </modify>
- </osmChange>
- CHANGESET
-
- # upload it
- post api_changeset_upload_path(changeset_id), :params => diff, :headers => auth_header
- assert_response :success,
- "can't upload multiple versions of an element in a diff: #{@response.body}"
-
- get api_changeset_download_path(changeset_id)
- assert_response :success
-
- assert_select "osmChange", 1
- assert_select "osmChange>create", 3
- assert_select "osmChange>delete", 1
- assert_select "osmChange>modify", 2
- assert_select "osmChange>create>node", 3
- assert_select "osmChange>delete>node", 1
- assert_select "osmChange>modify>node", 1
- assert_select "osmChange>modify>way", 1
- end
-
##
# check that the bounding box of a changeset gets updated correctly
# FIXME: This should really be moded to a integration test due to the with_controller