]> git.openstreetmap.org Git - rails.git/blob - test/controllers/api/relations_controller_test.rb
Lock changeset in api create element actions
[rails.git] / test / controllers / api / relations_controller_test.rb
1 require "test_helper"
2 require_relative "elements_test_helper"
3
4 module Api
5   class RelationsControllerTest < ActionDispatch::IntegrationTest
6     include ElementsTestHelper
7
8     ##
9     # test all routes which lead to this controller
10     def test_routes
11       assert_routing(
12         { :path => "/api/0.6/relations", :method => :get },
13         { :controller => "api/relations", :action => "index" }
14       )
15       assert_routing(
16         { :path => "/api/0.6/relations.json", :method => :get },
17         { :controller => "api/relations", :action => "index", :format => "json" }
18       )
19       assert_routing(
20         { :path => "/api/0.6/relations", :method => :post },
21         { :controller => "api/relations", :action => "create" }
22       )
23       assert_routing(
24         { :path => "/api/0.6/relation/1", :method => :get },
25         { :controller => "api/relations", :action => "show", :id => "1" }
26       )
27       assert_routing(
28         { :path => "/api/0.6/relation/1.json", :method => :get },
29         { :controller => "api/relations", :action => "show", :id => "1", :format => "json" }
30       )
31       assert_routing(
32         { :path => "/api/0.6/relation/1/full", :method => :get },
33         { :controller => "api/relations", :action => "show", :full => true, :id => "1" }
34       )
35       assert_routing(
36         { :path => "/api/0.6/relation/1/full.json", :method => :get },
37         { :controller => "api/relations", :action => "show", :full => true, :id => "1", :format => "json" }
38       )
39       assert_routing(
40         { :path => "/api/0.6/relation/1", :method => :put },
41         { :controller => "api/relations", :action => "update", :id => "1" }
42       )
43       assert_routing(
44         { :path => "/api/0.6/relation/1", :method => :delete },
45         { :controller => "api/relations", :action => "destroy", :id => "1" }
46       )
47
48       assert_recognizes(
49         { :controller => "api/relations", :action => "create" },
50         { :path => "/api/0.6/relation/create", :method => :put }
51       )
52     end
53
54     ##
55     # test fetching multiple relations
56     def test_index
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))
62
63       # check error when no parameter provided
64       get api_relations_path
65       assert_response :bad_request
66
67       # check error when no parameter value provided
68       get api_relations_path(:relations => "")
69       assert_response :bad_request
70
71       # test a working call
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
80       end
81
82       # test a working call with json format
83       get api_relations_path(:relations => "#{relation1.id},#{relation2.id},#{relation3.id},#{relation4.id}", :format => "json")
84
85       js = ActiveSupport::JSON.decode(@response.body)
86       assert_not_nil js
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? })
93
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
97     end
98
99     # -------------------------------------
100     # Test showing relations.
101     # -------------------------------------
102
103     def test_show_not_found
104       get api_relation_path(0)
105       assert_response :not_found
106     end
107
108     def test_show_deleted
109       get api_relation_path(create(:relation, :deleted))
110       assert_response :gone
111     end
112
113     def test_show
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)
117
118       get api_relation_path(relation)
119
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
126       end
127     end
128
129     def test_full_not_found
130       get api_relation_path(999999, :full => true)
131       assert_response :not_found
132     end
133
134     def test_full_deleted
135       get api_relation_path(create(:relation, :deleted), :full => true)
136       assert_response :gone
137     end
138
139     def test_full_empty
140       relation = create(:relation)
141
142       get api_relation_path(relation, :full => true)
143
144       assert_response :success
145       assert_dom "relation", :count => 1 do
146         assert_dom "> @id", :text => relation.id.to_s
147       end
148     end
149
150     def test_full_with_node_member
151       relation = create(:relation)
152       node = create(:node)
153       create(:relation_member, :relation => relation, :member => node)
154
155       get api_relation_path(relation, :full => true)
156
157       assert_response :success
158       assert_dom "node", :count => 1 do
159         assert_dom "> @id", :text => node.id.to_s
160       end
161       assert_dom "relation", :count => 1 do
162         assert_dom "> @id", :text => relation.id.to_s
163       end
164     end
165
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)
170
171       get api_relation_path(relation, :full => true)
172
173       assert_response :success
174       assert_dom "node", :count => 1 do
175         assert_dom "> @id", :text => way.nodes[0].id.to_s
176       end
177       assert_dom "way", :count => 1 do
178         assert_dom "> @id", :text => way.id.to_s
179       end
180       assert_dom "relation", :count => 1 do
181         assert_dom "> @id", :text => relation.id.to_s
182       end
183     end
184
185     def test_full_with_node_member_json
186       relation = create(:relation)
187       node = create(:node)
188       create(:relation_member, :relation => relation, :member => node)
189
190       get api_relation_path(relation, :full => true, :format => "json")
191
192       assert_response :success
193       js = ActiveSupport::JSON.decode(@response.body)
194       assert_not_nil js
195       assert_equal 2, js["elements"].count
196
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"]
203
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"]
207     end
208
209     # -------------------------------------
210     # Test creating relations.
211     # -------------------------------------
212
213     def test_create_without_members_by_private_user
214       with_unchanging_request([:data_public => false]) do |headers, changeset|
215         osm = <<~OSM
216           <osm>
217             <relation changeset='#{changeset.id}'>
218               <tag k='test' v='yes' />
219             </relation>
220           </osm>
221         OSM
222
223         post api_relations_path, :params => osm, :headers => headers
224
225         assert_response :forbidden, "relation upload should have failed with forbidden"
226       end
227     end
228
229     def test_create_with_node_member_with_role_by_private_user
230       node = create(:node)
231
232       with_unchanging_request([:data_public => false]) do |headers, changeset|
233         osm = <<~OSM
234           <osm>
235             <relation changeset='#{changeset.id}'>
236               <member ref='#{node.id}' type='node' role='some'/>
237               <tag k='test' v='yes' />
238             </relation>
239           </osm>
240         OSM
241
242         post api_relations_path, :params => osm, :headers => headers
243
244         assert_response :forbidden, "relation upload did not return forbidden status"
245       end
246     end
247
248     def test_create_with_node_member_without_role_by_private_user
249       node = create(:node)
250
251       with_unchanging_request([:data_public => false]) do |headers, changeset|
252         osm = <<~OSM
253           <osm>
254             <relation changeset='#{changeset.id}'>
255               <member ref='#{node.id}' type='node'/>
256               <tag k='test' v='yes' />
257             </relation>
258           </osm>
259         OSM
260
261         post api_relations_path, :params => osm, :headers => headers
262
263         assert_response :forbidden, "relation upload did not return forbidden status"
264       end
265     end
266
267     def test_create_with_node_and_way_members_by_private_user
268       node = create(:node)
269       way = create(:way_with_nodes, :nodes_count => 2)
270
271       with_unchanging_request([:data_public => false]) do |headers, changeset|
272         osm = <<~OSM
273           <osm>
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' />
278             </relation>
279           </osm>
280         OSM
281
282         post api_relations_path, :params => osm, :headers => headers
283
284         assert_response :forbidden, "relation upload did not return success status"
285       end
286     end
287
288     def test_create_without_members
289       with_request do |headers, changeset|
290         assert_difference "Relation.count" => 1,
291                           "RelationMember.count" => 0 do
292           osm = <<~OSM
293             <osm>
294               <relation changeset='#{changeset.id}'>
295                 <tag k='test' v='yes' />
296               </relation>
297             </osm>
298           OSM
299
300           post api_relations_path, :params => osm, :headers => headers
301
302           assert_response :success, "relation upload did not return success status"
303         end
304
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"
311
312         changeset.reload
313         assert_equal 1, changeset.num_changes
314         assert_predicate changeset, :num_type_changes_in_sync?
315         assert_equal 1, changeset.num_created_relations
316       end
317     end
318
319     def test_create_with_node_member_with_role
320       node = create(:node)
321
322       with_request do |headers, changeset|
323         assert_difference "Relation.count" => 1,
324                           "RelationMember.count" => 1 do
325           osm = <<~OSM
326             <osm>
327               <relation changeset='#{changeset.id}'>
328                 <member ref='#{node.id}' type='node' role='some'/>
329                 <tag k='test' v='yes' />
330               </relation>
331             </osm>
332           OSM
333
334           post api_relations_path, :params => osm, :headers => headers
335
336           assert_response :success, "relation upload did not return success status"
337         end
338
339         created_relation_id = @response.body
340         relation = Relation.find(created_relation_id)
341         assert_equal [["Node", node.id, "some"]], relation.members
342         assert_equal({ "test" => "yes" }, relation.tags)
343         assert_equal changeset.id, relation.changeset_id, "saved relation does not belong in the changeset it was assigned to"
344         assert relation.visible, "saved relation is not visible"
345
346         changeset.reload
347         assert_equal 1, changeset.num_changes
348         assert_predicate changeset, :num_type_changes_in_sync?
349         assert_equal 1, changeset.num_created_relations
350       end
351     end
352
353     def test_create_with_node_member_without_role
354       node = create(:node)
355
356       with_request do |headers, changeset|
357         assert_difference "Relation.count" => 1,
358                           "RelationMember.count" => 1 do
359           osm = <<~OSM
360             <osm>
361               <relation changeset='#{changeset.id}'>
362                 <member ref='#{node.id}' type='node'/>
363                 <tag k='test' v='yes' />
364               </relation>
365             </osm>
366           OSM
367
368           post api_relations_path, :params => osm, :headers => headers
369
370           assert_response :success, "relation upload did not return success status"
371         end
372
373         created_relation_id = @response.body
374         relation = Relation.find(created_relation_id)
375         assert_equal [["Node", node.id, ""]], relation.members
376         assert_equal({ "test" => "yes" }, relation.tags)
377         assert_equal changeset.id, relation.changeset_id, "saved relation does not belong in the changeset it was assigned to"
378         assert relation.visible, "saved relation is not visible"
379
380         changeset.reload
381         assert_equal 1, changeset.num_changes
382         assert_predicate changeset, :num_type_changes_in_sync?
383         assert_equal 1, changeset.num_created_relations
384       end
385     end
386
387     def test_create_with_node_and_way_members
388       node = create(:node)
389       way = create(:way_with_nodes, :nodes_count => 2)
390
391       with_request do |headers, changeset|
392         assert_difference "Relation.count" => 1,
393                           "RelationMember.count" => 2 do
394           osm = <<~OSM
395             <osm>
396               <relation changeset='#{changeset.id}'>
397                 <member type='node' ref='#{node.id}' role='some'/>
398                 <member type='way' ref='#{way.id}' role='other'/>
399                 <tag k='test' v='yes' />
400               </relation>
401             </osm>
402           OSM
403
404           post api_relations_path, :params => osm, :headers => headers
405
406           assert_response :success, "relation upload did not return success status"
407         end
408
409         created_relation_id = @response.body
410         relation = Relation.find(created_relation_id)
411         assert_equal [["Node", node.id, "some"],
412                       ["Way", way.id, "other"]], relation.members
413         assert_equal({ "test" => "yes" }, relation.tags)
414         assert_equal changeset.id, relation.changeset_id, "saved relation does not belong in the changeset it was assigned to"
415         assert relation.visible, "saved relation is not visible"
416
417         changeset.reload
418         assert_equal 1, changeset.num_changes
419         assert_predicate changeset, :num_type_changes_in_sync?
420         assert_equal 1, changeset.num_created_relations
421       end
422     end
423
424     def test_create_in_missing_changeset
425       node = create(:node)
426
427       with_unchanging_request do |headers|
428         osm = <<~OSM
429           <osm>
430             <relation changeset='0'>
431               <member type='node' ref='#{node.id}' role='some'/>
432             </relation>
433           </osm>
434         OSM
435
436         post api_relations_path, :params => osm, :headers => headers
437
438         assert_response :conflict
439       end
440     end
441
442     def test_create_with_missing_node_member
443       with_unchanging_request do |headers, changeset|
444         osm = <<~OSM
445           <osm>
446             <relation changeset='#{changeset.id}'>
447               <member type='node' ref='0'/>
448             </relation>
449           </osm>
450         OSM
451
452         post api_relations_path, :params => osm, :headers => headers
453
454         assert_response :precondition_failed, "relation upload with invalid node did not return 'precondition failed'"
455         assert_equal "Precondition failed: Relation with id  cannot be saved due to Node with id 0", @response.body
456       end
457     end
458
459     def test_create_with_invalid_member_type
460       node = create(:node)
461
462       with_unchanging_request do |headers, changeset|
463         osm = <<~OSM
464           <osm>
465             <relation changeset='#{changeset.id}'>
466               <member type='type' ref='#{node.id}' role=''/>
467             </relation>
468           </osm>
469         OSM
470
471         post api_relations_path, :params => osm, :headers => headers
472
473         assert_response :bad_request
474         assert_match(/Cannot parse valid relation from xml string/, @response.body)
475         assert_match(/The type is not allowed only, /, @response.body)
476       end
477     end
478
479     def test_create_and_show
480       user = create(:user)
481       changeset = create(:changeset, :user => user)
482
483       osm = <<~OSM
484         <osm>
485           <relation changeset='#{changeset.id}'/>
486         </osm>
487       OSM
488
489       post api_relations_path, :params => osm, :headers => bearer_authorization_header(user)
490
491       assert_response :success, "relation upload did not return success status"
492
493       created_relation_id = @response.body
494
495       get api_relation_path(created_relation_id)
496
497       assert_response :success
498     end
499
500     def test_create_race_condition
501       user = create(:user)
502       changeset = create(:changeset, :user => user)
503       node = create(:node)
504       auth_header = bearer_authorization_header user
505       path = api_relations_path
506       concurrency_level = 16
507
508       threads = Array.new(concurrency_level) do
509         Thread.new do
510           osm = <<~OSM
511             <osm>
512               <relation changeset='#{changeset.id}'>
513                 <member type='node' ref='#{node.id}' role=''/>
514               </relation>
515             </osm>
516           OSM
517           post path, :params => osm, :headers => auth_header
518         end
519       end
520       threads.each(&:join)
521
522       changeset.reload
523       assert_equal concurrency_level, changeset.num_changes
524       assert_predicate changeset, :num_type_changes_in_sync?
525       assert_equal concurrency_level, changeset.num_created_relations
526     end
527
528     # ------------------------------------
529     # Test updating relations
530     # ------------------------------------
531
532     def test_update
533       relation = create(:relation)
534
535       with_request do |headers, changeset|
536         osm_xml = xml_for_relation relation
537         osm_xml = update_changeset osm_xml, changeset.id
538
539         put api_relation_path(relation), :params => osm_xml.to_s, :headers => headers
540
541         assert_response :success
542
543         relation.reload
544         assert_equal 2, relation.version
545
546         changeset.reload
547         assert_equal 1, changeset.num_changes
548         assert_predicate changeset, :num_type_changes_in_sync?
549         assert_equal 1, changeset.num_modified_relations
550       end
551     end
552
553     def test_update_in_missing_changeset
554       with_unchanging(:relation) do |relation|
555         with_unchanging_request do |headers|
556           osm_xml = xml_for_relation relation
557           osm_xml = update_changeset osm_xml, 0
558
559           put api_relation_path(relation), :params => osm_xml.to_s, :headers => headers
560
561           assert_response :conflict, "update with changeset=0 should be rejected"
562         end
563       end
564     end
565
566     def test_update_other_relation
567       with_unchanging(:relation) do |relation|
568         with_unchanging(:relation) do |other_relation|
569           with_unchanging_request do |headers, changeset|
570             osm_xml = xml_for_relation other_relation
571             osm_xml = update_changeset osm_xml, changeset.id
572
573             put api_relation_path(relation), :params => osm_xml.to_s, :headers => headers
574
575             assert_response :bad_request
576           end
577         end
578       end
579     end
580
581     # -------------------------------------
582     # Test deleting relations.
583     # -------------------------------------
584
585     def test_destroy_when_unauthorized
586       with_unchanging(:relation) do |relation|
587         delete api_relation_path(relation)
588
589         assert_response :unauthorized
590       end
591     end
592
593     def test_destroy_without_payload_by_private_user
594       with_unchanging(:relation) do |relation|
595         with_unchanging_request([:data_public => false]) do |headers|
596           delete api_relation_path(relation), :headers => headers
597
598           assert_response :forbidden
599         end
600       end
601     end
602
603     def test_destroy_without_changeset_id_by_private_user
604       with_unchanging(:relation) do |relation|
605         with_unchanging_request([:data_public => false]) do |headers|
606           osm = "<osm><relation id='#{relation.id}' version='#{relation.version}'/></osm>"
607
608           delete api_relation_path(relation), :params => osm, :headers => headers
609
610           assert_response :forbidden
611         end
612       end
613     end
614
615     def test_destroy_in_closed_changeset_by_private_user
616       with_unchanging(:relation) do |relation|
617         with_unchanging_request([:data_public => false], [:closed]) do |headers, changeset|
618           osm_xml = xml_for_relation relation
619           osm_xml = update_changeset osm_xml, changeset.id
620
621           delete api_relation_path(relation), :params => osm_xml.to_s, :headers => headers
622
623           assert_response :forbidden
624         end
625       end
626     end
627
628     def test_destroy_in_missing_changeset_by_private_user
629       with_unchanging(:relation) do |relation|
630         with_unchanging_request([:data_public => false]) do |headers|
631           osm_xml = xml_for_relation relation
632           osm_xml = update_changeset osm_xml, 0
633
634           delete api_relation_path(relation), :params => osm_xml.to_s, :headers => headers
635
636           assert_response :forbidden
637         end
638       end
639     end
640
641     def test_destroy_relation_used_by_other_relation_by_private_user
642       with_unchanging(:relation) do |relation|
643         create(:relation_member, :member => relation)
644
645         with_unchanging_request([:data_public => false]) do |headers, changeset|
646           osm_xml = xml_for_relation relation
647           osm_xml = update_changeset osm_xml, changeset.id
648
649           delete api_relation_path(relation), :params => osm_xml.to_s, :headers => headers
650
651           assert_response :forbidden
652         end
653       end
654     end
655
656     def test_destroy_by_private_user
657       with_unchanging(:relation) do |relation|
658         with_unchanging_request([:data_public => false]) do |headers, changeset|
659           osm_xml = xml_for_relation relation
660           osm_xml = update_changeset osm_xml, changeset.id
661
662           delete api_relation_path(relation), :params => osm_xml.to_s, :headers => headers
663
664           assert_response :forbidden
665         end
666       end
667     end
668
669     def test_destroy_deleted_relation_by_private_user
670       with_unchanging(:relation, :deleted) do |relation|
671         with_unchanging_request([:data_public => false]) do |headers, changeset|
672           osm_xml = xml_for_relation relation
673           osm_xml = update_changeset osm_xml, changeset.id
674
675           delete api_relation_path(relation), :params => osm_xml.to_s, :headers => headers
676
677           assert_response :forbidden
678         end
679       end
680     end
681
682     def test_destroy_missing_relation_by_private_user
683       with_unchanging_request([:data_public => false]) do |headers|
684         delete api_relation_path(0), :headers => headers
685
686         assert_response :forbidden
687       end
688     end
689
690     def test_destroy_without_payload
691       with_unchanging(:relation) do |relation|
692         with_unchanging_request do |headers|
693           delete api_relation_path(relation), :headers => headers
694
695           assert_response :bad_request
696         end
697       end
698     end
699
700     def test_destroy_without_changeset_id
701       with_unchanging(:relation) do |relation|
702         with_unchanging_request do |headers|
703           osm = "<osm><relation id='#{relation.id}' version='#{relation.version}'/></osm>"
704
705           delete api_relation_path(relation), :params => osm, :headers => headers
706
707           assert_response :bad_request
708           assert_match(/Changeset id is missing/, @response.body)
709         end
710       end
711     end
712
713     def test_destroy_in_closed_changeset
714       with_unchanging(:relation) do |relation|
715         with_unchanging_request([], [:closed]) do |headers, changeset|
716           osm_xml = xml_for_relation relation
717           osm_xml = update_changeset osm_xml, changeset.id
718
719           delete api_relation_path(relation), :params => osm_xml.to_s, :headers => headers
720
721           assert_response :conflict
722         end
723       end
724     end
725
726     def test_destroy_in_missing_changeset
727       with_unchanging(:relation) do |relation|
728         with_unchanging_request do |headers|
729           osm_xml = xml_for_relation relation
730           osm_xml = update_changeset osm_xml, 0
731
732           delete api_relation_path(relation), :params => osm_xml.to_s, :headers => headers
733
734           assert_response :conflict
735         end
736       end
737     end
738
739     def test_destroy_in_changeset_of_other_user
740       with_unchanging(:relation) do |relation|
741         other_user = create(:user)
742
743         with_unchanging_request([], [:user => other_user]) do |headers, changeset|
744           osm_xml = xml_for_relation relation
745           osm_xml = update_changeset osm_xml, changeset.id
746
747           delete api_relation_path(relation), :params => osm_xml.to_s, :headers => headers
748
749           assert_response :conflict, "shouldn't be able to delete a relation in a changeset owned by someone else (#{@response.body})"
750         end
751       end
752     end
753
754     def test_destroy_other_relation
755       with_unchanging(:relation) do |relation|
756         with_unchanging(:relation) do |other_relation|
757           with_unchanging_request do |headers, changeset|
758             osm_xml = xml_for_relation other_relation
759             osm_xml = update_changeset osm_xml, changeset.id
760
761             delete api_relation_path(relation), :params => osm_xml.to_s, :headers => headers
762
763             assert_response :bad_request, "shouldn't be able to delete a relation when payload is different to the url"
764           end
765         end
766       end
767     end
768
769     def test_destroy_relation_used_by_other_relation
770       with_unchanging(:relation) do |relation|
771         super_relation = create(:relation)
772         create(:relation_member, :relation => super_relation, :member => relation)
773
774         with_unchanging_request do |headers, changeset|
775           osm_xml = xml_for_relation relation
776           osm_xml = update_changeset osm_xml, changeset.id
777
778           delete api_relation_path(relation), :params => osm_xml.to_s, :headers => headers
779
780           assert_response :precondition_failed, "shouldn't be able to delete a relation used in a relation (#{@response.body})"
781           assert_equal "Precondition failed: The relation #{relation.id} is used in relation #{super_relation.id}.", @response.body
782         end
783       end
784     end
785
786     def test_destroy
787       relation = create(:relation)
788       create_list(:relation_tag, 4, :relation => relation)
789
790       with_request do |headers, changeset|
791         osm_xml = xml_for_relation relation
792         osm_xml = update_changeset osm_xml, changeset.id
793
794         delete api_relation_path(relation), :params => osm_xml.to_s, :headers => headers
795
796         assert_response :success
797         assert_operator @response.body.to_i, :>, relation.version, "delete request should return a new version number for relation"
798
799         changeset.reload
800         assert_equal 1, changeset.num_changes
801         assert_predicate changeset, :num_type_changes_in_sync?
802         assert_equal 1, changeset.num_deleted_relations
803       end
804     end
805
806     def test_destroy_deleted_relation
807       with_unchanging(:relation, :deleted) do |relation|
808         with_unchanging_request do |headers, changeset|
809           osm_xml = xml_for_relation relation
810           osm_xml = update_changeset osm_xml, changeset.id
811
812           delete api_relation_path(relation), :params => osm_xml.to_s, :headers => headers
813
814           assert_response :gone
815         end
816       end
817     end
818
819     def test_destroy_super_relation_then_used_relation
820       used_relation = create(:relation)
821       super_relation = create(:relation)
822       create(:relation_member, :relation => super_relation, :member => used_relation)
823
824       with_request do |headers, changeset|
825         osm_xml = xml_for_relation super_relation
826         osm_xml = update_changeset osm_xml, changeset.id
827
828         delete api_relation_path(super_relation), :params => osm_xml.to_s, :headers => headers
829
830         assert_response :success
831       end
832
833       with_request do |headers, changeset|
834         osm_xml = xml_for_relation used_relation
835         osm_xml = update_changeset osm_xml, changeset.id
836
837         delete api_relation_path(used_relation), :params => osm_xml.to_s, :headers => headers
838
839         assert_response :success, "should be able to delete a relation used in an old relation (#{@response.body})"
840       end
841     end
842
843     def test_destroy_missing_relation
844       with_unchanging_request do |headers|
845         delete api_relation_path(0), :headers => headers
846
847         assert_response :not_found
848       end
849     end
850
851     ##
852     # test initial rate limit
853     def test_initial_rate_limit
854       # create a user
855       user = create(:user)
856
857       # create some nodes
858       node1 = create(:node)
859       node2 = create(:node)
860
861       # create a changeset that puts us near the initial rate limit
862       changeset = create(:changeset, :user => user,
863                                      :created_at => Time.now.utc - 5.minutes,
864                                      :num_changes => Settings.initial_changes_per_hour - 1)
865
866       # create authentication header
867       auth_header = bearer_authorization_header user
868
869       # try creating a relation
870       xml = <<~OSM
871         <osm>
872           <relation changeset='#{changeset.id}'>
873             <member ref='#{node1.id}' type='node' role='some'/>
874             <member ref='#{node2.id}' type='node' role='some'/>
875           </relation>
876         </osm>
877       OSM
878       post api_relations_path, :params => xml, :headers => auth_header
879       assert_response :success, "relation create did not return success status"
880
881       # get the id of the relation we created
882       relationid = @response.body
883
884       # try updating the relation, which should be rate limited
885       xml = <<~OSM
886         <osm>
887           <relation id='#{relationid}' version='1' changeset='#{changeset.id}'>
888             <member ref='#{node2.id}' type='node' role='some'/>
889             <member ref='#{node1.id}' type='node' role='some'/>
890           </relation>
891         </osm>
892       OSM
893       put api_relation_path(relationid), :params => xml, :headers => auth_header
894       assert_response :too_many_requests, "relation update did not hit rate limit"
895
896       # try deleting the relation, which should be rate limited
897       xml = "<osm><relation id='#{relationid}' version='2' changeset='#{changeset.id}'/></osm>"
898       delete api_relation_path(relationid), :params => xml, :headers => auth_header
899       assert_response :too_many_requests, "relation delete did not hit rate limit"
900
901       # try creating a relation, which should be rate limited
902       xml = <<~OSM
903         <osm>
904           <relation changeset='#{changeset.id}'>
905             <member ref='#{node1.id}' type='node' role='some'/>
906             <member ref='#{node2.id}' type='node' role='some'/>
907           </relation>
908         </osm>
909       OSM
910       post api_relations_path, :params => xml, :headers => auth_header
911       assert_response :too_many_requests, "relation create did not hit rate limit"
912     end
913
914     ##
915     # test maximum rate limit
916     def test_maximum_rate_limit
917       # create a user
918       user = create(:user)
919
920       # create some nodes
921       node1 = create(:node)
922       node2 = create(:node)
923
924       # create a changeset to establish our initial edit time
925       changeset = create(:changeset, :user => user,
926                                      :created_at => Time.now.utc - 28.days)
927
928       # create changeset to put us near the maximum rate limit
929       total_changes = Settings.max_changes_per_hour - 1
930       while total_changes.positive?
931         changes = [total_changes, Changeset::MAX_ELEMENTS].min
932         changeset = create(:changeset, :user => user,
933                                        :created_at => Time.now.utc - 5.minutes,
934                                        :num_changes => changes)
935         total_changes -= changes
936       end
937
938       # create authentication header
939       auth_header = bearer_authorization_header user
940
941       # try creating a relation
942       xml = <<~OSM
943         <osm>
944           <relation changeset='#{changeset.id}'>
945             <member ref='#{node1.id}' type='node' role='some'/>
946             <member ref='#{node2.id}' type='node' role='some'/>
947           </relation>
948         </osm>
949       OSM
950       post api_relations_path, :params => xml, :headers => auth_header
951       assert_response :success, "relation create did not return success status"
952
953       # get the id of the relation we created
954       relationid = @response.body
955
956       # try updating the relation, which should be rate limited
957       xml = <<~OSM
958         <osm>
959           <relation id='#{relationid}' version='1' changeset='#{changeset.id}'>
960             <member ref='#{node2.id}' type='node' role='some'/>
961             <member ref='#{node1.id}' type='node' role='some'/>
962           </relation>
963         </osm>
964       OSM
965       put api_relation_path(relationid), :params => xml, :headers => auth_header
966       assert_response :too_many_requests, "relation update did not hit rate limit"
967
968       # try deleting the relation, which should be rate limited
969       xml = "<osm><relation id='#{relationid}' version='2' changeset='#{changeset.id}'/></osm>"
970       delete api_relation_path(relationid), :params => xml, :headers => auth_header
971       assert_response :too_many_requests, "relation delete did not hit rate limit"
972
973       # try creating a relation, which should be rate limited
974       xml = <<~OSM
975         <osm>
976           <relation changeset='#{changeset.id}'>
977             <member ref='#{node1.id}' type='node' role='some'/>
978             <member ref='#{node2.id}' type='node' role='some'/>
979           </relation>
980         </osm>
981       OSM
982       post api_relations_path, :params => xml, :headers => auth_header
983       assert_response :too_many_requests, "relation create did not hit rate limit"
984     end
985
986     private
987
988     def affected_models
989       [Relation, RelationTag, RelationMember,
990        OldRelation, OldRelationTag, OldRelationMember]
991     end
992
993     ##
994     # update an attribute in the node element
995     def xml_attr_rewrite(xml, name, value)
996       xml.find("//osm/relation").first[name] = value.to_s
997       xml
998     end
999   end
1000 end