4 class RelationsControllerTest < ActionDispatch::IntegrationTest
6 # test all routes which lead to this controller
9 { :path => "/api/0.6/relations", :method => :get },
10 { :controller => "api/relations", :action => "index" }
13 { :path => "/api/0.6/relations.json", :method => :get },
14 { :controller => "api/relations", :action => "index", :format => "json" }
17 { :path => "/api/0.6/relations", :method => :post },
18 { :controller => "api/relations", :action => "create" }
21 { :path => "/api/0.6/relation/1", :method => :get },
22 { :controller => "api/relations", :action => "show", :id => "1" }
25 { :path => "/api/0.6/relation/1.json", :method => :get },
26 { :controller => "api/relations", :action => "show", :id => "1", :format => "json" }
29 { :path => "/api/0.6/relation/1/full", :method => :get },
30 { :controller => "api/relations", :action => "show", :full => true, :id => "1" }
33 { :path => "/api/0.6/relation/1/full.json", :method => :get },
34 { :controller => "api/relations", :action => "show", :full => true, :id => "1", :format => "json" }
37 { :path => "/api/0.6/relation/1", :method => :put },
38 { :controller => "api/relations", :action => "update", :id => "1" }
41 { :path => "/api/0.6/relation/1", :method => :delete },
42 { :controller => "api/relations", :action => "destroy", :id => "1" }
46 { :path => "/api/0.6/way/1/relations", :method => :get },
47 { :controller => "api/relations", :action => "relations_for_way", :id => "1" }
50 { :path => "/api/0.6/relation/1/relations", :method => :get },
51 { :controller => "api/relations", :action => "relations_for_relation", :id => "1" }
54 { :path => "/api/0.6/way/1/relations.json", :method => :get },
55 { :controller => "api/relations", :action => "relations_for_way", :id => "1", :format => "json" }
58 { :path => "/api/0.6/relation/1/relations.json", :method => :get },
59 { :controller => "api/relations", :action => "relations_for_relation", :id => "1", :format => "json" }
63 { :controller => "api/relations", :action => "create" },
64 { :path => "/api/0.6/relation/create", :method => :put }
69 # test fetching multiple relations
71 relation1 = create(:relation)
72 relation2 = create(:relation, :deleted)
73 relation3 = create(:relation, :with_history, :version => 2)
74 relation4 = create(:relation, :with_history, :version => 2)
75 relation4.old_relations.find_by(:version => 1).redact!(create(:redaction))
77 # check error when no parameter provided
78 get api_relations_path
79 assert_response :bad_request
81 # check error when no parameter value provided
82 get api_relations_path(:relations => "")
83 assert_response :bad_request
86 get api_relations_path(:relations => "#{relation1.id},#{relation2.id},#{relation3.id},#{relation4.id}")
87 assert_response :success
88 assert_select "osm" do
89 assert_select "relation", :count => 4
90 assert_select "relation[id='#{relation1.id}'][visible='true']", :count => 1
91 assert_select "relation[id='#{relation2.id}'][visible='false']", :count => 1
92 assert_select "relation[id='#{relation3.id}'][visible='true']", :count => 1
93 assert_select "relation[id='#{relation4.id}'][visible='true']", :count => 1
96 # test a working call with json format
97 get api_relations_path(:relations => "#{relation1.id},#{relation2.id},#{relation3.id},#{relation4.id}", :format => "json")
99 js = ActiveSupport::JSON.decode(@response.body)
101 assert_equal 4, js["elements"].count
102 assert_equal 4, (js["elements"].count { |a| a["type"] == "relation" })
103 assert_equal 1, (js["elements"].count { |a| a["id"] == relation1.id && a["visible"].nil? })
104 assert_equal 1, (js["elements"].count { |a| a["id"] == relation2.id && a["visible"] == false })
105 assert_equal 1, (js["elements"].count { |a| a["id"] == relation3.id && a["visible"].nil? })
106 assert_equal 1, (js["elements"].count { |a| a["id"] == relation4.id && a["visible"].nil? })
108 # check error when a non-existent relation is included
109 get api_relations_path(:relations => "#{relation1.id},#{relation2.id},#{relation3.id},#{relation4.id},0")
110 assert_response :not_found
113 # -------------------------------------
114 # Test showing relations.
115 # -------------------------------------
117 def test_show_not_found
118 get api_relation_path(0)
119 assert_response :not_found
122 def test_show_deleted
123 get api_relation_path(create(:relation, :deleted))
124 assert_response :gone
128 relation = create(:relation, :timestamp => "2021-02-03T00:00:00Z")
129 node = create(:node, :timestamp => "2021-04-05T00:00:00Z")
130 create(:relation_member, :relation => relation, :member => node)
132 get api_relation_path(relation)
134 assert_response :success
135 assert_not_nil @response.header["Last-Modified"]
136 assert_equal "2021-02-03T00:00:00Z", Time.parse(@response.header["Last-Modified"]).utc.xmlschema
137 assert_dom "node", :count => 0
138 assert_dom "relation", :count => 1 do
139 assert_dom "> @id", :text => relation.id.to_s
143 def test_full_not_found
144 get api_relation_path(999999, :full => true)
145 assert_response :not_found
148 def test_full_deleted
149 get api_relation_path(create(:relation, :deleted), :full => true)
150 assert_response :gone
154 relation = create(:relation)
156 get api_relation_path(relation, :full => true)
158 assert_response :success
159 assert_dom "relation", :count => 1 do
160 assert_dom "> @id", :text => relation.id.to_s
164 def test_full_with_node_member
165 relation = create(:relation)
167 create(:relation_member, :relation => relation, :member => node)
169 get api_relation_path(relation, :full => true)
171 assert_response :success
172 assert_dom "node", :count => 1 do
173 assert_dom "> @id", :text => node.id.to_s
175 assert_dom "relation", :count => 1 do
176 assert_dom "> @id", :text => relation.id.to_s
180 def test_full_with_way_member
181 relation = create(:relation)
182 way = create(:way_with_nodes)
183 create(:relation_member, :relation => relation, :member => way)
185 get api_relation_path(relation, :full => true)
187 assert_response :success
188 assert_dom "node", :count => 1 do
189 assert_dom "> @id", :text => way.nodes[0].id.to_s
191 assert_dom "way", :count => 1 do
192 assert_dom "> @id", :text => way.id.to_s
194 assert_dom "relation", :count => 1 do
195 assert_dom "> @id", :text => relation.id.to_s
199 def test_full_with_node_member_json
200 relation = create(:relation)
202 create(:relation_member, :relation => relation, :member => node)
204 get api_relation_path(relation, :full => true, :format => "json")
206 assert_response :success
207 js = ActiveSupport::JSON.decode(@response.body)
209 assert_equal 2, js["elements"].count
211 js_relations = js["elements"].filter { |e| e["type"] == "relation" }
212 assert_equal 1, js_relations.count
213 assert_equal relation.id, js_relations[0]["id"]
214 assert_equal 1, js_relations[0]["members"].count
215 assert_equal "node", js_relations[0]["members"][0]["type"]
216 assert_equal node.id, js_relations[0]["members"][0]["ref"]
218 js_nodes = js["elements"].filter { |e| e["type"] == "node" }
219 assert_equal 1, js_nodes.count
220 assert_equal node.id, js_nodes[0]["id"]
223 def test_relations_for_way
225 # should include relations with that way as a member
226 relation_with_way = create(:relation_member, :member => way).relation
227 # should ignore relations without that way as a member
228 _relation_without_way = create(:relation_member).relation
229 # should ignore relations with the way involved indirectly, via a relation
230 second_relation = create(:relation_member, :member => way).relation
231 _super_relation = create(:relation_member, :member => second_relation).relation
232 # should combine multiple relation_member references into just one relation entry
233 create(:relation_member, :member => way, :relation => relation_with_way)
234 # should not include deleted relations
235 deleted_relation = create(:relation, :deleted)
236 create(:relation_member, :member => way, :relation => deleted_relation)
238 check_relations_for_element(way_relations_path(way), "way",
240 [relation_with_way, second_relation])
243 def test_relations_for_relation
244 relation = create(:relation)
245 # should include relations with that relation as a member
246 relation_with_relation = create(:relation_member, :member => relation).relation
247 # should ignore any relation without that relation as a member
248 _relation_without_relation = create(:relation_member).relation
249 # should ignore relations with the relation involved indirectly, via a relation
250 second_relation = create(:relation_member, :member => relation).relation
251 _super_relation = create(:relation_member, :member => second_relation).relation
252 # should combine multiple relation_member references into just one relation entry
253 create(:relation_member, :member => relation, :relation => relation_with_relation)
254 # should not include deleted relations
255 deleted_relation = create(:relation, :deleted)
256 create(:relation_member, :member => relation, :relation => deleted_relation)
257 check_relations_for_element(relation_relations_path(relation), "relation",
259 [relation_with_relation, second_relation])
262 # -------------------------------------
263 # Test simple relation creation.
264 # -------------------------------------
267 private_user = create(:user, :data_public => false)
268 private_changeset = create(:changeset, :user => private_user)
270 changeset = create(:changeset, :user => user)
272 way = create(:way_with_nodes, :nodes_count => 2)
274 auth_header = bearer_authorization_header private_user
276 # create an relation without members
277 xml = "<osm><relation changeset='#{private_changeset.id}'><tag k='test' v='yes' /></relation></osm>"
278 post api_relations_path, :params => xml, :headers => auth_header
279 # hope for forbidden, due to user
280 assert_response :forbidden,
281 "relation upload should have failed with forbidden"
284 # create an relation with a node as member
285 # This time try with a role attribute in the relation
286 xml = "<osm><relation changeset='#{private_changeset.id}'>" \
287 "<member ref='#{node.id}' type='node' role='some'/>" \
288 "<tag k='test' v='yes' /></relation></osm>"
289 post api_relations_path, :params => xml, :headers => auth_header
290 # hope for forbidden due to user
291 assert_response :forbidden,
292 "relation upload did not return forbidden status"
295 # create an relation with a node as member, this time test that we don't
296 # need a role attribute to be included
297 xml = "<osm><relation changeset='#{private_changeset.id}'>" \
298 "<member ref='#{node.id}' type='node'/><tag k='test' v='yes' /></relation></osm>"
299 post api_relations_path, :params => xml, :headers => auth_header
300 # hope for forbidden due to user
301 assert_response :forbidden,
302 "relation upload did not return forbidden status"
305 # create an relation with a way and a node as members
306 xml = "<osm><relation changeset='#{private_changeset.id}'>" \
307 "<member type='node' ref='#{node.id}' role='some'/>" \
308 "<member type='way' ref='#{way.id}' role='other'/>" \
309 "<tag k='test' v='yes' /></relation></osm>"
310 post api_relations_path, :params => xml, :headers => auth_header
311 # hope for forbidden, due to user
312 assert_response :forbidden,
313 "relation upload did not return success status"
315 ## Now try with the public user
316 auth_header = bearer_authorization_header user
318 # create an relation without members
319 xml = "<osm><relation changeset='#{changeset.id}'><tag k='test' v='yes' /></relation></osm>"
320 post api_relations_path, :params => xml, :headers => auth_header
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"
330 assert_equal(0, checkrelation.members.length, "saved relation contains members but should not")
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 get api_relation_path(relationid)
340 assert_response :success
343 # create an relation with a node as member
344 # This time try with a role attribute in the relation
345 xml = "<osm><relation changeset='#{changeset.id}'>" \
346 "<member ref='#{node.id}' type='node' role='some'/>" \
347 "<tag k='test' v='yes' /></relation></osm>"
348 post api_relations_path, :params => xml, :headers => auth_header
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"
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?
368 get api_relation_path(relationid)
369 assert_response :success
372 # create an relation with a node as member, this time test that we don't
373 # need a role attribute to be included
374 xml = "<osm><relation changeset='#{changeset.id}'>" \
375 "<member ref='#{node.id}' type='node'/><tag k='test' v='yes' /></relation></osm>"
376 post api_relations_path, :params => xml, :headers => auth_header
378 assert_response :success,
379 "relation upload did not return success status"
380 # read id of created relation and search for it
381 relationid = @response.body
382 checkrelation = Relation.find(relationid)
383 assert_not_nil checkrelation,
384 "uploaded relation not found in data base after upload"
386 assert_equal(1, checkrelation.members.length, "saved relation does not contain exactly one member")
387 assert_equal(1, checkrelation.tags.length, "saved relation does not contain exactly one tag")
388 assert_equal changeset.id, checkrelation.changeset.id,
389 "saved relation does not belong in the changeset it was assigned to"
390 assert_equal user.id, checkrelation.changeset.user_id,
391 "saved relation does not belong to user that created it"
392 assert checkrelation.visible,
393 "saved relation is not visible"
394 # ok the relation is there but can we also retrieve it?
396 get api_relation_path(relationid)
397 assert_response :success
400 # create an relation with a way and a node as members
401 xml = "<osm><relation changeset='#{changeset.id}'>" \
402 "<member type='node' ref='#{node.id}' role='some'/>" \
403 "<member type='way' ref='#{way.id}' role='other'/>" \
404 "<tag k='test' v='yes' /></relation></osm>"
405 post api_relations_path, :params => xml, :headers => auth_header
407 assert_response :success,
408 "relation upload did not return success status"
409 # read id of created relation and search for it
410 relationid = @response.body
411 checkrelation = Relation.find(relationid)
412 assert_not_nil checkrelation,
413 "uploaded relation not found in data base after upload"
415 assert_equal(2, checkrelation.members.length, "saved relation does not have exactly two members")
416 assert_equal(1, checkrelation.tags.length, "saved relation does not contain exactly one tag")
417 assert_equal changeset.id, checkrelation.changeset.id,
418 "saved relation does not belong in the changeset it was assigned to"
419 assert_equal user.id, checkrelation.changeset.user_id,
420 "saved relation does not belong to user that created it"
421 assert checkrelation.visible,
422 "saved relation is not visible"
423 # ok the relation is there but can we also retrieve it?
424 get api_relation_path(relationid)
425 assert_response :success
428 # ------------------------------------
429 # Test updating relations
430 # ------------------------------------
433 # test that, when tags are updated on a relation, the correct things
434 # happen to the correct tables and the API gives sensible results.
435 # this is to test a case that gregory marler noticed and posted to
437 ## FIXME Move this to an integration test
438 def test_update_relation_tags
440 changeset = create(:changeset, :user => user)
441 relation = create(:relation)
442 create_list(:relation_tag, 4, :relation => relation)
444 auth_header = bearer_authorization_header user
446 with_relation(relation.id) do |rel|
447 # alter one of the tags
448 tag = rel.find("//osm/relation/tag").first
449 tag["v"] = "some changed value"
450 update_changeset(rel, changeset.id)
452 # check that the downloaded tags are the same as the uploaded tags...
453 new_version = with_update(rel, auth_header) do |new_rel|
454 assert_tags_equal rel, new_rel
457 # check the original one in the current_* table again
458 with_relation(relation.id) { |r| assert_tags_equal rel, r }
460 # now check the version in the history
461 with_relation(relation.id, new_version) { |r| assert_tags_equal rel, r }
466 # test that, when tags are updated on a relation when using the diff
467 # upload function, the correct things happen to the correct tables
468 # and the API gives sensible results. this is to test a case that
469 # gregory marler noticed and posted to josm-dev.
470 def test_update_relation_tags_via_upload
472 changeset = create(:changeset, :user => user)
473 relation = create(:relation)
474 create_list(:relation_tag, 4, :relation => relation)
476 auth_header = bearer_authorization_header user
478 with_relation(relation.id) do |rel|
479 # alter one of the tags
480 tag = rel.find("//osm/relation/tag").first
481 tag["v"] = "some changed value"
482 update_changeset(rel, changeset.id)
484 # check that the downloaded tags are the same as the uploaded tags...
485 new_version = with_update_diff(rel, auth_header) do |new_rel|
486 assert_tags_equal rel, new_rel
489 # check the original one in the current_* table again
490 with_relation(relation.id) { |r| assert_tags_equal rel, r }
492 # now check the version in the history
493 with_relation(relation.id, new_version) { |r| assert_tags_equal rel, r }
497 def test_update_wrong_id
499 changeset = create(:changeset, :user => user)
500 relation = create(:relation)
501 other_relation = create(:relation)
503 auth_header = bearer_authorization_header user
504 with_relation(relation.id) do |rel|
505 update_changeset(rel, changeset.id)
506 put api_relation_path(other_relation), :params => rel.to_s, :headers => auth_header
507 assert_response :bad_request
511 # -------------------------------------
512 # Test creating some invalid relations.
513 # -------------------------------------
515 def test_create_invalid
517 changeset = create(:changeset, :user => user)
519 auth_header = bearer_authorization_header user
521 # create a relation with non-existing node as member
522 xml = "<osm><relation changeset='#{changeset.id}'>" \
523 "<member type='node' ref='0'/><tag k='test' v='yes' />" \
525 post api_relations_path, :params => xml, :headers => auth_header
527 assert_response :precondition_failed,
528 "relation upload with invalid node did not return 'precondition failed'"
529 assert_equal "Precondition failed: Relation with id cannot be saved due to Node with id 0", @response.body
532 # -------------------------------------
533 # Test creating a relation, with some invalid XML
534 # -------------------------------------
535 def test_create_invalid_xml
537 changeset = create(:changeset, :user => user)
540 auth_header = bearer_authorization_header user
542 # create some xml that should return an error
543 xml = "<osm><relation changeset='#{changeset.id}'>" \
544 "<member type='type' ref='#{node.id}' role=''/>" \
545 "<tag k='tester' v='yep'/></relation></osm>"
546 post api_relations_path, :params => xml, :headers => auth_header
548 assert_response :bad_request
549 assert_match(/Cannot parse valid relation from xml string/, @response.body)
550 assert_match(/The type is not allowed only, /, @response.body)
553 # -------------------------------------
554 # Test deleting relations.
555 # -------------------------------------
558 private_user = create(:user, :data_public => false)
559 private_user_closed_changeset = create(:changeset, :closed, :user => private_user)
561 closed_changeset = create(:changeset, :closed, :user => user)
562 changeset = create(:changeset, :user => user)
563 relation = create(:relation)
564 used_relation = create(:relation)
565 super_relation = create(:relation_member, :member => used_relation).relation
566 deleted_relation = create(:relation, :deleted)
567 multi_tag_relation = create(:relation)
568 create_list(:relation_tag, 4, :relation => multi_tag_relation)
570 ## First try to delete relation without auth
571 delete api_relation_path(relation)
572 assert_response :unauthorized
574 ## Then try with the private user, to make sure that you get a forbidden
575 auth_header = bearer_authorization_header private_user
577 # this shouldn't work, as we should need the payload...
578 delete api_relation_path(relation), :headers => auth_header
579 assert_response :forbidden
581 # try to delete without specifying a changeset
582 xml = "<osm><relation id='#{relation.id}'/></osm>"
583 delete api_relation_path(relation), :params => xml.to_s, :headers => auth_header
584 assert_response :forbidden
586 # try to delete with an invalid (closed) changeset
587 xml = update_changeset(xml_for_relation(relation),
588 private_user_closed_changeset.id)
589 delete api_relation_path(relation), :params => xml.to_s, :headers => auth_header
590 assert_response :forbidden
592 # try to delete with an invalid (non-existent) changeset
593 xml = update_changeset(xml_for_relation(relation), 0)
594 delete api_relation_path(relation), :params => xml.to_s, :headers => auth_header
595 assert_response :forbidden
597 # this won't work because the relation is in-use by another relation
598 xml = xml_for_relation(used_relation)
599 delete api_relation_path(used_relation), :params => xml.to_s, :headers => auth_header
600 assert_response :forbidden
602 # this should work when we provide the appropriate payload...
603 xml = xml_for_relation(relation)
604 delete api_relation_path(relation), :params => xml.to_s, :headers => auth_header
605 assert_response :forbidden
607 # this won't work since the relation is already deleted
608 xml = xml_for_relation(deleted_relation)
609 delete api_relation_path(deleted_relation), :params => xml.to_s, :headers => auth_header
610 assert_response :forbidden
612 # this won't work since the relation never existed
613 delete api_relation_path(0), :headers => auth_header
614 assert_response :forbidden
616 ## now set auth for the public user
617 auth_header = bearer_authorization_header user
619 # this shouldn't work, as we should need the payload...
620 delete api_relation_path(relation), :headers => auth_header
621 assert_response :bad_request
623 # try to delete without specifying a changeset
624 xml = "<osm><relation id='#{relation.id}' version='#{relation.version}' /></osm>"
625 delete api_relation_path(relation), :params => xml.to_s, :headers => auth_header
626 assert_response :bad_request
627 assert_match(/Changeset id is missing/, @response.body)
629 # try to delete with an invalid (closed) changeset
630 xml = update_changeset(xml_for_relation(relation),
632 delete api_relation_path(relation), :params => xml.to_s, :headers => auth_header
633 assert_response :conflict
635 # try to delete with an invalid (non-existent) changeset
636 xml = update_changeset(xml_for_relation(relation), 0)
637 delete api_relation_path(relation), :params => xml.to_s, :headers => auth_header
638 assert_response :conflict
640 # this won't work because the relation is in a changeset owned by someone else
641 xml = update_changeset(xml_for_relation(relation), create(:changeset).id)
642 delete api_relation_path(relation), :params => xml.to_s, :headers => auth_header
643 assert_response :conflict,
644 "shouldn't be able to delete a relation in a changeset owned by someone else (#{@response.body})"
646 # this won't work because the relation in the payload is different to that passed
647 xml = update_changeset(xml_for_relation(relation), changeset.id)
648 delete api_relation_path(create(:relation)), :params => xml.to_s, :headers => auth_header
649 assert_response :bad_request, "shouldn't be able to delete a relation when payload is different to the url"
651 # this won't work because the relation is in-use by another relation
652 xml = update_changeset(xml_for_relation(used_relation), changeset.id)
653 delete api_relation_path(used_relation), :params => xml.to_s, :headers => auth_header
654 assert_response :precondition_failed,
655 "shouldn't be able to delete a relation used in a relation (#{@response.body})"
656 assert_equal "Precondition failed: The relation #{used_relation.id} is used in relation #{super_relation.id}.", @response.body
658 # this should work when we provide the appropriate payload...
659 xml = update_changeset(xml_for_relation(multi_tag_relation), changeset.id)
660 delete api_relation_path(multi_tag_relation), :params => xml.to_s, :headers => auth_header
661 assert_response :success
663 # valid delete should return the new version number, which should
664 # be greater than the old version number
665 assert_operator @response.body.to_i, :>, multi_tag_relation.version, "delete request should return a new version number for relation"
667 # this won't work since the relation is already deleted
668 xml = update_changeset(xml_for_relation(deleted_relation), changeset.id)
669 delete api_relation_path(deleted_relation), :params => xml.to_s, :headers => auth_header
670 assert_response :gone
672 # Public visible relation needs to be deleted
673 xml = update_changeset(xml_for_relation(super_relation), changeset.id)
674 delete api_relation_path(super_relation), :params => xml.to_s, :headers => auth_header
675 assert_response :success
677 # this works now because the relation which was using this one
679 xml = update_changeset(xml_for_relation(used_relation), changeset.id)
680 delete api_relation_path(used_relation), :params => xml.to_s, :headers => auth_header
681 assert_response :success,
682 "should be able to delete a relation used in an old relation (#{@response.body})"
684 # this won't work since the relation never existed
685 delete api_relation_path(0), :headers => auth_header
686 assert_response :not_found
690 # when a relation's tag is modified then it should put the bounding
691 # box of all its members into the changeset.
692 def test_tag_modify_bounding_box
693 relation = create(:relation)
694 node1 = create(:node, :lat => 0.3, :lon => 0.3)
695 node2 = create(:node, :lat => 0.5, :lon => 0.5)
697 create(:way_node, :way => way, :node => node1)
698 create(:relation_member, :relation => relation, :member => way)
699 create(:relation_member, :relation => relation, :member => node2)
700 # the relation contains nodes1 and node2 (node1
701 # indirectly via the way), so the bbox should be [0.3,0.3,0.5,0.5].
702 check_changeset_modify(BoundingBox.new(0.3, 0.3, 0.5, 0.5)) do |changeset_id, auth_header|
703 # add a tag to an existing relation
704 relation_xml = xml_for_relation(relation)
705 relation_element = relation_xml.find("//osm/relation").first
706 new_tag = XML::Node.new("tag")
707 new_tag["k"] = "some_new_tag"
708 new_tag["v"] = "some_new_value"
709 relation_element << new_tag
711 # update changeset ID to point to new changeset
712 update_changeset(relation_xml, changeset_id)
715 put api_relation_path(relation), :params => relation_xml.to_s, :headers => auth_header
716 assert_response :success, "can't update relation for tag/bbox test"
721 # add a member to a relation and check the bounding box is only that
723 def test_add_member_bounding_box
724 relation = create(:relation)
725 node1 = create(:node, :lat => 4, :lon => 4)
726 node2 = create(:node, :lat => 7, :lon => 7)
728 create(:way_node, :way => way1, :node => create(:node, :lat => 8, :lon => 8))
730 create(:way_node, :way => way2, :node => create(:node, :lat => 9, :lon => 9), :sequence_id => 1)
731 create(:way_node, :way => way2, :node => create(:node, :lat => 10, :lon => 10), :sequence_id => 2)
733 [node1, node2, way1, way2].each do |element|
734 bbox = element.bbox.to_unscaled
735 check_changeset_modify(bbox) do |changeset_id, auth_header|
736 relation_xml = xml_for_relation(Relation.find(relation.id))
737 relation_element = relation_xml.find("//osm/relation").first
738 new_member = XML::Node.new("member")
739 new_member["ref"] = element.id.to_s
740 new_member["type"] = element.class.to_s.downcase
741 new_member["role"] = "some_role"
742 relation_element << new_member
744 # update changeset ID to point to new changeset
745 update_changeset(relation_xml, changeset_id)
748 put api_relation_path(relation), :params => relation_xml.to_s, :headers => auth_header
749 assert_response :success, "can't update relation for add #{element.class}/bbox test: #{@response.body}"
751 # get it back and check the ordering
752 get api_relation_path(relation)
753 assert_response :success, "can't read back the relation: #{@response.body}"
754 check_ordering(relation_xml, @response.body)
760 # remove a member from a relation and check the bounding box is
762 def test_remove_member_bounding_box
763 relation = create(:relation)
764 node1 = create(:node, :lat => 3, :lon => 3)
765 node2 = create(:node, :lat => 5, :lon => 5)
766 create(:relation_member, :relation => relation, :member => node1)
767 create(:relation_member, :relation => relation, :member => node2)
769 check_changeset_modify(BoundingBox.new(5, 5, 5, 5)) do |changeset_id, auth_header|
770 # remove node 5 (5,5) from an existing relation
771 relation_xml = xml_for_relation(relation)
773 .find("//osm/relation/member[@type='node'][@ref='#{node2.id}']")
776 # update changeset ID to point to new changeset
777 update_changeset(relation_xml, changeset_id)
780 put api_relation_path(relation), :params => relation_xml.to_s, :headers => auth_header
781 assert_response :success, "can't update relation for remove node/bbox test"
786 # check that relations are ordered
787 def test_relation_member_ordering
789 changeset = create(:changeset, :user => user)
790 node1 = create(:node)
791 node2 = create(:node)
792 node3 = create(:node)
793 way1 = create(:way_with_nodes, :nodes_count => 2)
794 way2 = create(:way_with_nodes, :nodes_count => 2)
796 auth_header = bearer_authorization_header user
800 <relation changeset='#{changeset.id}'>
801 <member ref='#{node1.id}' type='node' role='first'/>
802 <member ref='#{node2.id}' type='node' role='second'/>
803 <member ref='#{way1.id}' type='way' role='third'/>
804 <member ref='#{way2.id}' type='way' role='fourth'/>
808 doc = XML::Parser.string(doc_str).parse
810 post api_relations_path, :params => doc.to_s, :headers => auth_header
811 assert_response :success, "can't create a relation: #{@response.body}"
812 relation_id = @response.body.to_i
814 # get it back and check the ordering
815 get api_relation_path(relation_id)
816 assert_response :success, "can't read back the relation: #{@response.body}"
817 check_ordering(doc, @response.body)
819 # insert a member at the front
820 new_member = XML::Node.new "member"
821 new_member["ref"] = node3.id.to_s
822 new_member["type"] = "node"
823 new_member["role"] = "new first"
824 doc.find("//osm/relation").first.child.prev = new_member
825 # update the version, should be 1?
826 doc.find("//osm/relation").first["id"] = relation_id.to_s
827 doc.find("//osm/relation").first["version"] = 1.to_s
829 # upload the next version of the relation
830 put api_relation_path(relation_id), :params => doc.to_s, :headers => auth_header
831 assert_response :success, "can't update relation: #{@response.body}"
832 assert_equal 2, @response.body.to_i
834 # get it back again and check the ordering again
835 get api_relation_path(relation_id)
836 assert_response :success, "can't read back the relation: #{@response.body}"
837 check_ordering(doc, @response.body)
839 # check the ordering in the history tables:
840 with_controller(OldRelationsController.new) do
841 get api_old_relation_path(relation_id, 2)
842 assert_response :success, "can't read back version 2 of the relation #{relation_id}"
843 check_ordering(doc, @response.body)
848 # check that relations can contain duplicate members
849 def test_relation_member_duplicates
850 private_user = create(:user, :data_public => false)
852 changeset = create(:changeset, :user => user)
853 node1 = create(:node)
854 node2 = create(:node)
858 <relation changeset='#{changeset.id}'>
859 <member ref='#{node1.id}' type='node' role='forward'/>
860 <member ref='#{node2.id}' type='node' role='forward'/>
861 <member ref='#{node1.id}' type='node' role='forward'/>
862 <member ref='#{node2.id}' type='node' role='forward'/>
866 doc = XML::Parser.string(doc_str).parse
868 ## First try with the private user
869 auth_header = bearer_authorization_header private_user
871 post api_relations_path, :params => doc.to_s, :headers => auth_header
872 assert_response :forbidden
874 ## Now try with the public user
875 auth_header = bearer_authorization_header user
877 post api_relations_path, :params => doc.to_s, :headers => auth_header
878 assert_response :success, "can't create a relation: #{@response.body}"
879 relation_id = @response.body.to_i
881 # get it back and check the ordering
882 get api_relation_path(relation_id)
883 assert_response :success, "can't read back the relation: #{relation_id}"
884 check_ordering(doc, @response.body)
888 # test that the ordering of elements in the history is the same as in current.
889 def test_history_ordering
891 changeset = create(:changeset, :user => user)
892 node1 = create(:node)
893 node2 = create(:node)
894 node3 = create(:node)
895 node4 = create(:node)
899 <relation changeset='#{changeset.id}'>
900 <member ref='#{node1.id}' type='node' role='forward'/>
901 <member ref='#{node4.id}' type='node' role='forward'/>
902 <member ref='#{node3.id}' type='node' role='forward'/>
903 <member ref='#{node2.id}' type='node' role='forward'/>
907 doc = XML::Parser.string(doc_str).parse
908 auth_header = bearer_authorization_header user
910 post api_relations_path, :params => doc.to_s, :headers => auth_header
911 assert_response :success, "can't create a relation: #{@response.body}"
912 relation_id = @response.body.to_i
914 # check the ordering in the current tables:
915 get api_relation_path(relation_id)
916 assert_response :success, "can't read back the relation: #{@response.body}"
917 check_ordering(doc, @response.body)
919 # check the ordering in the history tables:
920 with_controller(OldRelationsController.new) do
921 get api_old_relation_path(relation_id, 1)
922 assert_response :success, "can't read back version 1 of the relation: #{@response.body}"
923 check_ordering(doc, @response.body)
928 # remove all the members from a relation. the result is pretty useless, but
929 # still technically valid.
930 def test_remove_all_members
931 relation = create(:relation)
932 node1 = create(:node, :lat => 0.3, :lon => 0.3)
933 node2 = create(:node, :lat => 0.5, :lon => 0.5)
935 create(:way_node, :way => way, :node => node1)
936 create(:relation_member, :relation => relation, :member => way)
937 create(:relation_member, :relation => relation, :member => node2)
939 check_changeset_modify(BoundingBox.new(0.3, 0.3, 0.5, 0.5)) do |changeset_id, auth_header|
940 relation_xml = xml_for_relation(relation)
942 .find("//osm/relation/member")
945 # update changeset ID to point to new changeset
946 update_changeset(relation_xml, changeset_id)
949 put api_relation_path(relation), :params => relation_xml.to_s, :headers => auth_header
950 assert_response :success, "can't update relation for remove all members test"
951 checkrelation = Relation.find(relation.id)
952 assert_not_nil(checkrelation,
953 "uploaded relation not found in database after upload")
954 assert_equal(0, checkrelation.members.length,
955 "relation contains members but they should have all been deleted")
960 # test initial rate limit
961 def test_initial_rate_limit
966 node1 = create(:node)
967 node2 = create(:node)
969 # create a changeset that puts us near the initial rate limit
970 changeset = create(:changeset, :user => user,
971 :created_at => Time.now.utc - 5.minutes,
972 :num_changes => Settings.initial_changes_per_hour - 1)
974 # create authentication header
975 auth_header = bearer_authorization_header user
977 # try creating a relation
978 xml = "<osm><relation changeset='#{changeset.id}'>" \
979 "<member ref='#{node1.id}' type='node' role='some'/>" \
980 "<member ref='#{node2.id}' type='node' role='some'/>" \
981 "<tag k='test' v='yes' /></relation></osm>"
982 post api_relations_path, :params => xml, :headers => auth_header
983 assert_response :success, "relation create did not return success status"
985 # get the id of the relation we created
986 relationid = @response.body
988 # try updating the relation, which should be rate limited
989 xml = "<osm><relation id='#{relationid}' version='1' changeset='#{changeset.id}'>" \
990 "<member ref='#{node2.id}' type='node' role='some'/>" \
991 "<member ref='#{node1.id}' type='node' role='some'/>" \
992 "<tag k='test' v='yes' /></relation></osm>"
993 put api_relation_path(relationid), :params => xml, :headers => auth_header
994 assert_response :too_many_requests, "relation update did not hit rate limit"
996 # try deleting the relation, which should be rate limited
997 xml = "<osm><relation id='#{relationid}' version='2' changeset='#{changeset.id}'/></osm>"
998 delete api_relation_path(relationid), :params => xml, :headers => auth_header
999 assert_response :too_many_requests, "relation delete did not hit rate limit"
1001 # try creating a relation, which should be rate limited
1002 xml = "<osm><relation changeset='#{changeset.id}'>" \
1003 "<member ref='#{node1.id}' type='node' role='some'/>" \
1004 "<member ref='#{node2.id}' type='node' role='some'/>" \
1005 "<tag k='test' v='yes' /></relation></osm>"
1006 post api_relations_path, :params => xml, :headers => auth_header
1007 assert_response :too_many_requests, "relation create did not hit rate limit"
1011 # test maximum rate limit
1012 def test_maximum_rate_limit
1014 user = create(:user)
1017 node1 = create(:node)
1018 node2 = create(:node)
1020 # create a changeset to establish our initial edit time
1021 changeset = create(:changeset, :user => user,
1022 :created_at => Time.now.utc - 28.days)
1024 # create changeset to put us near the maximum rate limit
1025 total_changes = Settings.max_changes_per_hour - 1
1026 while total_changes.positive?
1027 changes = [total_changes, Changeset::MAX_ELEMENTS].min
1028 changeset = create(:changeset, :user => user,
1029 :created_at => Time.now.utc - 5.minutes,
1030 :num_changes => changes)
1031 total_changes -= changes
1034 # create authentication header
1035 auth_header = bearer_authorization_header user
1037 # try creating a relation
1038 xml = "<osm><relation changeset='#{changeset.id}'>" \
1039 "<member ref='#{node1.id}' type='node' role='some'/>" \
1040 "<member ref='#{node2.id}' type='node' role='some'/>" \
1041 "<tag k='test' v='yes' /></relation></osm>"
1042 post api_relations_path, :params => xml, :headers => auth_header
1043 assert_response :success, "relation create did not return success status"
1045 # get the id of the relation we created
1046 relationid = @response.body
1048 # try updating the relation, which should be rate limited
1049 xml = "<osm><relation id='#{relationid}' version='1' changeset='#{changeset.id}'>" \
1050 "<member ref='#{node2.id}' type='node' role='some'/>" \
1051 "<member ref='#{node1.id}' type='node' role='some'/>" \
1052 "<tag k='test' v='yes' /></relation></osm>"
1053 put api_relation_path(relationid), :params => xml, :headers => auth_header
1054 assert_response :too_many_requests, "relation update did not hit rate limit"
1056 # try deleting the relation, which should be rate limited
1057 xml = "<osm><relation id='#{relationid}' version='2' changeset='#{changeset.id}'/></osm>"
1058 delete api_relation_path(relationid), :params => xml, :headers => auth_header
1059 assert_response :too_many_requests, "relation delete did not hit rate limit"
1061 # try creating a relation, which should be rate limited
1062 xml = "<osm><relation changeset='#{changeset.id}'>" \
1063 "<member ref='#{node1.id}' type='node' role='some'/>" \
1064 "<member ref='#{node2.id}' type='node' role='some'/>" \
1065 "<tag k='test' v='yes' /></relation></osm>"
1066 post api_relations_path, :params => xml, :headers => auth_header
1067 assert_response :too_many_requests, "relation create did not hit rate limit"
1072 def check_relations_for_element(path, type, id, expected_relations)
1073 # check the "relations for relation" mode
1075 assert_response :success
1077 # count one osm element
1078 assert_select "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
1080 # we should have only the expected number of relations
1081 assert_select "osm>relation", expected_relations.size
1083 # and each of them should contain the element we originally searched for
1084 expected_relations.each do |relation|
1085 # The relation should appear once, but the element could appear multiple times
1086 assert_select "osm>relation[id='#{relation.id}']", 1
1087 assert_select "osm>relation[id='#{relation.id}']>member[type='#{type}'][ref='#{id}']"
1092 # checks that the XML document and the string arguments have
1093 # members in the same order.
1094 def check_ordering(doc, xml)
1095 new_doc = XML::Parser.string(xml).parse
1097 doc_members = doc.find("//osm/relation/member").collect do |m|
1098 [m["ref"].to_i, m["type"].to_sym, m["role"]]
1101 new_members = new_doc.find("//osm/relation/member").collect do |m|
1102 [m["ref"].to_i, m["type"].to_sym, m["role"]]
1105 doc_members.zip(new_members).each do |d, n|
1106 assert_equal d, n, "members are not equal - ordering is wrong? (#{doc}, #{xml})"
1111 # create a changeset and yield to the caller to set it up, then assert
1112 # that the changeset bounding box is +bbox+.
1113 def check_changeset_modify(bbox)
1114 ## First test with the private user to check that you get a forbidden
1115 auth_header = bearer_authorization_header create(:user, :data_public => false)
1117 # create a new changeset for this operation, so we are assured
1118 # that the bounding box will be newly-generated.
1119 with_controller(Api::ChangesetsController.new) do
1120 xml = "<osm><changeset/></osm>"
1121 put changeset_create_path, :params => xml, :headers => auth_header
1122 assert_response :forbidden, "shouldn't be able to create changeset for modify test, as should get forbidden"
1125 ## Now do the whole thing with the public user
1126 auth_header = bearer_authorization_header
1128 # create a new changeset for this operation, so we are assured
1129 # that the bounding box will be newly-generated.
1130 changeset_id = with_controller(Api::ChangesetsController.new) do
1131 xml = "<osm><changeset/></osm>"
1132 put changeset_create_path, :params => xml, :headers => auth_header
1133 assert_response :success, "couldn't create changeset for modify test"
1137 # go back to the block to do the actual modifies
1138 yield changeset_id, auth_header
1140 # now download the changeset to check its bounding box
1141 with_controller(Api::ChangesetsController.new) do
1142 get changeset_show_path(changeset_id)
1143 assert_response :success, "can't re-read changeset for modify test"
1144 assert_select "osm>changeset", 1, "Changeset element doesn't exist in #{@response.body}"
1145 assert_select "osm>changeset[id='#{changeset_id}']", 1, "Changeset id=#{changeset_id} doesn't exist in #{@response.body}"
1146 assert_select "osm>changeset[min_lon='#{format('%<lon>.7f', :lon => bbox.min_lon)}']", 1, "Changeset min_lon wrong in #{@response.body}"
1147 assert_select "osm>changeset[min_lat='#{format('%<lat>.7f', :lat => bbox.min_lat)}']", 1, "Changeset min_lat wrong in #{@response.body}"
1148 assert_select "osm>changeset[max_lon='#{format('%<lon>.7f', :lon => bbox.max_lon)}']", 1, "Changeset max_lon wrong in #{@response.body}"
1149 assert_select "osm>changeset[max_lat='#{format('%<lat>.7f', :lat => bbox.max_lat)}']", 1, "Changeset max_lat wrong in #{@response.body}"
1154 # yields the relation with the given +id+ (and optional +version+
1155 # to read from the history tables) into the block. the parsed XML
1157 def with_relation(id, ver = nil)
1159 get api_relation_path(id)
1161 with_controller(OldRelationsController.new) do
1162 get api_old_relation_path(id, ver)
1165 assert_response :success
1166 yield xml_parse(@response.body)
1170 # updates the relation (XML) +rel+ and
1171 # yields the new version of that relation into the block.
1172 # the parsed XML doc is returned.
1173 def with_update(rel, headers)
1174 rel_id = rel.find("//osm/relation").first["id"].to_i
1175 put api_relation_path(rel_id), :params => rel.to_s, :headers => headers
1176 assert_response :success, "can't update relation: #{@response.body}"
1177 version = @response.body.to_i
1179 # now get the new version
1180 get api_relation_path(rel_id)
1181 assert_response :success
1182 new_rel = xml_parse(@response.body)
1190 # updates the relation (XML) +rel+ via the diff-upload API and
1191 # yields the new version of that relation into the block.
1192 # the parsed XML doc is returned.
1193 def with_update_diff(rel, headers)
1194 rel_id = rel.find("//osm/relation").first["id"].to_i
1195 cs_id = rel.find("//osm/relation").first["changeset"].to_i
1198 with_controller(Api::ChangesetsController.new) do
1199 doc = OSM::API.new.xml_doc
1200 change = XML::Node.new "osmChange"
1202 modify = XML::Node.new "modify"
1204 modify << doc.import(rel.find("//osm/relation").first)
1206 post changeset_upload_path(cs_id), :params => doc.to_s, :headers => headers
1207 assert_response :success, "can't upload diff relation: #{@response.body}"
1208 version = xml_parse(@response.body).find("//diffResult/relation").first["new_version"].to_i
1211 # now get the new version
1212 get api_relation_path(rel_id)
1213 assert_response :success
1214 new_rel = xml_parse(@response.body)
1222 # returns a k->v hash of tags from an xml doc
1223 def get_tags_as_hash(a)
1224 a.find("//osm/relation/tag").sort_by { |v| v["k"] }.each_with_object({}) do |v, h|
1230 # assert that all tags on relation documents +a+ and +b+
1232 def assert_tags_equal(a, b)
1233 # turn the XML doc into tags hashes
1234 a_tags = get_tags_as_hash(a)
1235 b_tags = get_tags_as_hash(b)
1237 assert_equal a_tags.keys, b_tags.keys, "Tag keys should be identical."
1238 a_tags.each do |k, v|
1239 assert_equal v, b_tags[k],
1240 "Tags which were not altered should be the same. " \
1241 "#{a_tags.inspect} != #{b_tags.inspect}"
1246 # update the changeset_id of a node element
1247 def update_changeset(xml, changeset_id)
1248 xml_attr_rewrite(xml, "changeset", changeset_id)
1252 # update an attribute in the node element
1253 def xml_attr_rewrite(xml, name, value)
1254 xml.find("//osm/relation").first[name] = value.to_s
1261 parser = XML::Parser.string(xml)