4   class RelationsControllerTest < ActionDispatch::IntegrationTest
 
   6     # test all routes which lead to this controller
 
   9         { :path => "/api/0.6/relations", :method => :get },
 
  10         { :controller => "api/relations", :action => "index" }
 
  13         { :path => "/api/0.6/relations.json", :method => :get },
 
  14         { :controller => "api/relations", :action => "index", :format => "json" }
 
  17         { :path => "/api/0.6/relations", :method => :post },
 
  18         { :controller => "api/relations", :action => "create" }
 
  21         { :path => "/api/0.6/relation/1", :method => :get },
 
  22         { :controller => "api/relations", :action => "show", :id => "1" }
 
  25         { :path => "/api/0.6/relation/1.json", :method => :get },
 
  26         { :controller => "api/relations", :action => "show", :id => "1", :format => "json" }
 
  29         { :path => "/api/0.6/relation/1/full", :method => :get },
 
  30         { :controller => "api/relations", :action => "show", :full => true, :id => "1" }
 
  33         { :path => "/api/0.6/relation/1/full.json", :method => :get },
 
  34         { :controller => "api/relations", :action => "show", :full => true, :id => "1", :format => "json" }
 
  37         { :path => "/api/0.6/relation/1", :method => :put },
 
  38         { :controller => "api/relations", :action => "update", :id => "1" }
 
  41         { :path => "/api/0.6/relation/1", :method => :delete },
 
  42         { :controller => "api/relations", :action => "destroy", :id => "1" }
 
  46         { :controller => "api/relations", :action => "create" },
 
  47         { :path => "/api/0.6/relation/create", :method => :put }
 
  52     # test fetching multiple relations
 
  54       relation1 = create(:relation)
 
  55       relation2 = create(:relation, :deleted)
 
  56       relation3 = create(:relation, :with_history, :version => 2)
 
  57       relation4 = create(:relation, :with_history, :version => 2)
 
  58       relation4.old_relations.find_by(:version => 1).redact!(create(:redaction))
 
  60       # check error when no parameter provided
 
  61       get api_relations_path
 
  62       assert_response :bad_request
 
  64       # check error when no parameter value provided
 
  65       get api_relations_path(:relations => "")
 
  66       assert_response :bad_request
 
  69       get api_relations_path(:relations => "#{relation1.id},#{relation2.id},#{relation3.id},#{relation4.id}")
 
  70       assert_response :success
 
  71       assert_select "osm" do
 
  72         assert_select "relation", :count => 4
 
  73         assert_select "relation[id='#{relation1.id}'][visible='true']", :count => 1
 
  74         assert_select "relation[id='#{relation2.id}'][visible='false']", :count => 1
 
  75         assert_select "relation[id='#{relation3.id}'][visible='true']", :count => 1
 
  76         assert_select "relation[id='#{relation4.id}'][visible='true']", :count => 1
 
  79       # test a working call with json format
 
  80       get api_relations_path(:relations => "#{relation1.id},#{relation2.id},#{relation3.id},#{relation4.id}", :format => "json")
 
  82       js = ActiveSupport::JSON.decode(@response.body)
 
  84       assert_equal 4, js["elements"].count
 
  85       assert_equal 4, (js["elements"].count { |a| a["type"] == "relation" })
 
  86       assert_equal 1, (js["elements"].count { |a| a["id"] == relation1.id && a["visible"].nil? })
 
  87       assert_equal 1, (js["elements"].count { |a| a["id"] == relation2.id && a["visible"] == false })
 
  88       assert_equal 1, (js["elements"].count { |a| a["id"] == relation3.id && a["visible"].nil? })
 
  89       assert_equal 1, (js["elements"].count { |a| a["id"] == relation4.id && a["visible"].nil? })
 
  91       # check error when a non-existent relation is included
 
  92       get api_relations_path(:relations => "#{relation1.id},#{relation2.id},#{relation3.id},#{relation4.id},0")
 
  93       assert_response :not_found
 
  96     # -------------------------------------
 
  97     # Test showing relations.
 
  98     # -------------------------------------
 
 100     def test_show_not_found
 
 101       get api_relation_path(0)
 
 102       assert_response :not_found
 
 105     def test_show_deleted
 
 106       get api_relation_path(create(:relation, :deleted))
 
 107       assert_response :gone
 
 111       relation = create(:relation, :timestamp => "2021-02-03T00:00:00Z")
 
 112       node = create(:node, :timestamp => "2021-04-05T00:00:00Z")
 
 113       create(:relation_member, :relation => relation, :member => node)
 
 115       get api_relation_path(relation)
 
 117       assert_response :success
 
 118       assert_not_nil @response.header["Last-Modified"]
 
 119       assert_equal "2021-02-03T00:00:00Z", Time.parse(@response.header["Last-Modified"]).utc.xmlschema
 
 120       assert_dom "node", :count => 0
 
 121       assert_dom "relation", :count => 1 do
 
 122         assert_dom "> @id", :text => relation.id.to_s
 
 126     def test_full_not_found
 
 127       get api_relation_path(999999, :full => true)
 
 128       assert_response :not_found
 
 131     def test_full_deleted
 
 132       get api_relation_path(create(:relation, :deleted), :full => true)
 
 133       assert_response :gone
 
 137       relation = create(:relation)
 
 139       get api_relation_path(relation, :full => true)
 
 141       assert_response :success
 
 142       assert_dom "relation", :count => 1 do
 
 143         assert_dom "> @id", :text => relation.id.to_s
 
 147     def test_full_with_node_member
 
 148       relation = create(:relation)
 
 150       create(:relation_member, :relation => relation, :member => node)
 
 152       get api_relation_path(relation, :full => true)
 
 154       assert_response :success
 
 155       assert_dom "node", :count => 1 do
 
 156         assert_dom "> @id", :text => node.id.to_s
 
 158       assert_dom "relation", :count => 1 do
 
 159         assert_dom "> @id", :text => relation.id.to_s
 
 163     def test_full_with_way_member
 
 164       relation = create(:relation)
 
 165       way = create(:way_with_nodes)
 
 166       create(:relation_member, :relation => relation, :member => way)
 
 168       get api_relation_path(relation, :full => true)
 
 170       assert_response :success
 
 171       assert_dom "node", :count => 1 do
 
 172         assert_dom "> @id", :text => way.nodes[0].id.to_s
 
 174       assert_dom "way", :count => 1 do
 
 175         assert_dom "> @id", :text => way.id.to_s
 
 177       assert_dom "relation", :count => 1 do
 
 178         assert_dom "> @id", :text => relation.id.to_s
 
 182     def test_full_with_node_member_json
 
 183       relation = create(:relation)
 
 185       create(:relation_member, :relation => relation, :member => node)
 
 187       get api_relation_path(relation, :full => true, :format => "json")
 
 189       assert_response :success
 
 190       js = ActiveSupport::JSON.decode(@response.body)
 
 192       assert_equal 2, js["elements"].count
 
 194       js_relations = js["elements"].filter { |e| e["type"] == "relation" }
 
 195       assert_equal 1, js_relations.count
 
 196       assert_equal relation.id, js_relations[0]["id"]
 
 197       assert_equal 1, js_relations[0]["members"].count
 
 198       assert_equal "node", js_relations[0]["members"][0]["type"]
 
 199       assert_equal node.id, js_relations[0]["members"][0]["ref"]
 
 201       js_nodes = js["elements"].filter { |e| e["type"] == "node" }
 
 202       assert_equal 1, js_nodes.count
 
 203       assert_equal node.id, js_nodes[0]["id"]
 
 206     # -------------------------------------
 
 207     # Test simple relation creation.
 
 208     # -------------------------------------
 
 211       private_user = create(:user, :data_public => false)
 
 212       private_changeset = create(:changeset, :user => private_user)
 
 214       changeset = create(:changeset, :user => user)
 
 216       way = create(:way_with_nodes, :nodes_count => 2)
 
 218       auth_header = bearer_authorization_header private_user
 
 220       # create an relation without members
 
 221       xml = "<osm><relation changeset='#{private_changeset.id}'><tag k='test' v='yes' /></relation></osm>"
 
 222       post api_relations_path, :params => xml, :headers => auth_header
 
 223       # hope for forbidden, due to user
 
 224       assert_response :forbidden,
 
 225                       "relation upload should have failed with forbidden"
 
 228       # create an relation with a node as member
 
 229       # This time try with a role attribute in the relation
 
 230       xml = "<osm><relation changeset='#{private_changeset.id}'>" \
 
 231             "<member  ref='#{node.id}' type='node' role='some'/>" \
 
 232             "<tag k='test' v='yes' /></relation></osm>"
 
 233       post api_relations_path, :params => xml, :headers => auth_header
 
 234       # hope for forbidden due to user
 
 235       assert_response :forbidden,
 
 236                       "relation upload did not return forbidden status"
 
 239       # create an relation with a node as member, this time test that we don't
 
 240       # need a role attribute to be included
 
 241       xml = "<osm><relation changeset='#{private_changeset.id}'>" \
 
 242             "<member  ref='#{node.id}' type='node'/><tag k='test' v='yes' /></relation></osm>"
 
 243       post api_relations_path, :params => xml, :headers => auth_header
 
 244       # hope for forbidden due to user
 
 245       assert_response :forbidden,
 
 246                       "relation upload did not return forbidden status"
 
 249       # create an relation with a way and a node as members
 
 250       xml = "<osm><relation changeset='#{private_changeset.id}'>" \
 
 251             "<member type='node' ref='#{node.id}' role='some'/>" \
 
 252             "<member type='way' ref='#{way.id}' role='other'/>" \
 
 253             "<tag k='test' v='yes' /></relation></osm>"
 
 254       post api_relations_path, :params => xml, :headers => auth_header
 
 255       # hope for forbidden, due to user
 
 256       assert_response :forbidden,
 
 257                       "relation upload did not return success status"
 
 259       ## Now try with the public user
 
 260       auth_header = bearer_authorization_header user
 
 262       # create an relation without members
 
 263       xml = "<osm><relation changeset='#{changeset.id}'><tag k='test' v='yes' /></relation></osm>"
 
 264       post api_relations_path, :params => xml, :headers => auth_header
 
 266       assert_response :success,
 
 267                       "relation upload did not return success status"
 
 268       # read id of created relation and search for it
 
 269       relationid = @response.body
 
 270       checkrelation = Relation.find(relationid)
 
 271       assert_not_nil checkrelation,
 
 272                      "uploaded relation not found in data base after upload"
 
 274       assert_equal(0, checkrelation.members.length, "saved relation contains members but should not")
 
 275       assert_equal(1, checkrelation.tags.length, "saved relation does not contain exactly one tag")
 
 276       assert_equal changeset.id, checkrelation.changeset.id,
 
 277                    "saved relation does not belong in the changeset it was assigned to"
 
 278       assert_equal user.id, checkrelation.changeset.user_id,
 
 279                    "saved relation does not belong to user that created it"
 
 280       assert checkrelation.visible,
 
 281              "saved relation is not visible"
 
 282       # ok the relation is there but can we also retrieve it?
 
 283       get api_relation_path(relationid)
 
 284       assert_response :success
 
 287       # create an relation with a node as member
 
 288       # This time try with a role attribute in the relation
 
 289       xml = "<osm><relation changeset='#{changeset.id}'>" \
 
 290             "<member  ref='#{node.id}' type='node' role='some'/>" \
 
 291             "<tag k='test' v='yes' /></relation></osm>"
 
 292       post api_relations_path, :params => xml, :headers => auth_header
 
 294       assert_response :success,
 
 295                       "relation upload did not return success status"
 
 296       # read id of created relation and search for it
 
 297       relationid = @response.body
 
 298       checkrelation = Relation.find(relationid)
 
 299       assert_not_nil checkrelation,
 
 300                      "uploaded relation not found in data base after upload"
 
 302       assert_equal(1, checkrelation.members.length, "saved relation does not contain exactly one member")
 
 303       assert_equal(1, checkrelation.tags.length, "saved relation does not contain exactly one tag")
 
 304       assert_equal changeset.id, checkrelation.changeset.id,
 
 305                    "saved relation does not belong in the changeset it was assigned to"
 
 306       assert_equal user.id, checkrelation.changeset.user_id,
 
 307                    "saved relation does not belong to user that created it"
 
 308       assert checkrelation.visible,
 
 309              "saved relation is not visible"
 
 310       # ok the relation is there but can we also retrieve it?
 
 312       get api_relation_path(relationid)
 
 313       assert_response :success
 
 316       # create an relation with a node as member, this time test that we don't
 
 317       # need a role attribute to be included
 
 318       xml = "<osm><relation changeset='#{changeset.id}'>" \
 
 319             "<member  ref='#{node.id}' type='node'/><tag k='test' v='yes' /></relation></osm>"
 
 320       post api_relations_path, :params => xml, :headers => auth_header
 
 322       assert_response :success,
 
 323                       "relation upload did not return success status"
 
 324       # read id of created relation and search for it
 
 325       relationid = @response.body
 
 326       checkrelation = Relation.find(relationid)
 
 327       assert_not_nil checkrelation,
 
 328                      "uploaded relation not found in data base after upload"
 
 330       assert_equal(1, checkrelation.members.length, "saved relation does not contain exactly one member")
 
 331       assert_equal(1, checkrelation.tags.length, "saved relation does not contain exactly one tag")
 
 332       assert_equal changeset.id, checkrelation.changeset.id,
 
 333                    "saved relation does not belong in the changeset it was assigned to"
 
 334       assert_equal user.id, checkrelation.changeset.user_id,
 
 335                    "saved relation does not belong to user that created it"
 
 336       assert checkrelation.visible,
 
 337              "saved relation is not visible"
 
 338       # ok the relation is there but can we also retrieve it?
 
 340       get api_relation_path(relationid)
 
 341       assert_response :success
 
 344       # create an relation with a way and a node as members
 
 345       xml = "<osm><relation changeset='#{changeset.id}'>" \
 
 346             "<member type='node' ref='#{node.id}' role='some'/>" \
 
 347             "<member type='way' ref='#{way.id}' role='other'/>" \
 
 348             "<tag k='test' v='yes' /></relation></osm>"
 
 349       post api_relations_path, :params => xml, :headers => auth_header
 
 351       assert_response :success,
 
 352                       "relation upload did not return success status"
 
 353       # read id of created relation and search for it
 
 354       relationid = @response.body
 
 355       checkrelation = Relation.find(relationid)
 
 356       assert_not_nil checkrelation,
 
 357                      "uploaded relation not found in data base after upload"
 
 359       assert_equal(2, checkrelation.members.length, "saved relation does not have exactly two members")
 
 360       assert_equal(1, checkrelation.tags.length, "saved relation does not contain exactly one tag")
 
 361       assert_equal changeset.id, checkrelation.changeset.id,
 
 362                    "saved relation does not belong in the changeset it was assigned to"
 
 363       assert_equal user.id, checkrelation.changeset.user_id,
 
 364                    "saved relation does not belong to user that created it"
 
 365       assert checkrelation.visible,
 
 366              "saved relation is not visible"
 
 367       # ok the relation is there but can we also retrieve it?
 
 368       get api_relation_path(relationid)
 
 369       assert_response :success
 
 372     # ------------------------------------
 
 373     # Test updating relations
 
 374     # ------------------------------------
 
 377     # test that, when tags are updated on a relation, the correct things
 
 378     # happen to the correct tables and the API gives sensible results.
 
 379     # this is to test a case that gregory marler noticed and posted to
 
 381     ## FIXME Move this to an integration test
 
 382     def test_update_relation_tags
 
 384       changeset = create(:changeset, :user => user)
 
 385       relation = create(:relation)
 
 386       create_list(:relation_tag, 4, :relation => relation)
 
 388       auth_header = bearer_authorization_header user
 
 390       with_relation(relation.id) do |rel|
 
 391         # alter one of the tags
 
 392         tag = rel.find("//osm/relation/tag").first
 
 393         tag["v"] = "some changed value"
 
 394         update_changeset(rel, changeset.id)
 
 396         # check that the downloaded tags are the same as the uploaded tags...
 
 397         new_version = with_update(rel, auth_header) do |new_rel|
 
 398           assert_tags_equal rel, new_rel
 
 401         # check the original one in the current_* table again
 
 402         with_relation(relation.id) { |r| assert_tags_equal rel, r }
 
 404         # now check the version in the history
 
 405         with_relation(relation.id, new_version) { |r| assert_tags_equal rel, r }
 
 410     # test that, when tags are updated on a relation when using the diff
 
 411     # upload function, the correct things happen to the correct tables
 
 412     # and the API gives sensible results. this is to test a case that
 
 413     # gregory marler noticed and posted to josm-dev.
 
 414     def test_update_relation_tags_via_upload
 
 416       changeset = create(:changeset, :user => user)
 
 417       relation = create(:relation)
 
 418       create_list(:relation_tag, 4, :relation => relation)
 
 420       auth_header = bearer_authorization_header user
 
 422       with_relation(relation.id) do |rel|
 
 423         # alter one of the tags
 
 424         tag = rel.find("//osm/relation/tag").first
 
 425         tag["v"] = "some changed value"
 
 426         update_changeset(rel, changeset.id)
 
 428         # check that the downloaded tags are the same as the uploaded tags...
 
 429         new_version = with_update_diff(rel, auth_header) do |new_rel|
 
 430           assert_tags_equal rel, new_rel
 
 433         # check the original one in the current_* table again
 
 434         with_relation(relation.id) { |r| assert_tags_equal rel, r }
 
 436         # now check the version in the history
 
 437         with_relation(relation.id, new_version) { |r| assert_tags_equal rel, r }
 
 441     def test_update_wrong_id
 
 443       changeset = create(:changeset, :user => user)
 
 444       relation = create(:relation)
 
 445       other_relation = create(:relation)
 
 447       auth_header = bearer_authorization_header user
 
 448       with_relation(relation.id) do |rel|
 
 449         update_changeset(rel, changeset.id)
 
 450         put api_relation_path(other_relation), :params => rel.to_s, :headers => auth_header
 
 451         assert_response :bad_request
 
 455     # -------------------------------------
 
 456     # Test creating some invalid relations.
 
 457     # -------------------------------------
 
 459     def test_create_invalid
 
 461       changeset = create(:changeset, :user => user)
 
 463       auth_header = bearer_authorization_header user
 
 465       # create a relation with non-existing node as member
 
 466       xml = "<osm><relation changeset='#{changeset.id}'>" \
 
 467             "<member type='node' ref='0'/><tag k='test' v='yes' />" \
 
 469       post api_relations_path, :params => xml, :headers => auth_header
 
 471       assert_response :precondition_failed,
 
 472                       "relation upload with invalid node did not return 'precondition failed'"
 
 473       assert_equal "Precondition failed: Relation with id  cannot be saved due to Node with id 0", @response.body
 
 476     # -------------------------------------
 
 477     # Test creating a relation, with some invalid XML
 
 478     # -------------------------------------
 
 479     def test_create_invalid_xml
 
 481       changeset = create(:changeset, :user => user)
 
 484       auth_header = bearer_authorization_header user
 
 486       # create some xml that should return an error
 
 487       xml = "<osm><relation changeset='#{changeset.id}'>" \
 
 488             "<member type='type' ref='#{node.id}' role=''/>" \
 
 489             "<tag k='tester' v='yep'/></relation></osm>"
 
 490       post api_relations_path, :params => xml, :headers => auth_header
 
 492       assert_response :bad_request
 
 493       assert_match(/Cannot parse valid relation from xml string/, @response.body)
 
 494       assert_match(/The type is not allowed only, /, @response.body)
 
 497     # -------------------------------------
 
 498     # Test deleting relations.
 
 499     # -------------------------------------
 
 502       private_user = create(:user, :data_public => false)
 
 503       private_user_closed_changeset = create(:changeset, :closed, :user => private_user)
 
 505       closed_changeset = create(:changeset, :closed, :user => user)
 
 506       changeset = create(:changeset, :user => user)
 
 507       relation = create(:relation)
 
 508       used_relation = create(:relation)
 
 509       super_relation = create(:relation_member, :member => used_relation).relation
 
 510       deleted_relation = create(:relation, :deleted)
 
 511       multi_tag_relation = create(:relation)
 
 512       create_list(:relation_tag, 4, :relation => multi_tag_relation)
 
 514       ## First try to delete relation without auth
 
 515       delete api_relation_path(relation)
 
 516       assert_response :unauthorized
 
 518       ## Then try with the private user, to make sure that you get a forbidden
 
 519       auth_header = bearer_authorization_header private_user
 
 521       # this shouldn't work, as we should need the payload...
 
 522       delete api_relation_path(relation), :headers => auth_header
 
 523       assert_response :forbidden
 
 525       # try to delete without specifying a changeset
 
 526       xml = "<osm><relation id='#{relation.id}'/></osm>"
 
 527       delete api_relation_path(relation), :params => xml.to_s, :headers => auth_header
 
 528       assert_response :forbidden
 
 530       # try to delete with an invalid (closed) changeset
 
 531       xml = update_changeset(xml_for_relation(relation),
 
 532                              private_user_closed_changeset.id)
 
 533       delete api_relation_path(relation), :params => xml.to_s, :headers => auth_header
 
 534       assert_response :forbidden
 
 536       # try to delete with an invalid (non-existent) changeset
 
 537       xml = update_changeset(xml_for_relation(relation), 0)
 
 538       delete api_relation_path(relation), :params => xml.to_s, :headers => auth_header
 
 539       assert_response :forbidden
 
 541       # this won't work because the relation is in-use by another relation
 
 542       xml = xml_for_relation(used_relation)
 
 543       delete api_relation_path(used_relation), :params => xml.to_s, :headers => auth_header
 
 544       assert_response :forbidden
 
 546       # this should work when we provide the appropriate payload...
 
 547       xml = xml_for_relation(relation)
 
 548       delete api_relation_path(relation), :params => xml.to_s, :headers => auth_header
 
 549       assert_response :forbidden
 
 551       # this won't work since the relation is already deleted
 
 552       xml = xml_for_relation(deleted_relation)
 
 553       delete api_relation_path(deleted_relation), :params => xml.to_s, :headers => auth_header
 
 554       assert_response :forbidden
 
 556       # this won't work since the relation never existed
 
 557       delete api_relation_path(0), :headers => auth_header
 
 558       assert_response :forbidden
 
 560       ## now set auth for the public user
 
 561       auth_header = bearer_authorization_header user
 
 563       # this shouldn't work, as we should need the payload...
 
 564       delete api_relation_path(relation), :headers => auth_header
 
 565       assert_response :bad_request
 
 567       # try to delete without specifying a changeset
 
 568       xml = "<osm><relation id='#{relation.id}' version='#{relation.version}' /></osm>"
 
 569       delete api_relation_path(relation), :params => xml.to_s, :headers => auth_header
 
 570       assert_response :bad_request
 
 571       assert_match(/Changeset id is missing/, @response.body)
 
 573       # try to delete with an invalid (closed) changeset
 
 574       xml = update_changeset(xml_for_relation(relation),
 
 576       delete api_relation_path(relation), :params => xml.to_s, :headers => auth_header
 
 577       assert_response :conflict
 
 579       # try to delete with an invalid (non-existent) changeset
 
 580       xml = update_changeset(xml_for_relation(relation), 0)
 
 581       delete api_relation_path(relation), :params => xml.to_s, :headers => auth_header
 
 582       assert_response :conflict
 
 584       # this won't work because the relation is in a changeset owned by someone else
 
 585       xml = update_changeset(xml_for_relation(relation), create(:changeset).id)
 
 586       delete api_relation_path(relation), :params => xml.to_s, :headers => auth_header
 
 587       assert_response :conflict,
 
 588                       "shouldn't be able to delete a relation in a changeset owned by someone else (#{@response.body})"
 
 590       # this won't work because the relation in the payload is different to that passed
 
 591       xml = update_changeset(xml_for_relation(relation), changeset.id)
 
 592       delete api_relation_path(create(:relation)), :params => xml.to_s, :headers => auth_header
 
 593       assert_response :bad_request, "shouldn't be able to delete a relation when payload is different to the url"
 
 595       # this won't work because the relation is in-use by another relation
 
 596       xml = update_changeset(xml_for_relation(used_relation), changeset.id)
 
 597       delete api_relation_path(used_relation), :params => xml.to_s, :headers => auth_header
 
 598       assert_response :precondition_failed,
 
 599                       "shouldn't be able to delete a relation used in a relation (#{@response.body})"
 
 600       assert_equal "Precondition failed: The relation #{used_relation.id} is used in relation #{super_relation.id}.", @response.body
 
 602       # this should work when we provide the appropriate payload...
 
 603       xml = update_changeset(xml_for_relation(multi_tag_relation), changeset.id)
 
 604       delete api_relation_path(multi_tag_relation), :params => xml.to_s, :headers => auth_header
 
 605       assert_response :success
 
 607       # valid delete should return the new version number, which should
 
 608       # be greater than the old version number
 
 609       assert_operator @response.body.to_i, :>, multi_tag_relation.version, "delete request should return a new version number for relation"
 
 611       # this won't work since the relation is already deleted
 
 612       xml = update_changeset(xml_for_relation(deleted_relation), changeset.id)
 
 613       delete api_relation_path(deleted_relation), :params => xml.to_s, :headers => auth_header
 
 614       assert_response :gone
 
 616       # Public visible relation needs to be deleted
 
 617       xml = update_changeset(xml_for_relation(super_relation), changeset.id)
 
 618       delete api_relation_path(super_relation), :params => xml.to_s, :headers => auth_header
 
 619       assert_response :success
 
 621       # this works now because the relation which was using this one
 
 623       xml = update_changeset(xml_for_relation(used_relation), changeset.id)
 
 624       delete api_relation_path(used_relation), :params => xml.to_s, :headers => auth_header
 
 625       assert_response :success,
 
 626                       "should be able to delete a relation used in an old relation (#{@response.body})"
 
 628       # this won't work since the relation never existed
 
 629       delete api_relation_path(0), :headers => auth_header
 
 630       assert_response :not_found
 
 634     # when a relation's tag is modified then it should put the bounding
 
 635     # box of all its members into the changeset.
 
 636     def test_tag_modify_bounding_box
 
 637       relation = create(:relation)
 
 638       node1 = create(:node, :lat => 0.3, :lon => 0.3)
 
 639       node2 = create(:node, :lat => 0.5, :lon => 0.5)
 
 641       create(:way_node, :way => way, :node => node1)
 
 642       create(:relation_member, :relation => relation, :member => way)
 
 643       create(:relation_member, :relation => relation, :member => node2)
 
 644       # the relation contains nodes1 and node2 (node1
 
 645       # indirectly via the way), so the bbox should be [0.3,0.3,0.5,0.5].
 
 646       check_changeset_modify(BoundingBox.new(0.3, 0.3, 0.5, 0.5)) do |changeset_id, auth_header|
 
 647         # add a tag to an existing relation
 
 648         relation_xml = xml_for_relation(relation)
 
 649         relation_element = relation_xml.find("//osm/relation").first
 
 650         new_tag = XML::Node.new("tag")
 
 651         new_tag["k"] = "some_new_tag"
 
 652         new_tag["v"] = "some_new_value"
 
 653         relation_element << new_tag
 
 655         # update changeset ID to point to new changeset
 
 656         update_changeset(relation_xml, changeset_id)
 
 659         put api_relation_path(relation), :params => relation_xml.to_s, :headers => auth_header
 
 660         assert_response :success, "can't update relation for tag/bbox test"
 
 665     # add a member to a relation and check the bounding box is only that
 
 667     def test_add_member_bounding_box
 
 668       relation = create(:relation)
 
 669       node1 = create(:node, :lat => 4, :lon => 4)
 
 670       node2 = create(:node, :lat => 7, :lon => 7)
 
 672       create(:way_node, :way => way1, :node => create(:node, :lat => 8, :lon => 8))
 
 674       create(:way_node, :way => way2, :node => create(:node, :lat => 9, :lon => 9), :sequence_id => 1)
 
 675       create(:way_node, :way => way2, :node => create(:node, :lat => 10, :lon => 10), :sequence_id => 2)
 
 677       [node1, node2, way1, way2].each do |element|
 
 678         bbox = element.bbox.to_unscaled
 
 679         check_changeset_modify(bbox) do |changeset_id, auth_header|
 
 680           relation_xml = xml_for_relation(Relation.find(relation.id))
 
 681           relation_element = relation_xml.find("//osm/relation").first
 
 682           new_member = XML::Node.new("member")
 
 683           new_member["ref"] = element.id.to_s
 
 684           new_member["type"] = element.class.to_s.downcase
 
 685           new_member["role"] = "some_role"
 
 686           relation_element << new_member
 
 688           # update changeset ID to point to new changeset
 
 689           update_changeset(relation_xml, changeset_id)
 
 692           put api_relation_path(relation), :params => relation_xml.to_s, :headers => auth_header
 
 693           assert_response :success, "can't update relation for add #{element.class}/bbox test: #{@response.body}"
 
 695           # get it back and check the ordering
 
 696           get api_relation_path(relation)
 
 697           assert_response :success, "can't read back the relation: #{@response.body}"
 
 698           check_ordering(relation_xml, @response.body)
 
 704     # remove a member from a relation and check the bounding box is
 
 706     def test_remove_member_bounding_box
 
 707       relation = create(:relation)
 
 708       node1 = create(:node, :lat => 3, :lon => 3)
 
 709       node2 = create(:node, :lat => 5, :lon => 5)
 
 710       create(:relation_member, :relation => relation, :member => node1)
 
 711       create(:relation_member, :relation => relation, :member => node2)
 
 713       check_changeset_modify(BoundingBox.new(5, 5, 5, 5)) do |changeset_id, auth_header|
 
 714         # remove node 5 (5,5) from an existing relation
 
 715         relation_xml = xml_for_relation(relation)
 
 717           .find("//osm/relation/member[@type='node'][@ref='#{node2.id}']")
 
 720         # update changeset ID to point to new changeset
 
 721         update_changeset(relation_xml, changeset_id)
 
 724         put api_relation_path(relation), :params => relation_xml.to_s, :headers => auth_header
 
 725         assert_response :success, "can't update relation for remove node/bbox test"
 
 730     # check that relations are ordered
 
 731     def test_relation_member_ordering
 
 733       changeset = create(:changeset, :user => user)
 
 734       node1 = create(:node)
 
 735       node2 = create(:node)
 
 736       node3 = create(:node)
 
 737       way1 = create(:way_with_nodes, :nodes_count => 2)
 
 738       way2 = create(:way_with_nodes, :nodes_count => 2)
 
 740       auth_header = bearer_authorization_header user
 
 744          <relation changeset='#{changeset.id}'>
 
 745           <member ref='#{node1.id}' type='node' role='first'/>
 
 746           <member ref='#{node2.id}' type='node' role='second'/>
 
 747           <member ref='#{way1.id}' type='way' role='third'/>
 
 748           <member ref='#{way2.id}' type='way' role='fourth'/>
 
 752       doc = XML::Parser.string(doc_str).parse
 
 754       post api_relations_path, :params => doc.to_s, :headers => auth_header
 
 755       assert_response :success, "can't create a relation: #{@response.body}"
 
 756       relation_id = @response.body.to_i
 
 758       # get it back and check the ordering
 
 759       get api_relation_path(relation_id)
 
 760       assert_response :success, "can't read back the relation: #{@response.body}"
 
 761       check_ordering(doc, @response.body)
 
 763       # insert a member at the front
 
 764       new_member = XML::Node.new "member"
 
 765       new_member["ref"] = node3.id.to_s
 
 766       new_member["type"] = "node"
 
 767       new_member["role"] = "new first"
 
 768       doc.find("//osm/relation").first.child.prev = new_member
 
 769       # update the version, should be 1?
 
 770       doc.find("//osm/relation").first["id"] = relation_id.to_s
 
 771       doc.find("//osm/relation").first["version"] = 1.to_s
 
 773       # upload the next version of the relation
 
 774       put api_relation_path(relation_id), :params => doc.to_s, :headers => auth_header
 
 775       assert_response :success, "can't update relation: #{@response.body}"
 
 776       assert_equal 2, @response.body.to_i
 
 778       # get it back again and check the ordering again
 
 779       get api_relation_path(relation_id)
 
 780       assert_response :success, "can't read back the relation: #{@response.body}"
 
 781       check_ordering(doc, @response.body)
 
 783       # check the ordering in the history tables:
 
 784       with_controller(OldRelationsController.new) do
 
 785         get api_relation_version_path(relation_id, 2)
 
 786         assert_response :success, "can't read back version 2 of the relation #{relation_id}"
 
 787         check_ordering(doc, @response.body)
 
 792     # check that relations can contain duplicate members
 
 793     def test_relation_member_duplicates
 
 794       private_user = create(:user, :data_public => false)
 
 796       changeset = create(:changeset, :user => user)
 
 797       node1 = create(:node)
 
 798       node2 = create(:node)
 
 802          <relation changeset='#{changeset.id}'>
 
 803           <member ref='#{node1.id}' type='node' role='forward'/>
 
 804           <member ref='#{node2.id}' type='node' role='forward'/>
 
 805           <member ref='#{node1.id}' type='node' role='forward'/>
 
 806           <member ref='#{node2.id}' type='node' role='forward'/>
 
 810       doc = XML::Parser.string(doc_str).parse
 
 812       ## First try with the private user
 
 813       auth_header = bearer_authorization_header private_user
 
 815       post api_relations_path, :params => doc.to_s, :headers => auth_header
 
 816       assert_response :forbidden
 
 818       ## Now try with the public user
 
 819       auth_header = bearer_authorization_header user
 
 821       post api_relations_path, :params => doc.to_s, :headers => auth_header
 
 822       assert_response :success, "can't create a relation: #{@response.body}"
 
 823       relation_id = @response.body.to_i
 
 825       # get it back and check the ordering
 
 826       get api_relation_path(relation_id)
 
 827       assert_response :success, "can't read back the relation: #{relation_id}"
 
 828       check_ordering(doc, @response.body)
 
 832     # test that the ordering of elements in the history is the same as in current.
 
 833     def test_history_ordering
 
 835       changeset = create(:changeset, :user => user)
 
 836       node1 = create(:node)
 
 837       node2 = create(:node)
 
 838       node3 = create(:node)
 
 839       node4 = create(:node)
 
 843          <relation changeset='#{changeset.id}'>
 
 844           <member ref='#{node1.id}' type='node' role='forward'/>
 
 845           <member ref='#{node4.id}' type='node' role='forward'/>
 
 846           <member ref='#{node3.id}' type='node' role='forward'/>
 
 847           <member ref='#{node2.id}' type='node' role='forward'/>
 
 851       doc = XML::Parser.string(doc_str).parse
 
 852       auth_header = bearer_authorization_header user
 
 854       post api_relations_path, :params => doc.to_s, :headers => auth_header
 
 855       assert_response :success, "can't create a relation: #{@response.body}"
 
 856       relation_id = @response.body.to_i
 
 858       # check the ordering in the current tables:
 
 859       get api_relation_path(relation_id)
 
 860       assert_response :success, "can't read back the relation: #{@response.body}"
 
 861       check_ordering(doc, @response.body)
 
 863       # check the ordering in the history tables:
 
 864       with_controller(OldRelationsController.new) do
 
 865         get api_relation_version_path(relation_id, 1)
 
 866         assert_response :success, "can't read back version 1 of the relation: #{@response.body}"
 
 867         check_ordering(doc, @response.body)
 
 872     # remove all the members from a relation. the result is pretty useless, but
 
 873     # still technically valid.
 
 874     def test_remove_all_members
 
 875       relation = create(:relation)
 
 876       node1 = create(:node, :lat => 0.3, :lon => 0.3)
 
 877       node2 = create(:node, :lat => 0.5, :lon => 0.5)
 
 879       create(:way_node, :way => way, :node => node1)
 
 880       create(:relation_member, :relation => relation, :member => way)
 
 881       create(:relation_member, :relation => relation, :member => node2)
 
 883       check_changeset_modify(BoundingBox.new(0.3, 0.3, 0.5, 0.5)) do |changeset_id, auth_header|
 
 884         relation_xml = xml_for_relation(relation)
 
 886           .find("//osm/relation/member")
 
 889         # update changeset ID to point to new changeset
 
 890         update_changeset(relation_xml, changeset_id)
 
 893         put api_relation_path(relation), :params => relation_xml.to_s, :headers => auth_header
 
 894         assert_response :success, "can't update relation for remove all members test"
 
 895         checkrelation = Relation.find(relation.id)
 
 896         assert_not_nil(checkrelation,
 
 897                        "uploaded relation not found in database after upload")
 
 898         assert_equal(0, checkrelation.members.length,
 
 899                      "relation contains members but they should have all been deleted")
 
 904     # test initial rate limit
 
 905     def test_initial_rate_limit
 
 910       node1 = create(:node)
 
 911       node2 = create(:node)
 
 913       # create a changeset that puts us near the initial rate limit
 
 914       changeset = create(:changeset, :user => user,
 
 915                                      :created_at => Time.now.utc - 5.minutes,
 
 916                                      :num_changes => Settings.initial_changes_per_hour - 1)
 
 918       # create authentication header
 
 919       auth_header = bearer_authorization_header user
 
 921       # try creating a relation
 
 922       xml = "<osm><relation changeset='#{changeset.id}'>" \
 
 923             "<member  ref='#{node1.id}' type='node' role='some'/>" \
 
 924             "<member  ref='#{node2.id}' type='node' role='some'/>" \
 
 925             "<tag k='test' v='yes' /></relation></osm>"
 
 926       post api_relations_path, :params => xml, :headers => auth_header
 
 927       assert_response :success, "relation create did not return success status"
 
 929       # get the id of the relation we created
 
 930       relationid = @response.body
 
 932       # try updating the relation, which should be rate limited
 
 933       xml = "<osm><relation id='#{relationid}' version='1' changeset='#{changeset.id}'>" \
 
 934             "<member  ref='#{node2.id}' type='node' role='some'/>" \
 
 935             "<member  ref='#{node1.id}' type='node' role='some'/>" \
 
 936             "<tag k='test' v='yes' /></relation></osm>"
 
 937       put api_relation_path(relationid), :params => xml, :headers => auth_header
 
 938       assert_response :too_many_requests, "relation update did not hit rate limit"
 
 940       # try deleting the relation, which should be rate limited
 
 941       xml = "<osm><relation id='#{relationid}' version='2' changeset='#{changeset.id}'/></osm>"
 
 942       delete api_relation_path(relationid), :params => xml, :headers => auth_header
 
 943       assert_response :too_many_requests, "relation delete did not hit rate limit"
 
 945       # try creating a relation, which should be rate limited
 
 946       xml = "<osm><relation changeset='#{changeset.id}'>" \
 
 947             "<member  ref='#{node1.id}' type='node' role='some'/>" \
 
 948             "<member  ref='#{node2.id}' type='node' role='some'/>" \
 
 949             "<tag k='test' v='yes' /></relation></osm>"
 
 950       post api_relations_path, :params => xml, :headers => auth_header
 
 951       assert_response :too_many_requests, "relation create did not hit rate limit"
 
 955     # test maximum rate limit
 
 956     def test_maximum_rate_limit
 
 961       node1 = create(:node)
 
 962       node2 = create(:node)
 
 964       # create a changeset to establish our initial edit time
 
 965       changeset = create(:changeset, :user => user,
 
 966                                      :created_at => Time.now.utc - 28.days)
 
 968       # create changeset to put us near the maximum rate limit
 
 969       total_changes = Settings.max_changes_per_hour - 1
 
 970       while total_changes.positive?
 
 971         changes = [total_changes, Changeset::MAX_ELEMENTS].min
 
 972         changeset = create(:changeset, :user => user,
 
 973                                        :created_at => Time.now.utc - 5.minutes,
 
 974                                        :num_changes => changes)
 
 975         total_changes -= changes
 
 978       # create authentication header
 
 979       auth_header = bearer_authorization_header user
 
 981       # try creating a relation
 
 982       xml = "<osm><relation changeset='#{changeset.id}'>" \
 
 983             "<member  ref='#{node1.id}' type='node' role='some'/>" \
 
 984             "<member  ref='#{node2.id}' type='node' role='some'/>" \
 
 985             "<tag k='test' v='yes' /></relation></osm>"
 
 986       post api_relations_path, :params => xml, :headers => auth_header
 
 987       assert_response :success, "relation create did not return success status"
 
 989       # get the id of the relation we created
 
 990       relationid = @response.body
 
 992       # try updating the relation, which should be rate limited
 
 993       xml = "<osm><relation id='#{relationid}' version='1' changeset='#{changeset.id}'>" \
 
 994             "<member  ref='#{node2.id}' type='node' role='some'/>" \
 
 995             "<member  ref='#{node1.id}' type='node' role='some'/>" \
 
 996             "<tag k='test' v='yes' /></relation></osm>"
 
 997       put api_relation_path(relationid), :params => xml, :headers => auth_header
 
 998       assert_response :too_many_requests, "relation update did not hit rate limit"
 
1000       # try deleting the relation, which should be rate limited
 
1001       xml = "<osm><relation id='#{relationid}' version='2' changeset='#{changeset.id}'/></osm>"
 
1002       delete api_relation_path(relationid), :params => xml, :headers => auth_header
 
1003       assert_response :too_many_requests, "relation delete did not hit rate limit"
 
1005       # try creating a relation, which should be rate limited
 
1006       xml = "<osm><relation changeset='#{changeset.id}'>" \
 
1007             "<member  ref='#{node1.id}' type='node' role='some'/>" \
 
1008             "<member  ref='#{node2.id}' type='node' role='some'/>" \
 
1009             "<tag k='test' v='yes' /></relation></osm>"
 
1010       post api_relations_path, :params => xml, :headers => auth_header
 
1011       assert_response :too_many_requests, "relation create did not hit rate limit"
 
1017     # checks that the XML document and the string arguments have
 
1018     # members in the same order.
 
1019     def check_ordering(doc, xml)
 
1020       new_doc = XML::Parser.string(xml).parse
 
1022       doc_members = doc.find("//osm/relation/member").collect do |m|
 
1023         [m["ref"].to_i, m["type"].to_sym, m["role"]]
 
1026       new_members = new_doc.find("//osm/relation/member").collect do |m|
 
1027         [m["ref"].to_i, m["type"].to_sym, m["role"]]
 
1030       doc_members.zip(new_members).each do |d, n|
 
1031         assert_equal d, n, "members are not equal - ordering is wrong? (#{doc}, #{xml})"
 
1036     # create a changeset and yield to the caller to set it up, then assert
 
1037     # that the changeset bounding box is +bbox+.
 
1038     def check_changeset_modify(bbox)
 
1039       ## First test with the private user to check that you get a forbidden
 
1040       auth_header = bearer_authorization_header create(:user, :data_public => false)
 
1042       # create a new changeset for this operation, so we are assured
 
1043       # that the bounding box will be newly-generated.
 
1044       with_controller(Api::ChangesetsController.new) do
 
1045         xml = "<osm><changeset/></osm>"
 
1046         post api_changesets_path, :params => xml, :headers => auth_header
 
1047         assert_response :forbidden, "shouldn't be able to create changeset for modify test, as should get forbidden"
 
1050       ## Now do the whole thing with the public user
 
1051       auth_header = bearer_authorization_header
 
1053       # create a new changeset for this operation, so we are assured
 
1054       # that the bounding box will be newly-generated.
 
1055       changeset_id = with_controller(Api::ChangesetsController.new) do
 
1056         xml = "<osm><changeset/></osm>"
 
1057         post api_changesets_path, :params => xml, :headers => auth_header
 
1058         assert_response :success, "couldn't create changeset for modify test"
 
1062       # go back to the block to do the actual modifies
 
1063       yield changeset_id, auth_header
 
1065       # now download the changeset to check its bounding box
 
1066       with_controller(Api::ChangesetsController.new) do
 
1067         get api_changeset_path(changeset_id)
 
1068         assert_response :success, "can't re-read changeset for modify test"
 
1069         assert_select "osm>changeset", 1, "Changeset element doesn't exist in #{@response.body}"
 
1070         assert_select "osm>changeset[id='#{changeset_id}']", 1, "Changeset id=#{changeset_id} doesn't exist in #{@response.body}"
 
1071         assert_select "osm>changeset[min_lon='#{format('%<lon>.7f', :lon => bbox.min_lon)}']", 1, "Changeset min_lon wrong in #{@response.body}"
 
1072         assert_select "osm>changeset[min_lat='#{format('%<lat>.7f', :lat => bbox.min_lat)}']", 1, "Changeset min_lat wrong in #{@response.body}"
 
1073         assert_select "osm>changeset[max_lon='#{format('%<lon>.7f', :lon => bbox.max_lon)}']", 1, "Changeset max_lon wrong in #{@response.body}"
 
1074         assert_select "osm>changeset[max_lat='#{format('%<lat>.7f', :lat => bbox.max_lat)}']", 1, "Changeset max_lat wrong in #{@response.body}"
 
1079     # yields the relation with the given +id+ (and optional +version+
 
1080     # to read from the history tables) into the block. the parsed XML
 
1082     def with_relation(id, ver = nil)
 
1084         get api_relation_path(id)
 
1086         with_controller(OldRelationsController.new) do
 
1087           get api_relation_version_path(id, ver)
 
1090       assert_response :success
 
1091       yield xml_parse(@response.body)
 
1095     # updates the relation (XML) +rel+ and
 
1096     # yields the new version of that relation into the block.
 
1097     # the parsed XML doc is returned.
 
1098     def with_update(rel, headers)
 
1099       rel_id = rel.find("//osm/relation").first["id"].to_i
 
1100       put api_relation_path(rel_id), :params => rel.to_s, :headers => headers
 
1101       assert_response :success, "can't update relation: #{@response.body}"
 
1102       version = @response.body.to_i
 
1104       # now get the new version
 
1105       get api_relation_path(rel_id)
 
1106       assert_response :success
 
1107       new_rel = xml_parse(@response.body)
 
1115     # updates the relation (XML) +rel+ via the diff-upload API and
 
1116     # yields the new version of that relation into the block.
 
1117     # the parsed XML doc is returned.
 
1118     def with_update_diff(rel, headers)
 
1119       rel_id = rel.find("//osm/relation").first["id"].to_i
 
1120       cs_id = rel.find("//osm/relation").first["changeset"].to_i
 
1123       with_controller(Api::ChangesetsController.new) do
 
1124         doc = OSM::API.new.xml_doc
 
1125         change = XML::Node.new "osmChange"
 
1127         modify = XML::Node.new "modify"
 
1129         modify << doc.import(rel.find("//osm/relation").first)
 
1131         post changeset_upload_path(cs_id), :params => doc.to_s, :headers => headers
 
1132         assert_response :success, "can't upload diff relation: #{@response.body}"
 
1133         version = xml_parse(@response.body).find("//diffResult/relation").first["new_version"].to_i
 
1136       # now get the new version
 
1137       get api_relation_path(rel_id)
 
1138       assert_response :success
 
1139       new_rel = xml_parse(@response.body)
 
1147     # returns a k->v hash of tags from an xml doc
 
1148     def get_tags_as_hash(a)
 
1149       a.find("//osm/relation/tag").sort_by { |v| v["k"] }.each_with_object({}) do |v, h|
 
1155     # assert that all tags on relation documents +a+ and +b+
 
1157     def assert_tags_equal(a, b)
 
1158       # turn the XML doc into tags hashes
 
1159       a_tags = get_tags_as_hash(a)
 
1160       b_tags = get_tags_as_hash(b)
 
1162       assert_equal a_tags.keys, b_tags.keys, "Tag keys should be identical."
 
1163       a_tags.each do |k, v|
 
1164         assert_equal v, b_tags[k],
 
1165                      "Tags which were not altered should be the same. " \
 
1166                      "#{a_tags.inspect} != #{b_tags.inspect}"
 
1171     # update the changeset_id of a node element
 
1172     def update_changeset(xml, changeset_id)
 
1173       xml_attr_rewrite(xml, "changeset", changeset_id)
 
1177     # update an attribute in the node element
 
1178     def xml_attr_rewrite(xml, name, value)
 
1179       xml.find("//osm/relation").first[name] = value.to_s
 
1186       parser = XML::Parser.string(xml)