]> git.openstreetmap.org Git - rails.git/blob - test/controllers/api/relations_controller_test.rb
32dfee08fc954f27b490872d200360ff34aaf484
[rails.git] / test / controllers / api / relations_controller_test.rb
1 require "test_helper"
2
3 module Api
4   class RelationsControllerTest < ActionDispatch::IntegrationTest
5     ##
6     # test all routes which lead to this controller
7     def test_routes
8       assert_routing(
9         { :path => "/api/0.6/relations", :method => :get },
10         { :controller => "api/relations", :action => "index" }
11       )
12       assert_routing(
13         { :path => "/api/0.6/relations.json", :method => :get },
14         { :controller => "api/relations", :action => "index", :format => "json" }
15       )
16       assert_routing(
17         { :path => "/api/0.6/relations", :method => :post },
18         { :controller => "api/relations", :action => "create" }
19       )
20       assert_routing(
21         { :path => "/api/0.6/relation/1", :method => :get },
22         { :controller => "api/relations", :action => "show", :id => "1" }
23       )
24       assert_routing(
25         { :path => "/api/0.6/relation/1.json", :method => :get },
26         { :controller => "api/relations", :action => "show", :id => "1", :format => "json" }
27       )
28       assert_routing(
29         { :path => "/api/0.6/relation/1/full", :method => :get },
30         { :controller => "api/relations", :action => "show", :full => true, :id => "1" }
31       )
32       assert_routing(
33         { :path => "/api/0.6/relation/1/full.json", :method => :get },
34         { :controller => "api/relations", :action => "show", :full => true, :id => "1", :format => "json" }
35       )
36       assert_routing(
37         { :path => "/api/0.6/relation/1", :method => :put },
38         { :controller => "api/relations", :action => "update", :id => "1" }
39       )
40       assert_routing(
41         { :path => "/api/0.6/relation/1", :method => :delete },
42         { :controller => "api/relations", :action => "destroy", :id => "1" }
43       )
44
45       assert_routing(
46         { :path => "/api/0.6/relation/1/relations", :method => :get },
47         { :controller => "api/relations", :action => "relations_for_relation", :id => "1" }
48       )
49       assert_routing(
50         { :path => "/api/0.6/relation/1/relations.json", :method => :get },
51         { :controller => "api/relations", :action => "relations_for_relation", :id => "1", :format => "json" }
52       )
53
54       assert_recognizes(
55         { :controller => "api/relations", :action => "create" },
56         { :path => "/api/0.6/relation/create", :method => :put }
57       )
58     end
59
60     ##
61     # test fetching multiple relations
62     def test_index
63       relation1 = create(:relation)
64       relation2 = create(:relation, :deleted)
65       relation3 = create(:relation, :with_history, :version => 2)
66       relation4 = create(:relation, :with_history, :version => 2)
67       relation4.old_relations.find_by(:version => 1).redact!(create(:redaction))
68
69       # check error when no parameter provided
70       get api_relations_path
71       assert_response :bad_request
72
73       # check error when no parameter value provided
74       get api_relations_path(:relations => "")
75       assert_response :bad_request
76
77       # test a working call
78       get api_relations_path(:relations => "#{relation1.id},#{relation2.id},#{relation3.id},#{relation4.id}")
79       assert_response :success
80       assert_select "osm" do
81         assert_select "relation", :count => 4
82         assert_select "relation[id='#{relation1.id}'][visible='true']", :count => 1
83         assert_select "relation[id='#{relation2.id}'][visible='false']", :count => 1
84         assert_select "relation[id='#{relation3.id}'][visible='true']", :count => 1
85         assert_select "relation[id='#{relation4.id}'][visible='true']", :count => 1
86       end
87
88       # test a working call with json format
89       get api_relations_path(:relations => "#{relation1.id},#{relation2.id},#{relation3.id},#{relation4.id}", :format => "json")
90
91       js = ActiveSupport::JSON.decode(@response.body)
92       assert_not_nil js
93       assert_equal 4, js["elements"].count
94       assert_equal 4, (js["elements"].count { |a| a["type"] == "relation" })
95       assert_equal 1, (js["elements"].count { |a| a["id"] == relation1.id && a["visible"].nil? })
96       assert_equal 1, (js["elements"].count { |a| a["id"] == relation2.id && a["visible"] == false })
97       assert_equal 1, (js["elements"].count { |a| a["id"] == relation3.id && a["visible"].nil? })
98       assert_equal 1, (js["elements"].count { |a| a["id"] == relation4.id && a["visible"].nil? })
99
100       # check error when a non-existent relation is included
101       get api_relations_path(:relations => "#{relation1.id},#{relation2.id},#{relation3.id},#{relation4.id},0")
102       assert_response :not_found
103     end
104
105     # -------------------------------------
106     # Test showing relations.
107     # -------------------------------------
108
109     def test_show_not_found
110       get api_relation_path(0)
111       assert_response :not_found
112     end
113
114     def test_show_deleted
115       get api_relation_path(create(:relation, :deleted))
116       assert_response :gone
117     end
118
119     def test_show
120       relation = create(:relation, :timestamp => "2021-02-03T00:00:00Z")
121       node = create(:node, :timestamp => "2021-04-05T00:00:00Z")
122       create(:relation_member, :relation => relation, :member => node)
123
124       get api_relation_path(relation)
125
126       assert_response :success
127       assert_not_nil @response.header["Last-Modified"]
128       assert_equal "2021-02-03T00:00:00Z", Time.parse(@response.header["Last-Modified"]).utc.xmlschema
129       assert_dom "node", :count => 0
130       assert_dom "relation", :count => 1 do
131         assert_dom "> @id", :text => relation.id.to_s
132       end
133     end
134
135     def test_full_not_found
136       get api_relation_path(999999, :full => true)
137       assert_response :not_found
138     end
139
140     def test_full_deleted
141       get api_relation_path(create(:relation, :deleted), :full => true)
142       assert_response :gone
143     end
144
145     def test_full_empty
146       relation = create(:relation)
147
148       get api_relation_path(relation, :full => true)
149
150       assert_response :success
151       assert_dom "relation", :count => 1 do
152         assert_dom "> @id", :text => relation.id.to_s
153       end
154     end
155
156     def test_full_with_node_member
157       relation = create(:relation)
158       node = create(:node)
159       create(:relation_member, :relation => relation, :member => node)
160
161       get api_relation_path(relation, :full => true)
162
163       assert_response :success
164       assert_dom "node", :count => 1 do
165         assert_dom "> @id", :text => node.id.to_s
166       end
167       assert_dom "relation", :count => 1 do
168         assert_dom "> @id", :text => relation.id.to_s
169       end
170     end
171
172     def test_full_with_way_member
173       relation = create(:relation)
174       way = create(:way_with_nodes)
175       create(:relation_member, :relation => relation, :member => way)
176
177       get api_relation_path(relation, :full => true)
178
179       assert_response :success
180       assert_dom "node", :count => 1 do
181         assert_dom "> @id", :text => way.nodes[0].id.to_s
182       end
183       assert_dom "way", :count => 1 do
184         assert_dom "> @id", :text => way.id.to_s
185       end
186       assert_dom "relation", :count => 1 do
187         assert_dom "> @id", :text => relation.id.to_s
188       end
189     end
190
191     def test_full_with_node_member_json
192       relation = create(:relation)
193       node = create(:node)
194       create(:relation_member, :relation => relation, :member => node)
195
196       get api_relation_path(relation, :full => true, :format => "json")
197
198       assert_response :success
199       js = ActiveSupport::JSON.decode(@response.body)
200       assert_not_nil js
201       assert_equal 2, js["elements"].count
202
203       js_relations = js["elements"].filter { |e| e["type"] == "relation" }
204       assert_equal 1, js_relations.count
205       assert_equal relation.id, js_relations[0]["id"]
206       assert_equal 1, js_relations[0]["members"].count
207       assert_equal "node", js_relations[0]["members"][0]["type"]
208       assert_equal node.id, js_relations[0]["members"][0]["ref"]
209
210       js_nodes = js["elements"].filter { |e| e["type"] == "node" }
211       assert_equal 1, js_nodes.count
212       assert_equal node.id, js_nodes[0]["id"]
213     end
214
215     def test_relations_for_relation
216       relation = create(:relation)
217       # should include relations with that relation as a member
218       relation_with_relation = create(:relation_member, :member => relation).relation
219       # should ignore any relation without that relation as a member
220       _relation_without_relation = create(:relation_member).relation
221       # should ignore relations with the relation involved indirectly, via a relation
222       second_relation = create(:relation_member, :member => relation).relation
223       _super_relation = create(:relation_member, :member => second_relation).relation
224       # should combine multiple relation_member references into just one relation entry
225       create(:relation_member, :member => relation, :relation => relation_with_relation)
226       # should not include deleted relations
227       deleted_relation = create(:relation, :deleted)
228       create(:relation_member, :member => relation, :relation => deleted_relation)
229       check_relations_for_element(relation_relations_path(relation), "relation",
230                                   relation.id,
231                                   [relation_with_relation, second_relation])
232     end
233
234     # -------------------------------------
235     # Test simple relation creation.
236     # -------------------------------------
237
238     def test_create
239       private_user = create(:user, :data_public => false)
240       private_changeset = create(:changeset, :user => private_user)
241       user = create(:user)
242       changeset = create(:changeset, :user => user)
243       node = create(:node)
244       way = create(:way_with_nodes, :nodes_count => 2)
245
246       auth_header = bearer_authorization_header private_user
247
248       # create an relation without members
249       xml = "<osm><relation changeset='#{private_changeset.id}'><tag k='test' v='yes' /></relation></osm>"
250       post api_relations_path, :params => xml, :headers => auth_header
251       # hope for forbidden, due to user
252       assert_response :forbidden,
253                       "relation upload should have failed with forbidden"
254
255       ###
256       # create an relation with a node as member
257       # This time try with a role attribute in the relation
258       xml = "<osm><relation changeset='#{private_changeset.id}'>" \
259             "<member  ref='#{node.id}' type='node' role='some'/>" \
260             "<tag k='test' v='yes' /></relation></osm>"
261       post api_relations_path, :params => xml, :headers => auth_header
262       # hope for forbidden due to user
263       assert_response :forbidden,
264                       "relation upload did not return forbidden status"
265
266       ###
267       # create an relation with a node as member, this time test that we don't
268       # need a role attribute to be included
269       xml = "<osm><relation changeset='#{private_changeset.id}'>" \
270             "<member  ref='#{node.id}' type='node'/><tag k='test' v='yes' /></relation></osm>"
271       post api_relations_path, :params => xml, :headers => auth_header
272       # hope for forbidden due to user
273       assert_response :forbidden,
274                       "relation upload did not return forbidden status"
275
276       ###
277       # create an relation with a way and a node as members
278       xml = "<osm><relation changeset='#{private_changeset.id}'>" \
279             "<member type='node' ref='#{node.id}' role='some'/>" \
280             "<member type='way' ref='#{way.id}' role='other'/>" \
281             "<tag k='test' v='yes' /></relation></osm>"
282       post api_relations_path, :params => xml, :headers => auth_header
283       # hope for forbidden, due to user
284       assert_response :forbidden,
285                       "relation upload did not return success status"
286
287       ## Now try with the public user
288       auth_header = bearer_authorization_header user
289
290       # create an relation without members
291       xml = "<osm><relation changeset='#{changeset.id}'><tag k='test' v='yes' /></relation></osm>"
292       post api_relations_path, :params => xml, :headers => auth_header
293       # hope for success
294       assert_response :success,
295                       "relation upload did not return success status"
296       # read id of created relation and search for it
297       relationid = @response.body
298       checkrelation = Relation.find(relationid)
299       assert_not_nil checkrelation,
300                      "uploaded relation not found in data base after upload"
301       # compare values
302       assert_equal(0, checkrelation.members.length, "saved relation contains members but should not")
303       assert_equal(1, checkrelation.tags.length, "saved relation does not contain exactly one tag")
304       assert_equal changeset.id, checkrelation.changeset.id,
305                    "saved relation does not belong in the changeset it was assigned to"
306       assert_equal user.id, checkrelation.changeset.user_id,
307                    "saved relation does not belong to user that created it"
308       assert checkrelation.visible,
309              "saved relation is not visible"
310       # ok the relation is there but can we also retrieve it?
311       get api_relation_path(relationid)
312       assert_response :success
313
314       ###
315       # create an relation with a node as member
316       # This time try with a role attribute in the relation
317       xml = "<osm><relation changeset='#{changeset.id}'>" \
318             "<member  ref='#{node.id}' type='node' role='some'/>" \
319             "<tag k='test' v='yes' /></relation></osm>"
320       post api_relations_path, :params => xml, :headers => auth_header
321       # hope for success
322       assert_response :success,
323                       "relation upload did not return success status"
324       # read id of created relation and search for it
325       relationid = @response.body
326       checkrelation = Relation.find(relationid)
327       assert_not_nil checkrelation,
328                      "uploaded relation not found in data base after upload"
329       # compare values
330       assert_equal(1, checkrelation.members.length, "saved relation does not contain exactly one member")
331       assert_equal(1, checkrelation.tags.length, "saved relation does not contain exactly one tag")
332       assert_equal changeset.id, checkrelation.changeset.id,
333                    "saved relation does not belong in the changeset it was assigned to"
334       assert_equal user.id, checkrelation.changeset.user_id,
335                    "saved relation does not belong to user that created it"
336       assert checkrelation.visible,
337              "saved relation is not visible"
338       # ok the relation is there but can we also retrieve it?
339
340       get api_relation_path(relationid)
341       assert_response :success
342
343       ###
344       # create an relation with a node as member, this time test that we don't
345       # need a role attribute to be included
346       xml = "<osm><relation changeset='#{changeset.id}'>" \
347             "<member  ref='#{node.id}' type='node'/><tag k='test' v='yes' /></relation></osm>"
348       post api_relations_path, :params => xml, :headers => auth_header
349       # hope for success
350       assert_response :success,
351                       "relation upload did not return success status"
352       # read id of created relation and search for it
353       relationid = @response.body
354       checkrelation = Relation.find(relationid)
355       assert_not_nil checkrelation,
356                      "uploaded relation not found in data base after upload"
357       # compare values
358       assert_equal(1, checkrelation.members.length, "saved relation does not contain exactly one member")
359       assert_equal(1, checkrelation.tags.length, "saved relation does not contain exactly one tag")
360       assert_equal changeset.id, checkrelation.changeset.id,
361                    "saved relation does not belong in the changeset it was assigned to"
362       assert_equal user.id, checkrelation.changeset.user_id,
363                    "saved relation does not belong to user that created it"
364       assert checkrelation.visible,
365              "saved relation is not visible"
366       # ok the relation is there but can we also retrieve it?
367
368       get api_relation_path(relationid)
369       assert_response :success
370
371       ###
372       # create an relation with a way and a node as members
373       xml = "<osm><relation changeset='#{changeset.id}'>" \
374             "<member type='node' ref='#{node.id}' role='some'/>" \
375             "<member type='way' ref='#{way.id}' role='other'/>" \
376             "<tag k='test' v='yes' /></relation></osm>"
377       post api_relations_path, :params => xml, :headers => auth_header
378       # hope for success
379       assert_response :success,
380                       "relation upload did not return success status"
381       # read id of created relation and search for it
382       relationid = @response.body
383       checkrelation = Relation.find(relationid)
384       assert_not_nil checkrelation,
385                      "uploaded relation not found in data base after upload"
386       # compare values
387       assert_equal(2, checkrelation.members.length, "saved relation does not have exactly two members")
388       assert_equal(1, checkrelation.tags.length, "saved relation does not contain exactly one tag")
389       assert_equal changeset.id, checkrelation.changeset.id,
390                    "saved relation does not belong in the changeset it was assigned to"
391       assert_equal user.id, checkrelation.changeset.user_id,
392                    "saved relation does not belong to user that created it"
393       assert checkrelation.visible,
394              "saved relation is not visible"
395       # ok the relation is there but can we also retrieve it?
396       get api_relation_path(relationid)
397       assert_response :success
398     end
399
400     # ------------------------------------
401     # Test updating relations
402     # ------------------------------------
403
404     ##
405     # test that, when tags are updated on a relation, the correct things
406     # happen to the correct tables and the API gives sensible results.
407     # this is to test a case that gregory marler noticed and posted to
408     # josm-dev.
409     ## FIXME Move this to an integration test
410     def test_update_relation_tags
411       user = create(:user)
412       changeset = create(:changeset, :user => user)
413       relation = create(:relation)
414       create_list(:relation_tag, 4, :relation => relation)
415
416       auth_header = bearer_authorization_header user
417
418       with_relation(relation.id) do |rel|
419         # alter one of the tags
420         tag = rel.find("//osm/relation/tag").first
421         tag["v"] = "some changed value"
422         update_changeset(rel, changeset.id)
423
424         # check that the downloaded tags are the same as the uploaded tags...
425         new_version = with_update(rel, auth_header) do |new_rel|
426           assert_tags_equal rel, new_rel
427         end
428
429         # check the original one in the current_* table again
430         with_relation(relation.id) { |r| assert_tags_equal rel, r }
431
432         # now check the version in the history
433         with_relation(relation.id, new_version) { |r| assert_tags_equal rel, r }
434       end
435     end
436
437     ##
438     # test that, when tags are updated on a relation when using the diff
439     # upload function, the correct things happen to the correct tables
440     # and the API gives sensible results. this is to test a case that
441     # gregory marler noticed and posted to josm-dev.
442     def test_update_relation_tags_via_upload
443       user = create(:user)
444       changeset = create(:changeset, :user => user)
445       relation = create(:relation)
446       create_list(:relation_tag, 4, :relation => relation)
447
448       auth_header = bearer_authorization_header user
449
450       with_relation(relation.id) do |rel|
451         # alter one of the tags
452         tag = rel.find("//osm/relation/tag").first
453         tag["v"] = "some changed value"
454         update_changeset(rel, changeset.id)
455
456         # check that the downloaded tags are the same as the uploaded tags...
457         new_version = with_update_diff(rel, auth_header) do |new_rel|
458           assert_tags_equal rel, new_rel
459         end
460
461         # check the original one in the current_* table again
462         with_relation(relation.id) { |r| assert_tags_equal rel, r }
463
464         # now check the version in the history
465         with_relation(relation.id, new_version) { |r| assert_tags_equal rel, r }
466       end
467     end
468
469     def test_update_wrong_id
470       user = create(:user)
471       changeset = create(:changeset, :user => user)
472       relation = create(:relation)
473       other_relation = create(:relation)
474
475       auth_header = bearer_authorization_header user
476       with_relation(relation.id) do |rel|
477         update_changeset(rel, changeset.id)
478         put api_relation_path(other_relation), :params => rel.to_s, :headers => auth_header
479         assert_response :bad_request
480       end
481     end
482
483     # -------------------------------------
484     # Test creating some invalid relations.
485     # -------------------------------------
486
487     def test_create_invalid
488       user = create(:user)
489       changeset = create(:changeset, :user => user)
490
491       auth_header = bearer_authorization_header user
492
493       # create a relation with non-existing node as member
494       xml = "<osm><relation changeset='#{changeset.id}'>" \
495             "<member type='node' ref='0'/><tag k='test' v='yes' />" \
496             "</relation></osm>"
497       post api_relations_path, :params => xml, :headers => auth_header
498       # expect failure
499       assert_response :precondition_failed,
500                       "relation upload with invalid node did not return 'precondition failed'"
501       assert_equal "Precondition failed: Relation with id  cannot be saved due to Node with id 0", @response.body
502     end
503
504     # -------------------------------------
505     # Test creating a relation, with some invalid XML
506     # -------------------------------------
507     def test_create_invalid_xml
508       user = create(:user)
509       changeset = create(:changeset, :user => user)
510       node = create(:node)
511
512       auth_header = bearer_authorization_header user
513
514       # create some xml that should return an error
515       xml = "<osm><relation changeset='#{changeset.id}'>" \
516             "<member type='type' ref='#{node.id}' role=''/>" \
517             "<tag k='tester' v='yep'/></relation></osm>"
518       post api_relations_path, :params => xml, :headers => auth_header
519       # expect failure
520       assert_response :bad_request
521       assert_match(/Cannot parse valid relation from xml string/, @response.body)
522       assert_match(/The type is not allowed only, /, @response.body)
523     end
524
525     # -------------------------------------
526     # Test deleting relations.
527     # -------------------------------------
528
529     def test_destroy
530       private_user = create(:user, :data_public => false)
531       private_user_closed_changeset = create(:changeset, :closed, :user => private_user)
532       user = create(:user)
533       closed_changeset = create(:changeset, :closed, :user => user)
534       changeset = create(:changeset, :user => user)
535       relation = create(:relation)
536       used_relation = create(:relation)
537       super_relation = create(:relation_member, :member => used_relation).relation
538       deleted_relation = create(:relation, :deleted)
539       multi_tag_relation = create(:relation)
540       create_list(:relation_tag, 4, :relation => multi_tag_relation)
541
542       ## First try to delete relation without auth
543       delete api_relation_path(relation)
544       assert_response :unauthorized
545
546       ## Then try with the private user, to make sure that you get a forbidden
547       auth_header = bearer_authorization_header private_user
548
549       # this shouldn't work, as we should need the payload...
550       delete api_relation_path(relation), :headers => auth_header
551       assert_response :forbidden
552
553       # try to delete without specifying a changeset
554       xml = "<osm><relation id='#{relation.id}'/></osm>"
555       delete api_relation_path(relation), :params => xml.to_s, :headers => auth_header
556       assert_response :forbidden
557
558       # try to delete with an invalid (closed) changeset
559       xml = update_changeset(xml_for_relation(relation),
560                              private_user_closed_changeset.id)
561       delete api_relation_path(relation), :params => xml.to_s, :headers => auth_header
562       assert_response :forbidden
563
564       # try to delete with an invalid (non-existent) changeset
565       xml = update_changeset(xml_for_relation(relation), 0)
566       delete api_relation_path(relation), :params => xml.to_s, :headers => auth_header
567       assert_response :forbidden
568
569       # this won't work because the relation is in-use by another relation
570       xml = xml_for_relation(used_relation)
571       delete api_relation_path(used_relation), :params => xml.to_s, :headers => auth_header
572       assert_response :forbidden
573
574       # this should work when we provide the appropriate payload...
575       xml = xml_for_relation(relation)
576       delete api_relation_path(relation), :params => xml.to_s, :headers => auth_header
577       assert_response :forbidden
578
579       # this won't work since the relation is already deleted
580       xml = xml_for_relation(deleted_relation)
581       delete api_relation_path(deleted_relation), :params => xml.to_s, :headers => auth_header
582       assert_response :forbidden
583
584       # this won't work since the relation never existed
585       delete api_relation_path(0), :headers => auth_header
586       assert_response :forbidden
587
588       ## now set auth for the public user
589       auth_header = bearer_authorization_header user
590
591       # this shouldn't work, as we should need the payload...
592       delete api_relation_path(relation), :headers => auth_header
593       assert_response :bad_request
594
595       # try to delete without specifying a changeset
596       xml = "<osm><relation id='#{relation.id}' version='#{relation.version}' /></osm>"
597       delete api_relation_path(relation), :params => xml.to_s, :headers => auth_header
598       assert_response :bad_request
599       assert_match(/Changeset id is missing/, @response.body)
600
601       # try to delete with an invalid (closed) changeset
602       xml = update_changeset(xml_for_relation(relation),
603                              closed_changeset.id)
604       delete api_relation_path(relation), :params => xml.to_s, :headers => auth_header
605       assert_response :conflict
606
607       # try to delete with an invalid (non-existent) changeset
608       xml = update_changeset(xml_for_relation(relation), 0)
609       delete api_relation_path(relation), :params => xml.to_s, :headers => auth_header
610       assert_response :conflict
611
612       # this won't work because the relation is in a changeset owned by someone else
613       xml = update_changeset(xml_for_relation(relation), create(:changeset).id)
614       delete api_relation_path(relation), :params => xml.to_s, :headers => auth_header
615       assert_response :conflict,
616                       "shouldn't be able to delete a relation in a changeset owned by someone else (#{@response.body})"
617
618       # this won't work because the relation in the payload is different to that passed
619       xml = update_changeset(xml_for_relation(relation), changeset.id)
620       delete api_relation_path(create(:relation)), :params => xml.to_s, :headers => auth_header
621       assert_response :bad_request, "shouldn't be able to delete a relation when payload is different to the url"
622
623       # this won't work because the relation is in-use by another relation
624       xml = update_changeset(xml_for_relation(used_relation), changeset.id)
625       delete api_relation_path(used_relation), :params => xml.to_s, :headers => auth_header
626       assert_response :precondition_failed,
627                       "shouldn't be able to delete a relation used in a relation (#{@response.body})"
628       assert_equal "Precondition failed: The relation #{used_relation.id} is used in relation #{super_relation.id}.", @response.body
629
630       # this should work when we provide the appropriate payload...
631       xml = update_changeset(xml_for_relation(multi_tag_relation), changeset.id)
632       delete api_relation_path(multi_tag_relation), :params => xml.to_s, :headers => auth_header
633       assert_response :success
634
635       # valid delete should return the new version number, which should
636       # be greater than the old version number
637       assert_operator @response.body.to_i, :>, multi_tag_relation.version, "delete request should return a new version number for relation"
638
639       # this won't work since the relation is already deleted
640       xml = update_changeset(xml_for_relation(deleted_relation), changeset.id)
641       delete api_relation_path(deleted_relation), :params => xml.to_s, :headers => auth_header
642       assert_response :gone
643
644       # Public visible relation needs to be deleted
645       xml = update_changeset(xml_for_relation(super_relation), changeset.id)
646       delete api_relation_path(super_relation), :params => xml.to_s, :headers => auth_header
647       assert_response :success
648
649       # this works now because the relation which was using this one
650       # has been deleted.
651       xml = update_changeset(xml_for_relation(used_relation), changeset.id)
652       delete api_relation_path(used_relation), :params => xml.to_s, :headers => auth_header
653       assert_response :success,
654                       "should be able to delete a relation used in an old relation (#{@response.body})"
655
656       # this won't work since the relation never existed
657       delete api_relation_path(0), :headers => auth_header
658       assert_response :not_found
659     end
660
661     ##
662     # when a relation's tag is modified then it should put the bounding
663     # box of all its members into the changeset.
664     def test_tag_modify_bounding_box
665       relation = create(:relation)
666       node1 = create(:node, :lat => 0.3, :lon => 0.3)
667       node2 = create(:node, :lat => 0.5, :lon => 0.5)
668       way = create(:way)
669       create(:way_node, :way => way, :node => node1)
670       create(:relation_member, :relation => relation, :member => way)
671       create(:relation_member, :relation => relation, :member => node2)
672       # the relation contains nodes1 and node2 (node1
673       # indirectly via the way), so the bbox should be [0.3,0.3,0.5,0.5].
674       check_changeset_modify(BoundingBox.new(0.3, 0.3, 0.5, 0.5)) do |changeset_id, auth_header|
675         # add a tag to an existing relation
676         relation_xml = xml_for_relation(relation)
677         relation_element = relation_xml.find("//osm/relation").first
678         new_tag = XML::Node.new("tag")
679         new_tag["k"] = "some_new_tag"
680         new_tag["v"] = "some_new_value"
681         relation_element << new_tag
682
683         # update changeset ID to point to new changeset
684         update_changeset(relation_xml, changeset_id)
685
686         # upload the change
687         put api_relation_path(relation), :params => relation_xml.to_s, :headers => auth_header
688         assert_response :success, "can't update relation for tag/bbox test"
689       end
690     end
691
692     ##
693     # add a member to a relation and check the bounding box is only that
694     # element.
695     def test_add_member_bounding_box
696       relation = create(:relation)
697       node1 = create(:node, :lat => 4, :lon => 4)
698       node2 = create(:node, :lat => 7, :lon => 7)
699       way1 = create(:way)
700       create(:way_node, :way => way1, :node => create(:node, :lat => 8, :lon => 8))
701       way2 = create(:way)
702       create(:way_node, :way => way2, :node => create(:node, :lat => 9, :lon => 9), :sequence_id => 1)
703       create(:way_node, :way => way2, :node => create(:node, :lat => 10, :lon => 10), :sequence_id => 2)
704
705       [node1, node2, way1, way2].each do |element|
706         bbox = element.bbox.to_unscaled
707         check_changeset_modify(bbox) do |changeset_id, auth_header|
708           relation_xml = xml_for_relation(Relation.find(relation.id))
709           relation_element = relation_xml.find("//osm/relation").first
710           new_member = XML::Node.new("member")
711           new_member["ref"] = element.id.to_s
712           new_member["type"] = element.class.to_s.downcase
713           new_member["role"] = "some_role"
714           relation_element << new_member
715
716           # update changeset ID to point to new changeset
717           update_changeset(relation_xml, changeset_id)
718
719           # upload the change
720           put api_relation_path(relation), :params => relation_xml.to_s, :headers => auth_header
721           assert_response :success, "can't update relation for add #{element.class}/bbox test: #{@response.body}"
722
723           # get it back and check the ordering
724           get api_relation_path(relation)
725           assert_response :success, "can't read back the relation: #{@response.body}"
726           check_ordering(relation_xml, @response.body)
727         end
728       end
729     end
730
731     ##
732     # remove a member from a relation and check the bounding box is
733     # only that element.
734     def test_remove_member_bounding_box
735       relation = create(:relation)
736       node1 = create(:node, :lat => 3, :lon => 3)
737       node2 = create(:node, :lat => 5, :lon => 5)
738       create(:relation_member, :relation => relation, :member => node1)
739       create(:relation_member, :relation => relation, :member => node2)
740
741       check_changeset_modify(BoundingBox.new(5, 5, 5, 5)) do |changeset_id, auth_header|
742         # remove node 5 (5,5) from an existing relation
743         relation_xml = xml_for_relation(relation)
744         relation_xml
745           .find("//osm/relation/member[@type='node'][@ref='#{node2.id}']")
746           .first.remove!
747
748         # update changeset ID to point to new changeset
749         update_changeset(relation_xml, changeset_id)
750
751         # upload the change
752         put api_relation_path(relation), :params => relation_xml.to_s, :headers => auth_header
753         assert_response :success, "can't update relation for remove node/bbox test"
754       end
755     end
756
757     ##
758     # check that relations are ordered
759     def test_relation_member_ordering
760       user = create(:user)
761       changeset = create(:changeset, :user => user)
762       node1 = create(:node)
763       node2 = create(:node)
764       node3 = create(:node)
765       way1 = create(:way_with_nodes, :nodes_count => 2)
766       way2 = create(:way_with_nodes, :nodes_count => 2)
767
768       auth_header = bearer_authorization_header user
769
770       doc_str = <<~OSM
771         <osm>
772          <relation changeset='#{changeset.id}'>
773           <member ref='#{node1.id}' type='node' role='first'/>
774           <member ref='#{node2.id}' type='node' role='second'/>
775           <member ref='#{way1.id}' type='way' role='third'/>
776           <member ref='#{way2.id}' type='way' role='fourth'/>
777          </relation>
778         </osm>
779       OSM
780       doc = XML::Parser.string(doc_str).parse
781
782       post api_relations_path, :params => doc.to_s, :headers => auth_header
783       assert_response :success, "can't create a relation: #{@response.body}"
784       relation_id = @response.body.to_i
785
786       # get it back and check the ordering
787       get api_relation_path(relation_id)
788       assert_response :success, "can't read back the relation: #{@response.body}"
789       check_ordering(doc, @response.body)
790
791       # insert a member at the front
792       new_member = XML::Node.new "member"
793       new_member["ref"] = node3.id.to_s
794       new_member["type"] = "node"
795       new_member["role"] = "new first"
796       doc.find("//osm/relation").first.child.prev = new_member
797       # update the version, should be 1?
798       doc.find("//osm/relation").first["id"] = relation_id.to_s
799       doc.find("//osm/relation").first["version"] = 1.to_s
800
801       # upload the next version of the relation
802       put api_relation_path(relation_id), :params => doc.to_s, :headers => auth_header
803       assert_response :success, "can't update relation: #{@response.body}"
804       assert_equal 2, @response.body.to_i
805
806       # get it back again and check the ordering again
807       get api_relation_path(relation_id)
808       assert_response :success, "can't read back the relation: #{@response.body}"
809       check_ordering(doc, @response.body)
810
811       # check the ordering in the history tables:
812       with_controller(OldRelationsController.new) do
813         get api_old_relation_path(relation_id, 2)
814         assert_response :success, "can't read back version 2 of the relation #{relation_id}"
815         check_ordering(doc, @response.body)
816       end
817     end
818
819     ##
820     # check that relations can contain duplicate members
821     def test_relation_member_duplicates
822       private_user = create(:user, :data_public => false)
823       user = create(:user)
824       changeset = create(:changeset, :user => user)
825       node1 = create(:node)
826       node2 = create(:node)
827
828       doc_str = <<~OSM
829         <osm>
830          <relation changeset='#{changeset.id}'>
831           <member ref='#{node1.id}' type='node' role='forward'/>
832           <member ref='#{node2.id}' type='node' role='forward'/>
833           <member ref='#{node1.id}' type='node' role='forward'/>
834           <member ref='#{node2.id}' type='node' role='forward'/>
835          </relation>
836         </osm>
837       OSM
838       doc = XML::Parser.string(doc_str).parse
839
840       ## First try with the private user
841       auth_header = bearer_authorization_header private_user
842
843       post api_relations_path, :params => doc.to_s, :headers => auth_header
844       assert_response :forbidden
845
846       ## Now try with the public user
847       auth_header = bearer_authorization_header user
848
849       post api_relations_path, :params => doc.to_s, :headers => auth_header
850       assert_response :success, "can't create a relation: #{@response.body}"
851       relation_id = @response.body.to_i
852
853       # get it back and check the ordering
854       get api_relation_path(relation_id)
855       assert_response :success, "can't read back the relation: #{relation_id}"
856       check_ordering(doc, @response.body)
857     end
858
859     ##
860     # test that the ordering of elements in the history is the same as in current.
861     def test_history_ordering
862       user = create(:user)
863       changeset = create(:changeset, :user => user)
864       node1 = create(:node)
865       node2 = create(:node)
866       node3 = create(:node)
867       node4 = create(:node)
868
869       doc_str = <<~OSM
870         <osm>
871          <relation changeset='#{changeset.id}'>
872           <member ref='#{node1.id}' type='node' role='forward'/>
873           <member ref='#{node4.id}' type='node' role='forward'/>
874           <member ref='#{node3.id}' type='node' role='forward'/>
875           <member ref='#{node2.id}' type='node' role='forward'/>
876          </relation>
877         </osm>
878       OSM
879       doc = XML::Parser.string(doc_str).parse
880       auth_header = bearer_authorization_header user
881
882       post api_relations_path, :params => doc.to_s, :headers => auth_header
883       assert_response :success, "can't create a relation: #{@response.body}"
884       relation_id = @response.body.to_i
885
886       # check the ordering in the current tables:
887       get api_relation_path(relation_id)
888       assert_response :success, "can't read back the relation: #{@response.body}"
889       check_ordering(doc, @response.body)
890
891       # check the ordering in the history tables:
892       with_controller(OldRelationsController.new) do
893         get api_old_relation_path(relation_id, 1)
894         assert_response :success, "can't read back version 1 of the relation: #{@response.body}"
895         check_ordering(doc, @response.body)
896       end
897     end
898
899     ##
900     # remove all the members from a relation. the result is pretty useless, but
901     # still technically valid.
902     def test_remove_all_members
903       relation = create(:relation)
904       node1 = create(:node, :lat => 0.3, :lon => 0.3)
905       node2 = create(:node, :lat => 0.5, :lon => 0.5)
906       way = create(:way)
907       create(:way_node, :way => way, :node => node1)
908       create(:relation_member, :relation => relation, :member => way)
909       create(:relation_member, :relation => relation, :member => node2)
910
911       check_changeset_modify(BoundingBox.new(0.3, 0.3, 0.5, 0.5)) do |changeset_id, auth_header|
912         relation_xml = xml_for_relation(relation)
913         relation_xml
914           .find("//osm/relation/member")
915           .each(&:remove!)
916
917         # update changeset ID to point to new changeset
918         update_changeset(relation_xml, changeset_id)
919
920         # upload the change
921         put api_relation_path(relation), :params => relation_xml.to_s, :headers => auth_header
922         assert_response :success, "can't update relation for remove all members test"
923         checkrelation = Relation.find(relation.id)
924         assert_not_nil(checkrelation,
925                        "uploaded relation not found in database after upload")
926         assert_equal(0, checkrelation.members.length,
927                      "relation contains members but they should have all been deleted")
928       end
929     end
930
931     ##
932     # test initial rate limit
933     def test_initial_rate_limit
934       # create a user
935       user = create(:user)
936
937       # create some nodes
938       node1 = create(:node)
939       node2 = create(:node)
940
941       # create a changeset that puts us near the initial rate limit
942       changeset = create(:changeset, :user => user,
943                                      :created_at => Time.now.utc - 5.minutes,
944                                      :num_changes => Settings.initial_changes_per_hour - 1)
945
946       # create authentication header
947       auth_header = bearer_authorization_header user
948
949       # try creating a relation
950       xml = "<osm><relation changeset='#{changeset.id}'>" \
951             "<member  ref='#{node1.id}' type='node' role='some'/>" \
952             "<member  ref='#{node2.id}' type='node' role='some'/>" \
953             "<tag k='test' v='yes' /></relation></osm>"
954       post api_relations_path, :params => xml, :headers => auth_header
955       assert_response :success, "relation create did not return success status"
956
957       # get the id of the relation we created
958       relationid = @response.body
959
960       # try updating the relation, which should be rate limited
961       xml = "<osm><relation id='#{relationid}' version='1' changeset='#{changeset.id}'>" \
962             "<member  ref='#{node2.id}' type='node' role='some'/>" \
963             "<member  ref='#{node1.id}' type='node' role='some'/>" \
964             "<tag k='test' v='yes' /></relation></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><relation changeset='#{changeset.id}'>" \
975             "<member  ref='#{node1.id}' type='node' role='some'/>" \
976             "<member  ref='#{node2.id}' type='node' role='some'/>" \
977             "<tag k='test' v='yes' /></relation></osm>"
978       post api_relations_path, :params => xml, :headers => auth_header
979       assert_response :too_many_requests, "relation create did not hit rate limit"
980     end
981
982     ##
983     # test maximum rate limit
984     def test_maximum_rate_limit
985       # create a user
986       user = create(:user)
987
988       # create some nodes
989       node1 = create(:node)
990       node2 = create(:node)
991
992       # create a changeset to establish our initial edit time
993       changeset = create(:changeset, :user => user,
994                                      :created_at => Time.now.utc - 28.days)
995
996       # create changeset to put us near the maximum rate limit
997       total_changes = Settings.max_changes_per_hour - 1
998       while total_changes.positive?
999         changes = [total_changes, Changeset::MAX_ELEMENTS].min
1000         changeset = create(:changeset, :user => user,
1001                                        :created_at => Time.now.utc - 5.minutes,
1002                                        :num_changes => changes)
1003         total_changes -= changes
1004       end
1005
1006       # create authentication header
1007       auth_header = bearer_authorization_header user
1008
1009       # try creating a relation
1010       xml = "<osm><relation changeset='#{changeset.id}'>" \
1011             "<member  ref='#{node1.id}' type='node' role='some'/>" \
1012             "<member  ref='#{node2.id}' type='node' role='some'/>" \
1013             "<tag k='test' v='yes' /></relation></osm>"
1014       post api_relations_path, :params => xml, :headers => auth_header
1015       assert_response :success, "relation create did not return success status"
1016
1017       # get the id of the relation we created
1018       relationid = @response.body
1019
1020       # try updating the relation, which should be rate limited
1021       xml = "<osm><relation id='#{relationid}' version='1' changeset='#{changeset.id}'>" \
1022             "<member  ref='#{node2.id}' type='node' role='some'/>" \
1023             "<member  ref='#{node1.id}' type='node' role='some'/>" \
1024             "<tag k='test' v='yes' /></relation></osm>"
1025       put api_relation_path(relationid), :params => xml, :headers => auth_header
1026       assert_response :too_many_requests, "relation update did not hit rate limit"
1027
1028       # try deleting the relation, which should be rate limited
1029       xml = "<osm><relation id='#{relationid}' version='2' changeset='#{changeset.id}'/></osm>"
1030       delete api_relation_path(relationid), :params => xml, :headers => auth_header
1031       assert_response :too_many_requests, "relation delete did not hit rate limit"
1032
1033       # try creating a relation, which should be rate limited
1034       xml = "<osm><relation changeset='#{changeset.id}'>" \
1035             "<member  ref='#{node1.id}' type='node' role='some'/>" \
1036             "<member  ref='#{node2.id}' type='node' role='some'/>" \
1037             "<tag k='test' v='yes' /></relation></osm>"
1038       post api_relations_path, :params => xml, :headers => auth_header
1039       assert_response :too_many_requests, "relation create did not hit rate limit"
1040     end
1041
1042     private
1043
1044     def check_relations_for_element(path, type, id, expected_relations)
1045       # check the "relations for relation" mode
1046       get path
1047       assert_response :success
1048
1049       # count one osm element
1050       assert_select "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
1051
1052       # we should have only the expected number of relations
1053       assert_select "osm>relation", expected_relations.size
1054
1055       # and each of them should contain the element we originally searched for
1056       expected_relations.each do |relation|
1057         # The relation should appear once, but the element could appear multiple times
1058         assert_select "osm>relation[id='#{relation.id}']", 1
1059         assert_select "osm>relation[id='#{relation.id}']>member[type='#{type}'][ref='#{id}']"
1060       end
1061     end
1062
1063     ##
1064     # checks that the XML document and the string arguments have
1065     # members in the same order.
1066     def check_ordering(doc, xml)
1067       new_doc = XML::Parser.string(xml).parse
1068
1069       doc_members = doc.find("//osm/relation/member").collect do |m|
1070         [m["ref"].to_i, m["type"].to_sym, m["role"]]
1071       end
1072
1073       new_members = new_doc.find("//osm/relation/member").collect do |m|
1074         [m["ref"].to_i, m["type"].to_sym, m["role"]]
1075       end
1076
1077       doc_members.zip(new_members).each do |d, n|
1078         assert_equal d, n, "members are not equal - ordering is wrong? (#{doc}, #{xml})"
1079       end
1080     end
1081
1082     ##
1083     # create a changeset and yield to the caller to set it up, then assert
1084     # that the changeset bounding box is +bbox+.
1085     def check_changeset_modify(bbox)
1086       ## First test with the private user to check that you get a forbidden
1087       auth_header = bearer_authorization_header create(:user, :data_public => false)
1088
1089       # create a new changeset for this operation, so we are assured
1090       # that the bounding box will be newly-generated.
1091       with_controller(Api::ChangesetsController.new) do
1092         xml = "<osm><changeset/></osm>"
1093         put changeset_create_path, :params => xml, :headers => auth_header
1094         assert_response :forbidden, "shouldn't be able to create changeset for modify test, as should get forbidden"
1095       end
1096
1097       ## Now do the whole thing with the public user
1098       auth_header = bearer_authorization_header
1099
1100       # create a new changeset for this operation, so we are assured
1101       # that the bounding box will be newly-generated.
1102       changeset_id = with_controller(Api::ChangesetsController.new) do
1103         xml = "<osm><changeset/></osm>"
1104         put changeset_create_path, :params => xml, :headers => auth_header
1105         assert_response :success, "couldn't create changeset for modify test"
1106         @response.body.to_i
1107       end
1108
1109       # go back to the block to do the actual modifies
1110       yield changeset_id, auth_header
1111
1112       # now download the changeset to check its bounding box
1113       with_controller(Api::ChangesetsController.new) do
1114         get changeset_show_path(changeset_id)
1115         assert_response :success, "can't re-read changeset for modify test"
1116         assert_select "osm>changeset", 1, "Changeset element doesn't exist in #{@response.body}"
1117         assert_select "osm>changeset[id='#{changeset_id}']", 1, "Changeset id=#{changeset_id} doesn't exist in #{@response.body}"
1118         assert_select "osm>changeset[min_lon='#{format('%<lon>.7f', :lon => bbox.min_lon)}']", 1, "Changeset min_lon wrong in #{@response.body}"
1119         assert_select "osm>changeset[min_lat='#{format('%<lat>.7f', :lat => bbox.min_lat)}']", 1, "Changeset min_lat wrong in #{@response.body}"
1120         assert_select "osm>changeset[max_lon='#{format('%<lon>.7f', :lon => bbox.max_lon)}']", 1, "Changeset max_lon wrong in #{@response.body}"
1121         assert_select "osm>changeset[max_lat='#{format('%<lat>.7f', :lat => bbox.max_lat)}']", 1, "Changeset max_lat wrong in #{@response.body}"
1122       end
1123     end
1124
1125     ##
1126     # yields the relation with the given +id+ (and optional +version+
1127     # to read from the history tables) into the block. the parsed XML
1128     # doc is returned.
1129     def with_relation(id, ver = nil)
1130       if ver.nil?
1131         get api_relation_path(id)
1132       else
1133         with_controller(OldRelationsController.new) do
1134           get api_old_relation_path(id, ver)
1135         end
1136       end
1137       assert_response :success
1138       yield xml_parse(@response.body)
1139     end
1140
1141     ##
1142     # updates the relation (XML) +rel+ and
1143     # yields the new version of that relation into the block.
1144     # the parsed XML doc is returned.
1145     def with_update(rel, headers)
1146       rel_id = rel.find("//osm/relation").first["id"].to_i
1147       put api_relation_path(rel_id), :params => rel.to_s, :headers => headers
1148       assert_response :success, "can't update relation: #{@response.body}"
1149       version = @response.body.to_i
1150
1151       # now get the new version
1152       get api_relation_path(rel_id)
1153       assert_response :success
1154       new_rel = xml_parse(@response.body)
1155
1156       yield new_rel
1157
1158       version
1159     end
1160
1161     ##
1162     # updates the relation (XML) +rel+ via the diff-upload API and
1163     # yields the new version of that relation into the block.
1164     # the parsed XML doc is returned.
1165     def with_update_diff(rel, headers)
1166       rel_id = rel.find("//osm/relation").first["id"].to_i
1167       cs_id = rel.find("//osm/relation").first["changeset"].to_i
1168       version = nil
1169
1170       with_controller(Api::ChangesetsController.new) do
1171         doc = OSM::API.new.xml_doc
1172         change = XML::Node.new "osmChange"
1173         doc.root = change
1174         modify = XML::Node.new "modify"
1175         change << modify
1176         modify << doc.import(rel.find("//osm/relation").first)
1177
1178         post changeset_upload_path(cs_id), :params => doc.to_s, :headers => headers
1179         assert_response :success, "can't upload diff relation: #{@response.body}"
1180         version = xml_parse(@response.body).find("//diffResult/relation").first["new_version"].to_i
1181       end
1182
1183       # now get the new version
1184       get api_relation_path(rel_id)
1185       assert_response :success
1186       new_rel = xml_parse(@response.body)
1187
1188       yield new_rel
1189
1190       version
1191     end
1192
1193     ##
1194     # returns a k->v hash of tags from an xml doc
1195     def get_tags_as_hash(a)
1196       a.find("//osm/relation/tag").sort_by { |v| v["k"] }.each_with_object({}) do |v, h|
1197         h[v["k"]] = v["v"]
1198       end
1199     end
1200
1201     ##
1202     # assert that all tags on relation documents +a+ and +b+
1203     # are equal
1204     def assert_tags_equal(a, b)
1205       # turn the XML doc into tags hashes
1206       a_tags = get_tags_as_hash(a)
1207       b_tags = get_tags_as_hash(b)
1208
1209       assert_equal a_tags.keys, b_tags.keys, "Tag keys should be identical."
1210       a_tags.each do |k, v|
1211         assert_equal v, b_tags[k],
1212                      "Tags which were not altered should be the same. " \
1213                      "#{a_tags.inspect} != #{b_tags.inspect}"
1214       end
1215     end
1216
1217     ##
1218     # update the changeset_id of a node element
1219     def update_changeset(xml, changeset_id)
1220       xml_attr_rewrite(xml, "changeset", changeset_id)
1221     end
1222
1223     ##
1224     # update an attribute in the node element
1225     def xml_attr_rewrite(xml, name, value)
1226       xml.find("//osm/relation").first[name] = value.to_s
1227       xml
1228     end
1229
1230     ##
1231     # parse some xml
1232     def xml_parse(xml)
1233       parser = XML::Parser.string(xml)
1234       parser.parse
1235     end
1236   end
1237 end