2 require_relative "elements_test_helper"
5 class RelationsControllerTest < ActionDispatch::IntegrationTest
6 include ElementsTestHelper
9 # test all routes which lead to this controller
12 { :path => "/api/0.6/relations", :method => :get },
13 { :controller => "api/relations", :action => "index" }
16 { :path => "/api/0.6/relations.json", :method => :get },
17 { :controller => "api/relations", :action => "index", :format => "json" }
20 { :path => "/api/0.6/relations", :method => :post },
21 { :controller => "api/relations", :action => "create" }
24 { :path => "/api/0.6/relation/1", :method => :get },
25 { :controller => "api/relations", :action => "show", :id => "1" }
28 { :path => "/api/0.6/relation/1.json", :method => :get },
29 { :controller => "api/relations", :action => "show", :id => "1", :format => "json" }
32 { :path => "/api/0.6/relation/1/full", :method => :get },
33 { :controller => "api/relations", :action => "show", :full => true, :id => "1" }
36 { :path => "/api/0.6/relation/1/full.json", :method => :get },
37 { :controller => "api/relations", :action => "show", :full => true, :id => "1", :format => "json" }
40 { :path => "/api/0.6/relation/1", :method => :put },
41 { :controller => "api/relations", :action => "update", :id => "1" }
44 { :path => "/api/0.6/relation/1", :method => :delete },
45 { :controller => "api/relations", :action => "destroy", :id => "1" }
49 { :controller => "api/relations", :action => "create" },
50 { :path => "/api/0.6/relation/create", :method => :put }
55 # test fetching multiple relations
57 relation1 = create(:relation)
58 relation2 = create(:relation, :deleted)
59 relation3 = create(:relation, :with_history, :version => 2)
60 relation4 = create(:relation, :with_history, :version => 2)
61 relation4.old_relations.find_by(:version => 1).redact!(create(:redaction))
63 # check error when no parameter provided
64 get api_relations_path
65 assert_response :bad_request
67 # check error when no parameter value provided
68 get api_relations_path(:relations => "")
69 assert_response :bad_request
72 get api_relations_path(:relations => "#{relation1.id},#{relation2.id},#{relation3.id},#{relation4.id}")
73 assert_response :success
74 assert_select "osm" do
75 assert_select "relation", :count => 4
76 assert_select "relation[id='#{relation1.id}'][visible='true']", :count => 1
77 assert_select "relation[id='#{relation2.id}'][visible='false']", :count => 1
78 assert_select "relation[id='#{relation3.id}'][visible='true']", :count => 1
79 assert_select "relation[id='#{relation4.id}'][visible='true']", :count => 1
82 # test a working call with json format
83 get api_relations_path(:relations => "#{relation1.id},#{relation2.id},#{relation3.id},#{relation4.id}", :format => "json")
85 js = ActiveSupport::JSON.decode(@response.body)
87 assert_equal 4, js["elements"].count
88 assert_equal 4, (js["elements"].count { |a| a["type"] == "relation" })
89 assert_equal 1, (js["elements"].count { |a| a["id"] == relation1.id && a["visible"].nil? })
90 assert_equal 1, (js["elements"].count { |a| a["id"] == relation2.id && a["visible"] == false })
91 assert_equal 1, (js["elements"].count { |a| a["id"] == relation3.id && a["visible"].nil? })
92 assert_equal 1, (js["elements"].count { |a| a["id"] == relation4.id && a["visible"].nil? })
94 # check error when a non-existent relation is included
95 get api_relations_path(:relations => "#{relation1.id},#{relation2.id},#{relation3.id},#{relation4.id},0")
96 assert_response :not_found
99 # -------------------------------------
100 # Test showing relations.
101 # -------------------------------------
103 def test_show_not_found
104 get api_relation_path(0)
105 assert_response :not_found
108 def test_show_deleted
109 get api_relation_path(create(:relation, :deleted))
110 assert_response :gone
114 relation = create(:relation, :timestamp => "2021-02-03T00:00:00Z")
115 node = create(:node, :timestamp => "2021-04-05T00:00:00Z")
116 create(:relation_member, :relation => relation, :member => node)
118 get api_relation_path(relation)
120 assert_response :success
121 assert_not_nil @response.header["Last-Modified"]
122 assert_equal "2021-02-03T00:00:00Z", Time.parse(@response.header["Last-Modified"]).utc.xmlschema
123 assert_dom "node", :count => 0
124 assert_dom "relation", :count => 1 do
125 assert_dom "> @id", :text => relation.id.to_s
129 def test_full_not_found
130 get api_relation_path(999999, :full => true)
131 assert_response :not_found
134 def test_full_deleted
135 get api_relation_path(create(:relation, :deleted), :full => true)
136 assert_response :gone
140 relation = create(:relation)
142 get api_relation_path(relation, :full => true)
144 assert_response :success
145 assert_dom "relation", :count => 1 do
146 assert_dom "> @id", :text => relation.id.to_s
150 def test_full_with_node_member
151 relation = create(:relation)
153 create(:relation_member, :relation => relation, :member => node)
155 get api_relation_path(relation, :full => true)
157 assert_response :success
158 assert_dom "node", :count => 1 do
159 assert_dom "> @id", :text => node.id.to_s
161 assert_dom "relation", :count => 1 do
162 assert_dom "> @id", :text => relation.id.to_s
166 def test_full_with_way_member
167 relation = create(:relation)
168 way = create(:way_with_nodes)
169 create(:relation_member, :relation => relation, :member => way)
171 get api_relation_path(relation, :full => true)
173 assert_response :success
174 assert_dom "node", :count => 1 do
175 assert_dom "> @id", :text => way.nodes[0].id.to_s
177 assert_dom "way", :count => 1 do
178 assert_dom "> @id", :text => way.id.to_s
180 assert_dom "relation", :count => 1 do
181 assert_dom "> @id", :text => relation.id.to_s
185 def test_full_with_node_member_json
186 relation = create(:relation)
188 create(:relation_member, :relation => relation, :member => node)
190 get api_relation_path(relation, :full => true, :format => "json")
192 assert_response :success
193 js = ActiveSupport::JSON.decode(@response.body)
195 assert_equal 2, js["elements"].count
197 js_relations = js["elements"].filter { |e| e["type"] == "relation" }
198 assert_equal 1, js_relations.count
199 assert_equal relation.id, js_relations[0]["id"]
200 assert_equal 1, js_relations[0]["members"].count
201 assert_equal "node", js_relations[0]["members"][0]["type"]
202 assert_equal node.id, js_relations[0]["members"][0]["ref"]
204 js_nodes = js["elements"].filter { |e| e["type"] == "node" }
205 assert_equal 1, js_nodes.count
206 assert_equal node.id, js_nodes[0]["id"]
209 # -------------------------------------
210 # Test creating relations.
211 # -------------------------------------
213 def test_create_without_members_by_private_user
214 with_unchanging_request([:data_public => false]) do |headers, changeset|
217 <relation changeset='#{changeset.id}'>
218 <tag k='test' v='yes' />
223 post api_relations_path, :params => osm, :headers => headers
225 assert_response :forbidden, "relation upload should have failed with forbidden"
229 def test_create_with_node_member_with_role_by_private_user
232 with_unchanging_request([:data_public => false]) do |headers, changeset|
235 <relation changeset='#{changeset.id}'>
236 <member ref='#{node.id}' type='node' role='some'/>
237 <tag k='test' v='yes' />
242 post api_relations_path, :params => osm, :headers => headers
244 assert_response :forbidden, "relation upload did not return forbidden status"
248 def test_create_with_node_member_without_role_by_private_user
251 with_unchanging_request([:data_public => false]) do |headers, changeset|
254 <relation changeset='#{changeset.id}'>
255 <member ref='#{node.id}' type='node'/>
256 <tag k='test' v='yes' />
261 post api_relations_path, :params => osm, :headers => headers
263 assert_response :forbidden, "relation upload did not return forbidden status"
267 def test_create_with_node_and_way_members_by_private_user
269 way = create(:way_with_nodes, :nodes_count => 2)
271 with_unchanging_request([:data_public => false]) do |headers, changeset|
274 <relation changeset='#{changeset.id}'>
275 <member type='node' ref='#{node.id}' role='some'/>
276 <member type='way' ref='#{way.id}' role='other'/>
277 <tag k='test' v='yes' />
282 post api_relations_path, :params => osm, :headers => headers
284 assert_response :forbidden, "relation upload did not return success status"
288 def test_create_without_members
289 with_request do |headers, changeset|
290 assert_difference "Relation.count" => 1,
291 "RelationMember.count" => 0 do
294 <relation changeset='#{changeset.id}'>
295 <tag k='test' v='yes' />
300 post api_relations_path, :params => osm, :headers => headers
302 assert_response :success, "relation upload did not return success status"
305 created_relation_id = @response.body
306 relation = Relation.find(created_relation_id)
307 assert_empty relation.members
308 assert_equal({ "test" => "yes" }, relation.tags)
309 assert_equal changeset.id, relation.changeset_id, "saved relation does not belong in the changeset it was assigned to"
310 assert relation.visible, "saved relation is not visible"
314 def test_create_with_node_member_with_role
317 with_request do |headers, changeset|
318 assert_difference "Relation.count" => 1,
319 "RelationMember.count" => 1 do
322 <relation changeset='#{changeset.id}'>
323 <member ref='#{node.id}' type='node' role='some'/>
324 <tag k='test' v='yes' />
329 post api_relations_path, :params => osm, :headers => headers
331 assert_response :success, "relation upload did not return success status"
334 created_relation_id = @response.body
335 relation = Relation.find(created_relation_id)
336 assert_equal [["Node", node.id, "some"]], relation.members
337 assert_equal({ "test" => "yes" }, relation.tags)
338 assert_equal changeset.id, relation.changeset_id, "saved relation does not belong in the changeset it was assigned to"
339 assert relation.visible, "saved relation is not visible"
343 def test_create_with_node_member_without_role
346 with_request do |headers, changeset|
347 assert_difference "Relation.count" => 1,
348 "RelationMember.count" => 1 do
351 <relation changeset='#{changeset.id}'>
352 <member ref='#{node.id}' type='node'/>
353 <tag k='test' v='yes' />
358 post api_relations_path, :params => osm, :headers => headers
360 assert_response :success, "relation upload did not return success status"
363 created_relation_id = @response.body
364 relation = Relation.find(created_relation_id)
365 assert_equal [["Node", node.id, ""]], relation.members
366 assert_equal({ "test" => "yes" }, relation.tags)
367 assert_equal changeset.id, relation.changeset_id, "saved relation does not belong in the changeset it was assigned to"
368 assert relation.visible, "saved relation is not visible"
372 def test_create_with_node_and_way_members
374 way = create(:way_with_nodes, :nodes_count => 2)
376 with_request do |headers, changeset|
377 assert_difference "Relation.count" => 1,
378 "RelationMember.count" => 2 do
381 <relation changeset='#{changeset.id}'>
382 <member type='node' ref='#{node.id}' role='some'/>
383 <member type='way' ref='#{way.id}' role='other'/>
384 <tag k='test' v='yes' />
389 post api_relations_path, :params => osm, :headers => headers
391 assert_response :success, "relation upload did not return success status"
394 created_relation_id = @response.body
395 relation = Relation.find(created_relation_id)
396 assert_equal [["Node", node.id, "some"],
397 ["Way", way.id, "other"]], relation.members
398 assert_equal({ "test" => "yes" }, relation.tags)
399 assert_equal changeset.id, relation.changeset_id, "saved relation does not belong in the changeset it was assigned to"
400 assert relation.visible, "saved relation is not visible"
404 def test_create_and_show
406 changeset = create(:changeset, :user => user)
410 <relation changeset='#{changeset.id}'/>
414 post api_relations_path, :params => osm, :headers => bearer_authorization_header(user)
416 assert_response :success, "relation upload did not return success status"
418 created_relation_id = @response.body
420 get api_relation_path(created_relation_id)
422 assert_response :success
425 # ------------------------------------
426 # Test updating relations
427 # ------------------------------------
429 def test_update_wrong_id
431 changeset = create(:changeset, :user => user)
432 relation = create(:relation)
433 other_relation = create(:relation)
435 auth_header = bearer_authorization_header user
436 get api_relation_path(relation)
437 assert_response :success
438 rel = XML::Parser.string(@response.body).parse
440 update_changeset(rel, changeset.id)
441 put api_relation_path(other_relation), :params => rel.to_s, :headers => auth_header
442 assert_response :bad_request
445 # -------------------------------------
446 # Test creating some invalid relations.
447 # -------------------------------------
449 def test_create_invalid
451 changeset = create(:changeset, :user => user)
453 auth_header = bearer_authorization_header user
455 # create a relation with non-existing node as member
458 <relation changeset='#{changeset.id}'>
459 <member type='node' ref='0'/>
463 post api_relations_path, :params => xml, :headers => auth_header
465 assert_response :precondition_failed,
466 "relation upload with invalid node did not return 'precondition failed'"
467 assert_equal "Precondition failed: Relation with id cannot be saved due to Node with id 0", @response.body
470 # -------------------------------------
471 # Test creating a relation, with some invalid XML
472 # -------------------------------------
473 def test_create_invalid_xml
475 changeset = create(:changeset, :user => user)
478 auth_header = bearer_authorization_header user
480 # create some xml that should return an error
483 <relation changeset='#{changeset.id}'>
484 <member type='type' ref='#{node.id}' role=''/>
488 post api_relations_path, :params => xml, :headers => auth_header
490 assert_response :bad_request
491 assert_match(/Cannot parse valid relation from xml string/, @response.body)
492 assert_match(/The type is not allowed only, /, @response.body)
495 # -------------------------------------
496 # Test deleting relations.
497 # -------------------------------------
500 private_user = create(:user, :data_public => false)
501 private_user_closed_changeset = create(:changeset, :closed, :user => private_user)
503 closed_changeset = create(:changeset, :closed, :user => user)
504 changeset = create(:changeset, :user => user)
505 relation = create(:relation)
506 used_relation = create(:relation)
507 super_relation = create(:relation_member, :member => used_relation).relation
508 deleted_relation = create(:relation, :deleted)
509 multi_tag_relation = create(:relation)
510 create_list(:relation_tag, 4, :relation => multi_tag_relation)
512 ## First try to delete relation without auth
513 delete api_relation_path(relation)
514 assert_response :unauthorized
516 ## Then try with the private user, to make sure that you get a forbidden
517 auth_header = bearer_authorization_header private_user
519 # this shouldn't work, as we should need the payload...
520 delete api_relation_path(relation), :headers => auth_header
521 assert_response :forbidden
523 # try to delete without specifying a changeset
524 xml = "<osm><relation id='#{relation.id}'/></osm>"
525 delete api_relation_path(relation), :params => xml.to_s, :headers => auth_header
526 assert_response :forbidden
528 # try to delete with an invalid (closed) changeset
529 xml = update_changeset(xml_for_relation(relation),
530 private_user_closed_changeset.id)
531 delete api_relation_path(relation), :params => xml.to_s, :headers => auth_header
532 assert_response :forbidden
534 # try to delete with an invalid (non-existent) changeset
535 xml = update_changeset(xml_for_relation(relation), 0)
536 delete api_relation_path(relation), :params => xml.to_s, :headers => auth_header
537 assert_response :forbidden
539 # this won't work because the relation is in-use by another relation
540 xml = xml_for_relation(used_relation)
541 delete api_relation_path(used_relation), :params => xml.to_s, :headers => auth_header
542 assert_response :forbidden
544 # this should work when we provide the appropriate payload...
545 xml = xml_for_relation(relation)
546 delete api_relation_path(relation), :params => xml.to_s, :headers => auth_header
547 assert_response :forbidden
549 # this won't work since the relation is already deleted
550 xml = xml_for_relation(deleted_relation)
551 delete api_relation_path(deleted_relation), :params => xml.to_s, :headers => auth_header
552 assert_response :forbidden
554 # this won't work since the relation never existed
555 delete api_relation_path(0), :headers => auth_header
556 assert_response :forbidden
558 ## now set auth for the public user
559 auth_header = bearer_authorization_header user
561 # this shouldn't work, as we should need the payload...
562 delete api_relation_path(relation), :headers => auth_header
563 assert_response :bad_request
565 # try to delete without specifying a changeset
566 xml = "<osm><relation id='#{relation.id}' version='#{relation.version}' /></osm>"
567 delete api_relation_path(relation), :params => xml.to_s, :headers => auth_header
568 assert_response :bad_request
569 assert_match(/Changeset id is missing/, @response.body)
571 # try to delete with an invalid (closed) changeset
572 xml = update_changeset(xml_for_relation(relation),
574 delete api_relation_path(relation), :params => xml.to_s, :headers => auth_header
575 assert_response :conflict
577 # try to delete with an invalid (non-existent) changeset
578 xml = update_changeset(xml_for_relation(relation), 0)
579 delete api_relation_path(relation), :params => xml.to_s, :headers => auth_header
580 assert_response :conflict
582 # this won't work because the relation is in a changeset owned by someone else
583 xml = update_changeset(xml_for_relation(relation), create(:changeset).id)
584 delete api_relation_path(relation), :params => xml.to_s, :headers => auth_header
585 assert_response :conflict,
586 "shouldn't be able to delete a relation in a changeset owned by someone else (#{@response.body})"
588 # this won't work because the relation in the payload is different to that passed
589 xml = update_changeset(xml_for_relation(relation), changeset.id)
590 delete api_relation_path(create(:relation)), :params => xml.to_s, :headers => auth_header
591 assert_response :bad_request, "shouldn't be able to delete a relation when payload is different to the url"
593 # this won't work because the relation is in-use by another relation
594 xml = update_changeset(xml_for_relation(used_relation), changeset.id)
595 delete api_relation_path(used_relation), :params => xml.to_s, :headers => auth_header
596 assert_response :precondition_failed,
597 "shouldn't be able to delete a relation used in a relation (#{@response.body})"
598 assert_equal "Precondition failed: The relation #{used_relation.id} is used in relation #{super_relation.id}.", @response.body
600 # this should work when we provide the appropriate payload...
601 xml = update_changeset(xml_for_relation(multi_tag_relation), changeset.id)
602 delete api_relation_path(multi_tag_relation), :params => xml.to_s, :headers => auth_header
603 assert_response :success
605 # valid delete should return the new version number, which should
606 # be greater than the old version number
607 assert_operator @response.body.to_i, :>, multi_tag_relation.version, "delete request should return a new version number for relation"
609 # this won't work since the relation is already deleted
610 xml = update_changeset(xml_for_relation(deleted_relation), changeset.id)
611 delete api_relation_path(deleted_relation), :params => xml.to_s, :headers => auth_header
612 assert_response :gone
614 # Public visible relation needs to be deleted
615 xml = update_changeset(xml_for_relation(super_relation), changeset.id)
616 delete api_relation_path(super_relation), :params => xml.to_s, :headers => auth_header
617 assert_response :success
619 # this works now because the relation which was using this one
621 xml = update_changeset(xml_for_relation(used_relation), changeset.id)
622 delete api_relation_path(used_relation), :params => xml.to_s, :headers => auth_header
623 assert_response :success,
624 "should be able to delete a relation used in an old relation (#{@response.body})"
626 # this won't work since the relation never existed
627 delete api_relation_path(0), :headers => auth_header
628 assert_response :not_found
632 # test initial rate limit
633 def test_initial_rate_limit
638 node1 = create(:node)
639 node2 = create(:node)
641 # create a changeset that puts us near the initial rate limit
642 changeset = create(:changeset, :user => user,
643 :created_at => Time.now.utc - 5.minutes,
644 :num_changes => Settings.initial_changes_per_hour - 1)
646 # create authentication header
647 auth_header = bearer_authorization_header user
649 # try creating a relation
652 <relation changeset='#{changeset.id}'>
653 <member ref='#{node1.id}' type='node' role='some'/>
654 <member ref='#{node2.id}' type='node' role='some'/>
658 post api_relations_path, :params => xml, :headers => auth_header
659 assert_response :success, "relation create did not return success status"
661 # get the id of the relation we created
662 relationid = @response.body
664 # try updating the relation, which should be rate limited
667 <relation id='#{relationid}' version='1' changeset='#{changeset.id}'>
668 <member ref='#{node2.id}' type='node' role='some'/>
669 <member ref='#{node1.id}' type='node' role='some'/>
673 put api_relation_path(relationid), :params => xml, :headers => auth_header
674 assert_response :too_many_requests, "relation update did not hit rate limit"
676 # try deleting the relation, which should be rate limited
677 xml = "<osm><relation id='#{relationid}' version='2' changeset='#{changeset.id}'/></osm>"
678 delete api_relation_path(relationid), :params => xml, :headers => auth_header
679 assert_response :too_many_requests, "relation delete did not hit rate limit"
681 # try creating a relation, which should be rate limited
684 <relation changeset='#{changeset.id}'>
685 <member ref='#{node1.id}' type='node' role='some'/>
686 <member ref='#{node2.id}' type='node' role='some'/>
690 post api_relations_path, :params => xml, :headers => auth_header
691 assert_response :too_many_requests, "relation create did not hit rate limit"
695 # test maximum rate limit
696 def test_maximum_rate_limit
701 node1 = create(:node)
702 node2 = create(:node)
704 # create a changeset to establish our initial edit time
705 changeset = create(:changeset, :user => user,
706 :created_at => Time.now.utc - 28.days)
708 # create changeset to put us near the maximum rate limit
709 total_changes = Settings.max_changes_per_hour - 1
710 while total_changes.positive?
711 changes = [total_changes, Changeset::MAX_ELEMENTS].min
712 changeset = create(:changeset, :user => user,
713 :created_at => Time.now.utc - 5.minutes,
714 :num_changes => changes)
715 total_changes -= changes
718 # create authentication header
719 auth_header = bearer_authorization_header user
721 # try creating a relation
724 <relation changeset='#{changeset.id}'>
725 <member ref='#{node1.id}' type='node' role='some'/>
726 <member ref='#{node2.id}' type='node' role='some'/>
730 post api_relations_path, :params => xml, :headers => auth_header
731 assert_response :success, "relation create did not return success status"
733 # get the id of the relation we created
734 relationid = @response.body
736 # try updating the relation, which should be rate limited
739 <relation id='#{relationid}' version='1' changeset='#{changeset.id}'>
740 <member ref='#{node2.id}' type='node' role='some'/>
741 <member ref='#{node1.id}' type='node' role='some'/>
745 put api_relation_path(relationid), :params => xml, :headers => auth_header
746 assert_response :too_many_requests, "relation update did not hit rate limit"
748 # try deleting the relation, which should be rate limited
749 xml = "<osm><relation id='#{relationid}' version='2' changeset='#{changeset.id}'/></osm>"
750 delete api_relation_path(relationid), :params => xml, :headers => auth_header
751 assert_response :too_many_requests, "relation delete did not hit rate limit"
753 # try creating a relation, which should be rate limited
756 <relation changeset='#{changeset.id}'>
757 <member ref='#{node1.id}' type='node' role='some'/>
758 <member ref='#{node2.id}' type='node' role='some'/>
762 post api_relations_path, :params => xml, :headers => auth_header
763 assert_response :too_many_requests, "relation create did not hit rate limit"
769 [Relation, RelationTag, RelationMember,
770 OldRelation, OldRelationTag, OldRelationMember]
774 # update an attribute in the node element
775 def xml_attr_rewrite(xml, name, value)
776 xml.find("//osm/relation").first[name] = value.to_s