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 simple relation creation.
211 # -------------------------------------
214 private_user = create(:user, :data_public => false)
215 private_changeset = create(:changeset, :user => private_user)
217 changeset = create(:changeset, :user => user)
219 way = create(:way_with_nodes, :nodes_count => 2)
221 auth_header = bearer_authorization_header private_user
223 # create an relation without members
226 <relation changeset='#{private_changeset.id}'>
227 <tag k='test' v='yes' />
231 post api_relations_path, :params => xml, :headers => auth_header
232 # hope for forbidden, due to user
233 assert_response :forbidden,
234 "relation upload should have failed with forbidden"
237 # create an relation with a node as member
238 # This time try with a role attribute in the relation
241 <relation changeset='#{private_changeset.id}'>
242 <member ref='#{node.id}' type='node' role='some'/>
243 <tag k='test' v='yes' />
247 post api_relations_path, :params => xml, :headers => auth_header
248 # hope for forbidden due to user
249 assert_response :forbidden,
250 "relation upload did not return forbidden status"
253 # create an relation with a node as member, this time test that we don't
254 # need a role attribute to be included
257 <relation changeset='#{private_changeset.id}'>
258 <member ref='#{node.id}' type='node'/>
259 <tag k='test' v='yes' />
263 post api_relations_path, :params => xml, :headers => auth_header
264 # hope for forbidden due to user
265 assert_response :forbidden,
266 "relation upload did not return forbidden status"
269 # create an relation with a way and a node as members
272 <relation changeset='#{private_changeset.id}'>
273 <member type='node' ref='#{node.id}' role='some'/>
274 <member type='way' ref='#{way.id}' role='other'/>
275 <tag k='test' v='yes' />
279 post api_relations_path, :params => xml, :headers => auth_header
280 # hope for forbidden, due to user
281 assert_response :forbidden,
282 "relation upload did not return success status"
284 ## Now try with the public user
285 auth_header = bearer_authorization_header user
287 # create an relation without members
290 <relation changeset='#{changeset.id}'>
291 <tag k='test' v='yes' />
295 post api_relations_path, :params => xml, :headers => auth_header
297 assert_response :success,
298 "relation upload did not return success status"
299 # read id of created relation and search for it
300 relationid = @response.body
301 checkrelation = Relation.find(relationid)
302 assert_not_nil checkrelation,
303 "uploaded relation not found in data base after upload"
305 assert_equal(0, checkrelation.members.length, "saved relation contains members but should not")
306 assert_equal(1, checkrelation.tags.length, "saved relation does not contain exactly one tag")
307 assert_equal changeset.id, checkrelation.changeset.id,
308 "saved relation does not belong in the changeset it was assigned to"
309 assert_equal user.id, checkrelation.changeset.user_id,
310 "saved relation does not belong to user that created it"
311 assert checkrelation.visible,
312 "saved relation is not visible"
313 # ok the relation is there but can we also retrieve it?
314 get api_relation_path(relationid)
315 assert_response :success
318 # create an relation with a node as member
319 # This time try with a role attribute in the relation
322 <relation changeset='#{changeset.id}'>
323 <member ref='#{node.id}' type='node' role='some'/>
324 <tag k='test' v='yes' />
328 post api_relations_path, :params => xml, :headers => auth_header
330 assert_response :success,
331 "relation upload did not return success status"
332 # read id of created relation and search for it
333 relationid = @response.body
334 checkrelation = Relation.find(relationid)
335 assert_not_nil checkrelation,
336 "uploaded relation not found in data base after upload"
338 assert_equal(1, checkrelation.members.length, "saved relation does not contain exactly one member")
339 assert_equal(1, checkrelation.tags.length, "saved relation does not contain exactly one tag")
340 assert_equal changeset.id, checkrelation.changeset.id,
341 "saved relation does not belong in the changeset it was assigned to"
342 assert_equal user.id, checkrelation.changeset.user_id,
343 "saved relation does not belong to user that created it"
344 assert checkrelation.visible,
345 "saved relation is not visible"
346 # ok the relation is there but can we also retrieve it?
348 get api_relation_path(relationid)
349 assert_response :success
352 # create an relation with a node as member, this time test that we don't
353 # need a role attribute to be included
356 <relation changeset='#{changeset.id}'>
357 <member ref='#{node.id}' type='node'/>
358 <tag k='test' v='yes' />
362 post api_relations_path, :params => xml, :headers => auth_header
364 assert_response :success,
365 "relation upload did not return success status"
366 # read id of created relation and search for it
367 relationid = @response.body
368 checkrelation = Relation.find(relationid)
369 assert_not_nil checkrelation,
370 "uploaded relation not found in data base after upload"
372 assert_equal(1, checkrelation.members.length, "saved relation does not contain exactly one member")
373 assert_equal(1, checkrelation.tags.length, "saved relation does not contain exactly one tag")
374 assert_equal changeset.id, checkrelation.changeset.id,
375 "saved relation does not belong in the changeset it was assigned to"
376 assert_equal user.id, checkrelation.changeset.user_id,
377 "saved relation does not belong to user that created it"
378 assert checkrelation.visible,
379 "saved relation is not visible"
380 # ok the relation is there but can we also retrieve it?
382 get api_relation_path(relationid)
383 assert_response :success
386 # create an relation with a way and a node as members
389 <relation changeset='#{changeset.id}'>
390 <member type='node' ref='#{node.id}' role='some'/>
391 <member type='way' ref='#{way.id}' role='other'/>
392 <tag k='test' v='yes' />
396 post api_relations_path, :params => xml, :headers => auth_header
398 assert_response :success,
399 "relation upload did not return success status"
400 # read id of created relation and search for it
401 relationid = @response.body
402 checkrelation = Relation.find(relationid)
403 assert_not_nil checkrelation,
404 "uploaded relation not found in data base after upload"
406 assert_equal(2, checkrelation.members.length, "saved relation does not have exactly two members")
407 assert_equal(1, checkrelation.tags.length, "saved relation does not contain exactly one tag")
408 assert_equal changeset.id, checkrelation.changeset.id,
409 "saved relation does not belong in the changeset it was assigned to"
410 assert_equal user.id, checkrelation.changeset.user_id,
411 "saved relation does not belong to user that created it"
412 assert checkrelation.visible,
413 "saved relation is not visible"
414 # ok the relation is there but can we also retrieve it?
415 get api_relation_path(relationid)
416 assert_response :success
419 # ------------------------------------
420 # Test updating relations
421 # ------------------------------------
423 def test_update_wrong_id
425 changeset = create(:changeset, :user => user)
426 relation = create(:relation)
427 other_relation = create(:relation)
429 auth_header = bearer_authorization_header user
430 get api_relation_path(relation)
431 assert_response :success
432 rel = XML::Parser.string(@response.body).parse
434 update_changeset(rel, changeset.id)
435 put api_relation_path(other_relation), :params => rel.to_s, :headers => auth_header
436 assert_response :bad_request
439 # -------------------------------------
440 # Test creating some invalid relations.
441 # -------------------------------------
443 def test_create_invalid
445 changeset = create(:changeset, :user => user)
447 auth_header = bearer_authorization_header user
449 # create a relation with non-existing node as member
452 <relation changeset='#{changeset.id}'>
453 <member type='node' ref='0'/>
457 post api_relations_path, :params => xml, :headers => auth_header
459 assert_response :precondition_failed,
460 "relation upload with invalid node did not return 'precondition failed'"
461 assert_equal "Precondition failed: Relation with id cannot be saved due to Node with id 0", @response.body
464 # -------------------------------------
465 # Test creating a relation, with some invalid XML
466 # -------------------------------------
467 def test_create_invalid_xml
469 changeset = create(:changeset, :user => user)
472 auth_header = bearer_authorization_header user
474 # create some xml that should return an error
477 <relation changeset='#{changeset.id}'>
478 <member type='type' ref='#{node.id}' role=''/>
482 post api_relations_path, :params => xml, :headers => auth_header
484 assert_response :bad_request
485 assert_match(/Cannot parse valid relation from xml string/, @response.body)
486 assert_match(/The type is not allowed only, /, @response.body)
489 # -------------------------------------
490 # Test deleting relations.
491 # -------------------------------------
494 private_user = create(:user, :data_public => false)
495 private_user_closed_changeset = create(:changeset, :closed, :user => private_user)
497 closed_changeset = create(:changeset, :closed, :user => user)
498 changeset = create(:changeset, :user => user)
499 relation = create(:relation)
500 used_relation = create(:relation)
501 super_relation = create(:relation_member, :member => used_relation).relation
502 deleted_relation = create(:relation, :deleted)
503 multi_tag_relation = create(:relation)
504 create_list(:relation_tag, 4, :relation => multi_tag_relation)
506 ## First try to delete relation without auth
507 delete api_relation_path(relation)
508 assert_response :unauthorized
510 ## Then try with the private user, to make sure that you get a forbidden
511 auth_header = bearer_authorization_header private_user
513 # this shouldn't work, as we should need the payload...
514 delete api_relation_path(relation), :headers => auth_header
515 assert_response :forbidden
517 # try to delete without specifying a changeset
518 xml = "<osm><relation id='#{relation.id}'/></osm>"
519 delete api_relation_path(relation), :params => xml.to_s, :headers => auth_header
520 assert_response :forbidden
522 # try to delete with an invalid (closed) changeset
523 xml = update_changeset(xml_for_relation(relation),
524 private_user_closed_changeset.id)
525 delete api_relation_path(relation), :params => xml.to_s, :headers => auth_header
526 assert_response :forbidden
528 # try to delete with an invalid (non-existent) changeset
529 xml = update_changeset(xml_for_relation(relation), 0)
530 delete api_relation_path(relation), :params => xml.to_s, :headers => auth_header
531 assert_response :forbidden
533 # this won't work because the relation is in-use by another relation
534 xml = xml_for_relation(used_relation)
535 delete api_relation_path(used_relation), :params => xml.to_s, :headers => auth_header
536 assert_response :forbidden
538 # this should work when we provide the appropriate payload...
539 xml = xml_for_relation(relation)
540 delete api_relation_path(relation), :params => xml.to_s, :headers => auth_header
541 assert_response :forbidden
543 # this won't work since the relation is already deleted
544 xml = xml_for_relation(deleted_relation)
545 delete api_relation_path(deleted_relation), :params => xml.to_s, :headers => auth_header
546 assert_response :forbidden
548 # this won't work since the relation never existed
549 delete api_relation_path(0), :headers => auth_header
550 assert_response :forbidden
552 ## now set auth for the public user
553 auth_header = bearer_authorization_header user
555 # this shouldn't work, as we should need the payload...
556 delete api_relation_path(relation), :headers => auth_header
557 assert_response :bad_request
559 # try to delete without specifying a changeset
560 xml = "<osm><relation id='#{relation.id}' version='#{relation.version}' /></osm>"
561 delete api_relation_path(relation), :params => xml.to_s, :headers => auth_header
562 assert_response :bad_request
563 assert_match(/Changeset id is missing/, @response.body)
565 # try to delete with an invalid (closed) changeset
566 xml = update_changeset(xml_for_relation(relation),
568 delete api_relation_path(relation), :params => xml.to_s, :headers => auth_header
569 assert_response :conflict
571 # try to delete with an invalid (non-existent) changeset
572 xml = update_changeset(xml_for_relation(relation), 0)
573 delete api_relation_path(relation), :params => xml.to_s, :headers => auth_header
574 assert_response :conflict
576 # this won't work because the relation is in a changeset owned by someone else
577 xml = update_changeset(xml_for_relation(relation), create(:changeset).id)
578 delete api_relation_path(relation), :params => xml.to_s, :headers => auth_header
579 assert_response :conflict,
580 "shouldn't be able to delete a relation in a changeset owned by someone else (#{@response.body})"
582 # this won't work because the relation in the payload is different to that passed
583 xml = update_changeset(xml_for_relation(relation), changeset.id)
584 delete api_relation_path(create(:relation)), :params => xml.to_s, :headers => auth_header
585 assert_response :bad_request, "shouldn't be able to delete a relation when payload is different to the url"
587 # this won't work because the relation is in-use by another relation
588 xml = update_changeset(xml_for_relation(used_relation), changeset.id)
589 delete api_relation_path(used_relation), :params => xml.to_s, :headers => auth_header
590 assert_response :precondition_failed,
591 "shouldn't be able to delete a relation used in a relation (#{@response.body})"
592 assert_equal "Precondition failed: The relation #{used_relation.id} is used in relation #{super_relation.id}.", @response.body
594 # this should work when we provide the appropriate payload...
595 xml = update_changeset(xml_for_relation(multi_tag_relation), changeset.id)
596 delete api_relation_path(multi_tag_relation), :params => xml.to_s, :headers => auth_header
597 assert_response :success
599 # valid delete should return the new version number, which should
600 # be greater than the old version number
601 assert_operator @response.body.to_i, :>, multi_tag_relation.version, "delete request should return a new version number for relation"
603 # this won't work since the relation is already deleted
604 xml = update_changeset(xml_for_relation(deleted_relation), changeset.id)
605 delete api_relation_path(deleted_relation), :params => xml.to_s, :headers => auth_header
606 assert_response :gone
608 # Public visible relation needs to be deleted
609 xml = update_changeset(xml_for_relation(super_relation), changeset.id)
610 delete api_relation_path(super_relation), :params => xml.to_s, :headers => auth_header
611 assert_response :success
613 # this works now because the relation which was using this one
615 xml = update_changeset(xml_for_relation(used_relation), changeset.id)
616 delete api_relation_path(used_relation), :params => xml.to_s, :headers => auth_header
617 assert_response :success,
618 "should be able to delete a relation used in an old relation (#{@response.body})"
620 # this won't work since the relation never existed
621 delete api_relation_path(0), :headers => auth_header
622 assert_response :not_found
626 # test initial rate limit
627 def test_initial_rate_limit
632 node1 = create(:node)
633 node2 = create(:node)
635 # create a changeset that puts us near the initial rate limit
636 changeset = create(:changeset, :user => user,
637 :created_at => Time.now.utc - 5.minutes,
638 :num_changes => Settings.initial_changes_per_hour - 1)
640 # create authentication header
641 auth_header = bearer_authorization_header user
643 # try creating a relation
646 <relation changeset='#{changeset.id}'>
647 <member ref='#{node1.id}' type='node' role='some'/>
648 <member ref='#{node2.id}' type='node' role='some'/>
652 post api_relations_path, :params => xml, :headers => auth_header
653 assert_response :success, "relation create did not return success status"
655 # get the id of the relation we created
656 relationid = @response.body
658 # try updating the relation, which should be rate limited
661 <relation id='#{relationid}' version='1' changeset='#{changeset.id}'>
662 <member ref='#{node2.id}' type='node' role='some'/>
663 <member ref='#{node1.id}' type='node' role='some'/>
667 put api_relation_path(relationid), :params => xml, :headers => auth_header
668 assert_response :too_many_requests, "relation update did not hit rate limit"
670 # try deleting the relation, which should be rate limited
671 xml = "<osm><relation id='#{relationid}' version='2' changeset='#{changeset.id}'/></osm>"
672 delete api_relation_path(relationid), :params => xml, :headers => auth_header
673 assert_response :too_many_requests, "relation delete did not hit rate limit"
675 # try creating a relation, which should be rate limited
678 <relation changeset='#{changeset.id}'>
679 <member ref='#{node1.id}' type='node' role='some'/>
680 <member ref='#{node2.id}' type='node' role='some'/>
684 post api_relations_path, :params => xml, :headers => auth_header
685 assert_response :too_many_requests, "relation create did not hit rate limit"
689 # test maximum rate limit
690 def test_maximum_rate_limit
695 node1 = create(:node)
696 node2 = create(:node)
698 # create a changeset to establish our initial edit time
699 changeset = create(:changeset, :user => user,
700 :created_at => Time.now.utc - 28.days)
702 # create changeset to put us near the maximum rate limit
703 total_changes = Settings.max_changes_per_hour - 1
704 while total_changes.positive?
705 changes = [total_changes, Changeset::MAX_ELEMENTS].min
706 changeset = create(:changeset, :user => user,
707 :created_at => Time.now.utc - 5.minutes,
708 :num_changes => changes)
709 total_changes -= changes
712 # create authentication header
713 auth_header = bearer_authorization_header user
715 # try creating a relation
718 <relation changeset='#{changeset.id}'>
719 <member ref='#{node1.id}' type='node' role='some'/>
720 <member ref='#{node2.id}' type='node' role='some'/>
724 post api_relations_path, :params => xml, :headers => auth_header
725 assert_response :success, "relation create did not return success status"
727 # get the id of the relation we created
728 relationid = @response.body
730 # try updating the relation, which should be rate limited
733 <relation id='#{relationid}' version='1' changeset='#{changeset.id}'>
734 <member ref='#{node2.id}' type='node' role='some'/>
735 <member ref='#{node1.id}' type='node' role='some'/>
739 put api_relation_path(relationid), :params => xml, :headers => auth_header
740 assert_response :too_many_requests, "relation update did not hit rate limit"
742 # try deleting the relation, which should be rate limited
743 xml = "<osm><relation id='#{relationid}' version='2' changeset='#{changeset.id}'/></osm>"
744 delete api_relation_path(relationid), :params => xml, :headers => auth_header
745 assert_response :too_many_requests, "relation delete did not hit rate limit"
747 # try creating a relation, which should be rate limited
750 <relation changeset='#{changeset.id}'>
751 <member ref='#{node1.id}' type='node' role='some'/>
752 <member ref='#{node2.id}' type='node' role='some'/>
756 post api_relations_path, :params => xml, :headers => auth_header
757 assert_response :too_many_requests, "relation create did not hit rate limit"
763 [Relation, RelationTag, RelationMember,
764 OldRelation, OldRelationTag, OldRelationMember]
768 # update an attribute in the node element
769 def xml_attr_rewrite(xml, name, value)
770 xml.find("//osm/relation").first[name] = value.to_s