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