]> git.openstreetmap.org Git - rails.git/blob - test/controllers/api/relations_controller_test.rb
Merge remote-tracking branch 'upstream/pull/6178'
[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 simple relation creation.
211     # -------------------------------------
212
213     def test_create
214       private_user = create(:user, :data_public => false)
215       private_changeset = create(:changeset, :user => private_user)
216       user = create(:user)
217       changeset = create(:changeset, :user => user)
218       node = create(:node)
219       way = create(:way_with_nodes, :nodes_count => 2)
220
221       auth_header = bearer_authorization_header private_user
222
223       # create an relation without members
224       xml = <<~OSM
225         <osm>
226           <relation changeset='#{private_changeset.id}'>
227             <tag k='test' v='yes' />
228           </relation>
229         </osm>
230       OSM
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"
235
236       ###
237       # create an relation with a node as member
238       # This time try with a role attribute in the relation
239       xml = <<~OSM
240         <osm>
241           <relation changeset='#{private_changeset.id}'>
242             <member ref='#{node.id}' type='node' role='some'/>
243             <tag k='test' v='yes' />
244           </relation>
245         </osm>
246       OSM
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"
251
252       ###
253       # create an relation with a node as member, this time test that we don't
254       # need a role attribute to be included
255       xml = <<~OSM
256         <osm>
257           <relation changeset='#{private_changeset.id}'>
258             <member ref='#{node.id}' type='node'/>
259             <tag k='test' v='yes' />
260           </relation>
261         </osm>
262       OSM
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"
267
268       ###
269       # create an relation with a way and a node as members
270       xml = <<~OSM
271         <osm>
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' />
276           </relation>
277         </osm>
278       OSM
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"
283
284       ## Now try with the public user
285       auth_header = bearer_authorization_header user
286
287       # create an relation without members
288       xml = <<~OSM
289         <osm>
290           <relation changeset='#{changeset.id}'>
291             <tag k='test' v='yes' />
292           </relation>
293         </osm>
294       OSM
295       post api_relations_path, :params => xml, :headers => auth_header
296       # hope for success
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"
304       # compare values
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
316
317       ###
318       # create an relation with a node as member
319       # This time try with a role attribute in the relation
320       xml = <<~OSM
321         <osm>
322           <relation changeset='#{changeset.id}'>
323             <member ref='#{node.id}' type='node' role='some'/>
324             <tag k='test' v='yes' />
325           </relation>
326         </osm>
327       OSM
328       post api_relations_path, :params => xml, :headers => auth_header
329       # hope for success
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"
337       # compare values
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?
347
348       get api_relation_path(relationid)
349       assert_response :success
350
351       ###
352       # create an relation with a node as member, this time test that we don't
353       # need a role attribute to be included
354       xml = <<~OSM
355         <osm>
356           <relation changeset='#{changeset.id}'>
357             <member ref='#{node.id}' type='node'/>
358             <tag k='test' v='yes' />
359           </relation>
360         </osm>
361       OSM
362       post api_relations_path, :params => xml, :headers => auth_header
363       # hope for success
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"
371       # compare values
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?
381
382       get api_relation_path(relationid)
383       assert_response :success
384
385       ###
386       # create an relation with a way and a node as members
387       xml = <<~OSM
388         <osm>
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' />
393           </relation>
394         </osm>
395       OSM
396       post api_relations_path, :params => xml, :headers => auth_header
397       # hope for success
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"
405       # compare values
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
417     end
418
419     # ------------------------------------
420     # Test updating relations
421     # ------------------------------------
422
423     def test_update_wrong_id
424       user = create(:user)
425       changeset = create(:changeset, :user => user)
426       relation = create(:relation)
427       other_relation = create(:relation)
428
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
433
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
437     end
438
439     # -------------------------------------
440     # Test creating some invalid relations.
441     # -------------------------------------
442
443     def test_create_invalid
444       user = create(:user)
445       changeset = create(:changeset, :user => user)
446
447       auth_header = bearer_authorization_header user
448
449       # create a relation with non-existing node as member
450       xml = <<~OSM
451         <osm>
452           <relation changeset='#{changeset.id}'>
453             <member type='node' ref='0'/>
454           </relation>
455         </osm>
456       OSM
457       post api_relations_path, :params => xml, :headers => auth_header
458       # expect failure
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
462     end
463
464     # -------------------------------------
465     # Test creating a relation, with some invalid XML
466     # -------------------------------------
467     def test_create_invalid_xml
468       user = create(:user)
469       changeset = create(:changeset, :user => user)
470       node = create(:node)
471
472       auth_header = bearer_authorization_header user
473
474       # create some xml that should return an error
475       xml = <<~OSM
476         <osm>
477           <relation changeset='#{changeset.id}'>
478             <member type='type' ref='#{node.id}' role=''/>
479           </relation>
480         </osm>
481       OSM
482       post api_relations_path, :params => xml, :headers => auth_header
483       # expect failure
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)
487     end
488
489     # -------------------------------------
490     # Test deleting relations.
491     # -------------------------------------
492
493     def test_destroy
494       private_user = create(:user, :data_public => false)
495       private_user_closed_changeset = create(:changeset, :closed, :user => private_user)
496       user = create(: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)
505
506       ## First try to delete relation without auth
507       delete api_relation_path(relation)
508       assert_response :unauthorized
509
510       ## Then try with the private user, to make sure that you get a forbidden
511       auth_header = bearer_authorization_header private_user
512
513       # this shouldn't work, as we should need the payload...
514       delete api_relation_path(relation), :headers => auth_header
515       assert_response :forbidden
516
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
521
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
527
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
532
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
537
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
542
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
547
548       # this won't work since the relation never existed
549       delete api_relation_path(0), :headers => auth_header
550       assert_response :forbidden
551
552       ## now set auth for the public user
553       auth_header = bearer_authorization_header user
554
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
558
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)
564
565       # try to delete with an invalid (closed) changeset
566       xml = update_changeset(xml_for_relation(relation),
567                              closed_changeset.id)
568       delete api_relation_path(relation), :params => xml.to_s, :headers => auth_header
569       assert_response :conflict
570
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
575
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})"
581
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"
586
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
593
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
598
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"
602
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
607
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
612
613       # this works now because the relation which was using this one
614       # has been deleted.
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})"
619
620       # this won't work since the relation never existed
621       delete api_relation_path(0), :headers => auth_header
622       assert_response :not_found
623     end
624
625     ##
626     # test initial rate limit
627     def test_initial_rate_limit
628       # create a user
629       user = create(:user)
630
631       # create some nodes
632       node1 = create(:node)
633       node2 = create(:node)
634
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)
639
640       # create authentication header
641       auth_header = bearer_authorization_header user
642
643       # try creating a relation
644       xml = <<~OSM
645         <osm>
646           <relation changeset='#{changeset.id}'>
647             <member ref='#{node1.id}' type='node' role='some'/>
648             <member ref='#{node2.id}' type='node' role='some'/>
649           </relation>
650         </osm>
651       OSM
652       post api_relations_path, :params => xml, :headers => auth_header
653       assert_response :success, "relation create did not return success status"
654
655       # get the id of the relation we created
656       relationid = @response.body
657
658       # try updating the relation, which should be rate limited
659       xml = <<~OSM
660         <osm>
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'/>
664           </relation>
665         </osm>
666       OSM
667       put api_relation_path(relationid), :params => xml, :headers => auth_header
668       assert_response :too_many_requests, "relation update did not hit rate limit"
669
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"
674
675       # try creating a relation, which should be rate limited
676       xml = <<~OSM
677         <osm>
678           <relation changeset='#{changeset.id}'>
679             <member ref='#{node1.id}' type='node' role='some'/>
680             <member ref='#{node2.id}' type='node' role='some'/>
681           </relation>
682         </osm>
683       OSM
684       post api_relations_path, :params => xml, :headers => auth_header
685       assert_response :too_many_requests, "relation create did not hit rate limit"
686     end
687
688     ##
689     # test maximum rate limit
690     def test_maximum_rate_limit
691       # create a user
692       user = create(:user)
693
694       # create some nodes
695       node1 = create(:node)
696       node2 = create(:node)
697
698       # create a changeset to establish our initial edit time
699       changeset = create(:changeset, :user => user,
700                                      :created_at => Time.now.utc - 28.days)
701
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
710       end
711
712       # create authentication header
713       auth_header = bearer_authorization_header user
714
715       # try creating a relation
716       xml = <<~OSM
717         <osm>
718           <relation changeset='#{changeset.id}'>
719             <member ref='#{node1.id}' type='node' role='some'/>
720             <member ref='#{node2.id}' type='node' role='some'/>
721           </relation>
722         </osm>
723       OSM
724       post api_relations_path, :params => xml, :headers => auth_header
725       assert_response :success, "relation create did not return success status"
726
727       # get the id of the relation we created
728       relationid = @response.body
729
730       # try updating the relation, which should be rate limited
731       xml = <<~OSM
732         <osm>
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'/>
736           </relation>
737         </osm>
738       OSM
739       put api_relation_path(relationid), :params => xml, :headers => auth_header
740       assert_response :too_many_requests, "relation update did not hit rate limit"
741
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"
746
747       # try creating a relation, which should be rate limited
748       xml = <<~OSM
749         <osm>
750           <relation changeset='#{changeset.id}'>
751             <member ref='#{node1.id}' type='node' role='some'/>
752             <member ref='#{node2.id}' type='node' role='some'/>
753           </relation>
754         </osm>
755       OSM
756       post api_relations_path, :params => xml, :headers => auth_header
757       assert_response :too_many_requests, "relation create did not hit rate limit"
758     end
759
760     private
761
762     def affected_models
763       [Relation, RelationTag, RelationMember,
764        OldRelation, OldRelationTag, OldRelationMember]
765     end
766
767     ##
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
771       xml
772     end
773   end
774 end