1 # frozen_string_literal: true
 
   5 class ChangesetBboxTest < ActionDispatch::IntegrationTest
 
   7   # check that the bounding box of a changeset gets updated correctly
 
   8   def test_changeset_bbox
 
  10     create(:way_node, :way => way, :node => create(:node, :lat => 0.3, :lon => 0.3))
 
  12     auth_header = bearer_authorization_header
 
  14     # create a new changeset
 
  15     xml = "<osm><changeset/></osm>"
 
  16     post api_changesets_path, :params => xml, :headers => auth_header
 
  17     assert_response :success, "Creating of changeset failed."
 
  18     changeset_id = @response.body.to_i
 
  20     # add a single node to it
 
  21     with_controller(NodesController.new) do
 
  22       xml = "<osm><node lon='0.1' lat='0.2' changeset='#{changeset_id}'/></osm>"
 
  23       post api_nodes_path, :params => xml, :headers => auth_header
 
  24       assert_response :success, "Couldn't create node."
 
  27     # get the bounding box back from the changeset
 
  28     get api_changeset_path(changeset_id)
 
  29     assert_response :success, "Couldn't read back changeset."
 
  30     assert_dom "osm>changeset[min_lon='0.1000000']", 1
 
  31     assert_dom "osm>changeset[max_lon='0.1000000']", 1
 
  32     assert_dom "osm>changeset[min_lat='0.2000000']", 1
 
  33     assert_dom "osm>changeset[max_lat='0.2000000']", 1
 
  35     # add another node to it
 
  36     with_controller(NodesController.new) do
 
  37       xml = "<osm><node lon='0.2' lat='0.1' changeset='#{changeset_id}'/></osm>"
 
  38       post api_nodes_path, :params => xml, :headers => auth_header
 
  39       assert_response :success, "Couldn't create second node."
 
  42     # get the bounding box back from the changeset
 
  43     get api_changeset_path(changeset_id)
 
  44     assert_response :success, "Couldn't read back changeset for the second time."
 
  45     assert_dom "osm>changeset[min_lon='0.1000000']", 1
 
  46     assert_dom "osm>changeset[max_lon='0.2000000']", 1
 
  47     assert_dom "osm>changeset[min_lat='0.1000000']", 1
 
  48     assert_dom "osm>changeset[max_lat='0.2000000']", 1
 
  50     # add (delete) a way to it, which contains a point at (3,3)
 
  51     with_controller(WaysController.new) do
 
  52       xml = update_changeset(xml_for_way(way), changeset_id)
 
  53       delete api_way_path(way), :params => xml.to_s, :headers => auth_header
 
  54       assert_response :success, "Couldn't delete a way."
 
  57     # get the bounding box back from the changeset
 
  58     get api_changeset_path(changeset_id)
 
  59     assert_response :success, "Couldn't read back changeset for the third time."
 
  60     assert_dom "osm>changeset[min_lon='0.1000000']", 1
 
  61     assert_dom "osm>changeset[max_lon='0.3000000']", 1
 
  62     assert_dom "osm>changeset[min_lat='0.1000000']", 1
 
  63     assert_dom "osm>changeset[max_lat='0.3000000']", 1
 
  67   # when a relation's tag is modified then it should put the bounding
 
  68   # box of all its members into the changeset.
 
  69   def test_relation_tag_modify_bounding_box
 
  70     relation = create(:relation)
 
  71     node1 = create(:node, :lat => 0.3, :lon => 0.3)
 
  72     node2 = create(:node, :lat => 0.5, :lon => 0.5)
 
  74     create(:way_node, :way => way, :node => node1)
 
  75     create(:relation_member, :relation => relation, :member => way)
 
  76     create(:relation_member, :relation => relation, :member => node2)
 
  77     # the relation contains nodes1 and node2 (node1
 
  78     # indirectly via the way), so the bbox should be [0.3,0.3,0.5,0.5].
 
  79     check_changeset_modify(BoundingBox.new(0.3, 0.3, 0.5, 0.5)) do |changeset_id, auth_header|
 
  80       # add a tag to an existing relation
 
  81       relation_xml = xml_for_relation(relation)
 
  82       relation_element = relation_xml.find("//osm/relation").first
 
  83       new_tag = XML::Node.new("tag")
 
  84       new_tag["k"] = "some_new_tag"
 
  85       new_tag["v"] = "some_new_value"
 
  86       relation_element << new_tag
 
  88       # update changeset ID to point to new changeset
 
  89       update_changeset(relation_xml, changeset_id)
 
  92       put api_relation_path(relation), :params => relation_xml.to_s, :headers => auth_header
 
  93       assert_response :success, "can't update relation for tag/bbox test"
 
  98   # add a member to a relation and check the bounding box is only that
 
 100   def test_relation_add_member_bounding_box
 
 101     relation = create(:relation)
 
 102     node1 = create(:node, :lat => 4, :lon => 4)
 
 103     node2 = create(:node, :lat => 7, :lon => 7)
 
 105     create(:way_node, :way => way1, :node => create(:node, :lat => 8, :lon => 8))
 
 107     create(:way_node, :way => way2, :node => create(:node, :lat => 9, :lon => 9), :sequence_id => 1)
 
 108     create(:way_node, :way => way2, :node => create(:node, :lat => 10, :lon => 10), :sequence_id => 2)
 
 110     [node1, node2, way1, way2].each do |element|
 
 111       bbox = element.bbox.to_unscaled
 
 112       check_changeset_modify(bbox) do |changeset_id, auth_header|
 
 113         relation_xml = xml_for_relation(Relation.find(relation.id))
 
 114         relation_element = relation_xml.find("//osm/relation").first
 
 115         new_member = XML::Node.new("member")
 
 116         new_member["ref"] = element.id.to_s
 
 117         new_member["type"] = element.class.to_s.downcase
 
 118         new_member["role"] = "some_role"
 
 119         relation_element << new_member
 
 121         # update changeset ID to point to new changeset
 
 122         update_changeset(relation_xml, changeset_id)
 
 125         put api_relation_path(relation), :params => relation_xml.to_s, :headers => auth_header
 
 126         assert_response :success, "can't update relation for add #{element.class}/bbox test: #{@response.body}"
 
 128         # get it back and check the ordering
 
 129         get api_relation_path(relation)
 
 130         assert_members_equal_response relation_xml
 
 136   # remove a member from a relation and check the bounding box is
 
 138   def test_relation_remove_member_bounding_box
 
 139     relation = create(:relation)
 
 140     node1 = create(:node, :lat => 3, :lon => 3)
 
 141     node2 = create(:node, :lat => 5, :lon => 5)
 
 142     create(:relation_member, :relation => relation, :member => node1)
 
 143     create(:relation_member, :relation => relation, :member => node2)
 
 145     check_changeset_modify(BoundingBox.new(5, 5, 5, 5)) do |changeset_id, auth_header|
 
 146       # remove node 5 (5,5) from an existing relation
 
 147       relation_xml = xml_for_relation(relation)
 
 149         .find("//osm/relation/member[@type='node'][@ref='#{node2.id}']")
 
 152       # update changeset ID to point to new changeset
 
 153       update_changeset(relation_xml, changeset_id)
 
 156       put api_relation_path(relation), :params => relation_xml.to_s, :headers => auth_header
 
 157       assert_response :success, "can't update relation for remove node/bbox test"
 
 162   # remove all the members from a relation. the result is pretty useless, but
 
 163   # still technically valid.
 
 164   def test_relation_remove_all_members
 
 165     relation = create(:relation)
 
 166     node1 = create(:node, :lat => 0.3, :lon => 0.3)
 
 167     node2 = create(:node, :lat => 0.5, :lon => 0.5)
 
 169     create(:way_node, :way => way, :node => node1)
 
 170     create(:relation_member, :relation => relation, :member => way)
 
 171     create(:relation_member, :relation => relation, :member => node2)
 
 173     check_changeset_modify(BoundingBox.new(0.3, 0.3, 0.5, 0.5)) do |changeset_id, auth_header|
 
 174       relation_xml = xml_for_relation(relation)
 
 176         .find("//osm/relation/member")
 
 179       # update changeset ID to point to new changeset
 
 180       update_changeset(relation_xml, changeset_id)
 
 183       put api_relation_path(relation), :params => relation_xml.to_s, :headers => auth_header
 
 184       assert_response :success, "can't update relation for remove all members test"
 
 185       checkrelation = Relation.find(relation.id)
 
 186       assert_not_nil(checkrelation,
 
 187                      "uploaded relation not found in database after upload")
 
 188       assert_equal(0, checkrelation.members.length,
 
 189                    "relation contains members but they should have all been deleted")
 
 196   # create a changeset and yield to the caller to set it up, then assert
 
 197   # that the changeset bounding box is +bbox+.
 
 198   def check_changeset_modify(bbox)
 
 199     ## First test with the private user to check that you get a forbidden
 
 200     auth_header = bearer_authorization_header create(:user, :data_public => false)
 
 202     # create a new changeset for this operation, so we are assured
 
 203     # that the bounding box will be newly-generated.
 
 204     with_controller(Api::ChangesetsController.new) do
 
 205       xml = "<osm><changeset/></osm>"
 
 206       post api_changesets_path, :params => xml, :headers => auth_header
 
 207       assert_response :forbidden, "shouldn't be able to create changeset for modify test, as should get forbidden"
 
 210     ## Now do the whole thing with the public user
 
 211     auth_header = bearer_authorization_header
 
 213     # create a new changeset for this operation, so we are assured
 
 214     # that the bounding box will be newly-generated.
 
 215     changeset_id = with_controller(Api::ChangesetsController.new) do
 
 216       xml = "<osm><changeset/></osm>"
 
 217       post api_changesets_path, :params => xml, :headers => auth_header
 
 218       assert_response :success, "couldn't create changeset for modify test"
 
 222     # go back to the block to do the actual modifies
 
 223     yield changeset_id, auth_header
 
 225     # now download the changeset to check its bounding box
 
 226     with_controller(Api::ChangesetsController.new) do
 
 227       get api_changeset_path(changeset_id)
 
 228       assert_response :success, "can't re-read changeset for modify test"
 
 229       assert_select "osm>changeset", 1, "Changeset element doesn't exist in #{@response.body}"
 
 230       assert_select "osm>changeset[id='#{changeset_id}']", 1, "Changeset id=#{changeset_id} doesn't exist in #{@response.body}"
 
 231       assert_select "osm>changeset[min_lon='#{format('%<lon>.7f', :lon => bbox.min_lon)}']", 1, "Changeset min_lon wrong in #{@response.body}"
 
 232       assert_select "osm>changeset[min_lat='#{format('%<lat>.7f', :lat => bbox.min_lat)}']", 1, "Changeset min_lat wrong in #{@response.body}"
 
 233       assert_select "osm>changeset[max_lon='#{format('%<lon>.7f', :lon => bbox.max_lon)}']", 1, "Changeset max_lon wrong in #{@response.body}"
 
 234       assert_select "osm>changeset[max_lat='#{format('%<lat>.7f', :lat => bbox.max_lat)}']", 1, "Changeset max_lat wrong in #{@response.body}"
 
 239   # checks that the XML document and the response have
 
 240   # members in the same order.
 
 241   def assert_members_equal_response(doc, response_message = "can't read back the relation")
 
 242     assert_response :success, "#{response_message}: #{@response.body}"
 
 243     new_doc = XML::Parser.string(@response.body).parse
 
 245     doc_members = doc.find("//osm/relation/member").collect do |m|
 
 246       [m["ref"].to_i, m["type"].to_sym, m["role"]]
 
 249     new_members = new_doc.find("//osm/relation/member").collect do |m|
 
 250       [m["ref"].to_i, m["type"].to_sym, m["role"]]
 
 253     assert_equal doc_members, new_members, "members are not equal - ordering is wrong? (#{doc}, #{@response.body})"
 
 257   # update the changeset_id of an element
 
 258   def update_changeset(xml, changeset_id)
 
 259     xml_attr_rewrite(xml, "changeset", changeset_id)
 
 263   # update an attribute in an element
 
 264   def xml_attr_rewrite(xml, name, value)
 
 265     xml.find("//osm/*[self::node or self::way or self::relation]").first[name] = value.to_s