source: subversion/sites/rails_port/test/functional/relation_controller_test.rb @ 14586

Last change on this file since 14586 was 14586, checked in by tomhughes, 10 years ago

Merge api06 branch to trunk.

File size: 19.7 KB
Line 
1require File.dirname(__FILE__) + '/../test_helper'
2require 'relation_controller'
3
4class RelationControllerTest < ActionController::TestCase
5  api_fixtures
6
7  # -------------------------------------
8  # Test reading relations.
9  # -------------------------------------
10
11  def test_read
12    # check that a visible relation is returned properly
13    get :read, :id => current_relations(:visible_relation).id
14    assert_response :success
15
16    # check that an invisible relation is not returned
17    get :read, :id => current_relations(:invisible_relation).id
18    assert_response :gone
19
20    # check chat a non-existent relation is not returned
21    get :read, :id => 0
22    assert_response :not_found
23  end
24
25  ##
26  # check that all relations containing a particular node, and no extra
27  # relations, are returned from the relations_for_node call.
28  def test_relations_for_node
29    check_relations_for_element(:relations_for_node, "node", 
30                                current_nodes(:node_used_by_relationship).id,
31                                [ :visible_relation, :used_relation ])
32  end
33
34  def test_relations_for_way
35    check_relations_for_element(:relations_for_way, "way",
36                                current_ways(:used_way).id,
37                                [ :visible_relation ])
38  end
39
40  def test_relations_for_relation
41    check_relations_for_element(:relations_for_relation, "relation",
42                                current_relations(:used_relation).id,
43                                [ :visible_relation ])
44  end
45
46  def check_relations_for_element(method, type, id, expected_relations)
47    # check the "relations for relation" mode
48    get method, :id => id
49    assert_response :success
50
51    # count one osm element
52    assert_select "osm[version=#{API_VERSION}][generator=\"OpenStreetMap server\"]", 1
53
54    # we should have only the expected number of relations
55    assert_select "osm>relation", expected_relations.size
56
57    # and each of them should contain the node we originally searched for
58    expected_relations.each do |r|
59      relation_id = current_relations(r).id
60      assert_select "osm>relation#?", relation_id
61      assert_select "osm>relation#?>member[type=\"#{type}\"][ref=#{id}]", relation_id
62    end
63  end
64
65  def test_full
66    # check the "full" mode
67    get :full, :id => current_relations(:visible_relation).id
68    assert_response :success
69    # FIXME check whether this contains the stuff we want!
70    if $VERBOSE
71        print @response.body
72    end
73  end
74
75  # -------------------------------------
76  # Test simple relation creation.
77  # -------------------------------------
78
79  def test_create
80    basic_authorization "test@openstreetmap.org", "test"
81   
82    # put the relation in a dummy fixture changset
83    changeset_id = changesets(:normal_user_first_change).id
84
85    # create an relation without members
86    content "<osm><relation changeset='#{changeset_id}'><tag k='test' v='yes' /></relation></osm>"
87    put :create
88    # hope for success
89    assert_response :success, 
90        "relation upload did not return success status"
91    # read id of created relation and search for it
92    relationid = @response.body
93    checkrelation = Relation.find(relationid)
94    assert_not_nil checkrelation, 
95        "uploaded relation not found in data base after upload"
96    # compare values
97    assert_equal checkrelation.members.length, 0, 
98        "saved relation contains members but should not"
99    assert_equal checkrelation.tags.length, 1, 
100        "saved relation does not contain exactly one tag"
101    assert_equal changeset_id, checkrelation.changeset.id,
102        "saved relation does not belong in the changeset it was assigned to"
103    assert_equal users(:normal_user).id, checkrelation.changeset.user_id, 
104        "saved relation does not belong to user that created it"
105    assert_equal true, checkrelation.visible, 
106        "saved relation is not visible"
107    # ok the relation is there but can we also retrieve it?
108    get :read, :id => relationid
109    assert_response :success
110
111
112    ###
113    # create an relation with a node as member
114    # This time try with a role attribute in the relation
115    nid = current_nodes(:used_node_1).id
116    content "<osm><relation changeset='#{changeset_id}'>" +
117      "<member  ref='#{nid}' type='node' role='some'/>" +
118      "<tag k='test' v='yes' /></relation></osm>"
119    put :create
120    # hope for success
121    assert_response :success, 
122        "relation upload did not return success status"
123    # read id of created relation and search for it
124    relationid = @response.body
125    checkrelation = Relation.find(relationid)
126    assert_not_nil checkrelation, 
127        "uploaded relation not found in data base after upload"
128    # compare values
129    assert_equal checkrelation.members.length, 1, 
130        "saved relation does not contain exactly one member"
131    assert_equal checkrelation.tags.length, 1, 
132        "saved relation does not contain exactly one tag"
133    assert_equal changeset_id, checkrelation.changeset.id,
134        "saved relation does not belong in the changeset it was assigned to"
135    assert_equal users(:normal_user).id, checkrelation.changeset.user_id, 
136        "saved relation does not belong to user that created it"
137    assert_equal true, checkrelation.visible, 
138        "saved relation is not visible"
139    # ok the relation is there but can we also retrieve it?
140   
141    get :read, :id => relationid
142    assert_response :success
143   
144   
145    ###
146    # create an relation with a node as member, this time test that we don't
147    # need a role attribute to be included
148    nid = current_nodes(:used_node_1).id
149    content "<osm><relation changeset='#{changeset_id}'>" +
150      "<member  ref='#{nid}' type='node'/>"+
151      "<tag k='test' v='yes' /></relation></osm>"
152    put :create
153    # hope for success
154    assert_response :success, 
155        "relation upload did not return success status"
156    # read id of created relation and search for it
157    relationid = @response.body
158    checkrelation = Relation.find(relationid)
159    assert_not_nil checkrelation, 
160        "uploaded relation not found in data base after upload"
161    # compare values
162    assert_equal checkrelation.members.length, 1, 
163        "saved relation does not contain exactly one member"
164    assert_equal checkrelation.tags.length, 1, 
165        "saved relation does not contain exactly one tag"
166    assert_equal changeset_id, checkrelation.changeset.id,
167        "saved relation does not belong in the changeset it was assigned to"
168    assert_equal users(:normal_user).id, checkrelation.changeset.user_id, 
169        "saved relation does not belong to user that created it"
170    assert_equal true, checkrelation.visible, 
171        "saved relation is not visible"
172    # ok the relation is there but can we also retrieve it?
173   
174    get :read, :id => relationid
175    assert_response :success
176
177    ###
178    # create an relation with a way and a node as members
179    nid = current_nodes(:used_node_1).id
180    wid = current_ways(:used_way).id
181    content "<osm><relation changeset='#{changeset_id}'>" +
182      "<member type='node' ref='#{nid}' role='some'/>" +
183      "<member type='way' ref='#{wid}' role='other'/>" +
184      "<tag k='test' v='yes' /></relation></osm>"
185    put :create
186    # hope for success
187    assert_response :success, 
188        "relation upload did not return success status"
189    # read id of created relation and search for it
190    relationid = @response.body
191    checkrelation = Relation.find(relationid)
192    assert_not_nil checkrelation, 
193        "uploaded relation not found in data base after upload"
194    # compare values
195    assert_equal checkrelation.members.length, 2, 
196        "saved relation does not have exactly two members"
197    assert_equal checkrelation.tags.length, 1, 
198        "saved relation does not contain exactly one tag"
199    assert_equal changeset_id, checkrelation.changeset.id,
200        "saved relation does not belong in the changeset it was assigned to"
201    assert_equal users(:normal_user).id, checkrelation.changeset.user_id, 
202        "saved relation does not belong to user that created it"
203    assert_equal true, checkrelation.visible, 
204        "saved relation is not visible"
205    # ok the relation is there but can we also retrieve it?
206    get :read, :id => relationid
207    assert_response :success
208
209  end
210
211  # -------------------------------------
212  # Test creating some invalid relations.
213  # -------------------------------------
214
215  def test_create_invalid
216    basic_authorization "test@openstreetmap.org", "test"
217
218    # put the relation in a dummy fixture changset
219    changeset_id = changesets(:normal_user_first_change).id
220
221    # create a relation with non-existing node as member
222    content "<osm><relation changeset='#{changeset_id}'>" +
223      "<member type='node' ref='0'/><tag k='test' v='yes' />" +
224      "</relation></osm>"
225    put :create
226    # expect failure
227    assert_response :precondition_failed, 
228        "relation upload with invalid node did not return 'precondition failed'"
229  end
230
231  # -------------------------------------
232  # Test creating a relation, with some invalid XML
233  # -------------------------------------
234  def test_create_invalid_xml
235    basic_authorization "test@openstreetmap.org", "test"
236   
237    # put the relation in a dummy fixture changeset that works
238    changeset_id = changesets(:normal_user_first_change).id
239   
240    # create some xml that should return an error
241    content "<osm><relation changeset='#{changeset_id}'>" +
242    "<member type='type' ref='#{current_nodes(:used_node_1).id}' role=''/>" +
243    "<tag k='tester' v='yep'/></relation></osm>"
244    put :create
245    # expect failure
246    assert_response :bad_request
247    assert_match(/Cannot parse valid relation from xml string/, @response.body)
248    assert_match(/The type is not allowed only, /, @response.body)
249  end
250 
251 
252  # -------------------------------------
253  # Test deleting relations.
254  # -------------------------------------
255 
256  def test_delete
257    # first try to delete relation without auth
258    delete :delete, :id => current_relations(:visible_relation).id
259    assert_response :unauthorized
260
261    # now set auth
262    basic_authorization("test@openstreetmap.org", "test"); 
263
264    # this shouldn't work, as we should need the payload...
265    delete :delete, :id => current_relations(:visible_relation).id
266    assert_response :bad_request
267
268    # try to delete without specifying a changeset
269    content "<osm><relation id='#{current_relations(:visible_relation).id}'/></osm>"
270    delete :delete, :id => current_relations(:visible_relation).id
271    assert_response :bad_request
272    assert_match(/You are missing the required changeset in the relation/, @response.body)
273
274    # try to delete with an invalid (closed) changeset
275    content update_changeset(current_relations(:visible_relation).to_xml,
276                             changesets(:normal_user_closed_change).id)
277    delete :delete, :id => current_relations(:visible_relation).id
278    assert_response :conflict
279
280    # try to delete with an invalid (non-existent) changeset
281    content update_changeset(current_relations(:visible_relation).to_xml,0)
282    delete :delete, :id => current_relations(:visible_relation).id
283    assert_response :conflict
284
285    # this won't work because the relation is in-use by another relation
286    content(relations(:used_relation).to_xml)
287    delete :delete, :id => current_relations(:used_relation).id
288    assert_response :precondition_failed, 
289       "shouldn't be able to delete a relation used in a relation (#{@response.body})"
290
291    # this should work when we provide the appropriate payload...
292    content(relations(:visible_relation).to_xml)
293    delete :delete, :id => current_relations(:visible_relation).id
294    assert_response :success
295
296    # valid delete should return the new version number, which should
297    # be greater than the old version number
298    assert @response.body.to_i > current_relations(:visible_relation).version,
299       "delete request should return a new version number for relation"
300
301    # this won't work since the relation is already deleted
302    content(relations(:invisible_relation).to_xml)
303    delete :delete, :id => current_relations(:invisible_relation).id
304    assert_response :gone
305
306    # this works now because the relation which was using this one
307    # has been deleted.
308    content(relations(:used_relation).to_xml)
309    delete :delete, :id => current_relations(:used_relation).id
310    assert_response :success, 
311       "should be able to delete a relation used in an old relation (#{@response.body})"
312
313    # this won't work since the relation never existed
314    delete :delete, :id => 0
315    assert_response :not_found
316  end
317
318  ##
319  # when a relation's tag is modified then it should put the bounding
320  # box of all its members into the changeset.
321  def test_tag_modify_bounding_box
322    # in current fixtures, relation 5 contains nodes 3 and 5 (node 3
323    # indirectly via way 3), so the bbox should be [3,3,5,5].
324    check_changeset_modify([3,3,5,5]) do |changeset_id|
325      # add a tag to an existing relation
326      relation_xml = current_relations(:visible_relation).to_xml
327      relation_element = relation_xml.find("//osm/relation").first
328      new_tag = XML::Node.new("tag")
329      new_tag['k'] = "some_new_tag"
330      new_tag['v'] = "some_new_value"
331      relation_element << new_tag
332     
333      # update changeset ID to point to new changeset
334      update_changeset(relation_xml, changeset_id)
335     
336      # upload the change
337      content relation_xml
338      put :update, :id => current_relations(:visible_relation).id
339      assert_response :success, "can't update relation for tag/bbox test"
340    end
341  end
342
343  ##
344  # add a member to a relation and check the bounding box is only that
345  # element.
346  def test_add_member_bounding_box
347    check_changeset_modify([4,4,4,4]) do |changeset_id|
348      # add node 4 (4,4) to an existing relation
349      relation_xml = current_relations(:visible_relation).to_xml
350      relation_element = relation_xml.find("//osm/relation").first
351      new_member = XML::Node.new("member")
352      new_member['ref'] = current_nodes(:used_node_2).id.to_s
353      new_member['type'] = "node"
354      new_member['role'] = "some_role"
355      relation_element << new_member
356     
357      # update changeset ID to point to new changeset
358      update_changeset(relation_xml, changeset_id)
359     
360      # upload the change
361      content relation_xml
362      put :update, :id => current_relations(:visible_relation).id
363      assert_response :success, "can't update relation for add node/bbox test"
364    end
365  end
366 
367  ##
368  # remove a member from a relation and check the bounding box is
369  # only that element.
370  def test_remove_member_bounding_box
371    check_changeset_modify([5,5,5,5]) do |changeset_id|
372      # remove node 5 (5,5) from an existing relation
373      relation_xml = current_relations(:visible_relation).to_xml
374      relation_xml.
375        find("//osm/relation/member[@type='node'][@ref='5']").
376        first.remove!
377     
378      # update changeset ID to point to new changeset
379      update_changeset(relation_xml, changeset_id)
380     
381      # upload the change
382      content relation_xml
383      put :update, :id => current_relations(:visible_relation).id
384      assert_response :success, "can't update relation for remove node/bbox test"
385    end
386  end
387 
388  ##
389  # check that relations are ordered
390  def test_relation_member_ordering
391    basic_authorization("test@openstreetmap.org", "test"); 
392
393    doc_str = <<OSM
394<osm>
395 <relation changeset='1'>
396  <member ref='1' type='node' role='first'/>
397  <member ref='3' type='node' role='second'/>
398  <member ref='1' type='way' role='third'/>
399  <member ref='3' type='way' role='fourth'/>
400 </relation>
401</osm>
402OSM
403    doc = XML::Parser.string(doc_str).parse
404
405    content doc
406    put :create
407    assert_response :success, "can't create a relation: #{@response.body}"
408    relation_id = @response.body.to_i
409
410    # get it back and check the ordering
411    get :read, :id => relation_id
412    assert_response :success, "can't read back the relation: #{@response.body}"
413    check_ordering(doc, @response.body)
414
415    # insert a member at the front
416    new_member = XML::Node.new "member"
417    new_member['ref'] = 5.to_s
418    new_member['type'] = 'node'
419    new_member['role'] = 'new first'
420    doc.find("//osm/relation").first.child.prev = new_member
421    # update the version, should be 1?
422    doc.find("//osm/relation").first['id'] = relation_id.to_s
423    doc.find("//osm/relation").first['version'] = 1.to_s
424
425    # upload the next version of the relation
426    content doc
427    put :update, :id => relation_id
428    assert_response :success, "can't update relation: #{@response.body}"
429    new_version = @response.body.to_i
430
431    # get it back again and check the ordering again
432    get :read, :id => relation_id
433    assert_response :success, "can't read back the relation: #{@response.body}"
434    check_ordering(doc, @response.body)
435  end
436
437  ##
438  # check that relations can contain duplicate members
439  def test_relation_member_duplicates
440    basic_authorization("test@openstreetmap.org", "test"); 
441
442    doc_str = <<OSM
443<osm>
444 <relation changeset='1'>
445  <member ref='1' type='node' role='forward'/>
446  <member ref='3' type='node' role='forward'/>
447  <member ref='1' type='node' role='forward'/>
448  <member ref='3' type='node' role='forward'/>
449 </relation>
450</osm>
451OSM
452    doc = XML::Parser.string(doc_str).parse
453
454    content doc
455    put :create
456    assert_response :success, "can't create a relation: #{@response.body}"
457    relation_id = @response.body.to_i
458
459    # get it back and check the ordering
460    get :read, :id => relation_id
461    assert_response :success, "can't read back the relation: #{@response.body}"
462    check_ordering(doc, @response.body)
463  end
464
465  # ============================================================
466  # utility functions
467  # ============================================================
468
469  ##
470  # checks that the XML document and the string arguments have
471  # members in the same order.
472  def check_ordering(doc, xml)
473    new_doc = XML::Parser.string(xml).parse
474
475    doc_members = doc.find("//osm/relation/member").collect do |m|
476      [m['ref'].to_i, m['type'].to_sym, m['role']]
477    end
478
479    new_members = new_doc.find("//osm/relation/member").collect do |m|
480      [m['ref'].to_i, m['type'].to_sym, m['role']]
481    end
482
483    doc_members.zip(new_members).each do |d, n|
484      assert_equal d, n, "members are not equal - ordering is wrong? (#{doc}, #{xml})"
485    end
486  end
487
488  ##
489  # create a changeset and yield to the caller to set it up, then assert
490  # that the changeset bounding box is +bbox+.
491  def check_changeset_modify(bbox)
492    basic_authorization("test@openstreetmap.org", "test"); 
493
494    # create a new changeset for this operation, so we are assured
495    # that the bounding box will be newly-generated.
496    changeset_id = with_controller(ChangesetController.new) do
497      content "<osm><changeset/></osm>"
498      put :create
499      assert_response :success, "couldn't create changeset for modify test"
500      @response.body.to_i
501    end
502
503    # go back to the block to do the actual modifies
504    yield changeset_id
505
506    # now download the changeset to check its bounding box
507    with_controller(ChangesetController.new) do
508      get :read, :id => changeset_id
509      assert_response :success, "can't re-read changeset for modify test"
510      assert_select "osm>changeset", 1
511      assert_select "osm>changeset[id=#{changeset_id}]", 1
512      assert_select "osm>changeset[min_lon=#{bbox[0].to_f}]", 1
513      assert_select "osm>changeset[min_lat=#{bbox[1].to_f}]", 1
514      assert_select "osm>changeset[max_lon=#{bbox[2].to_f}]", 1
515      assert_select "osm>changeset[max_lat=#{bbox[3].to_f}]", 1
516    end
517  end
518
519  ##
520  # update the changeset_id of a node element
521  def update_changeset(xml, changeset_id)
522    xml_attr_rewrite(xml, 'changeset', changeset_id)
523  end
524
525  ##
526  # update an attribute in the node element
527  def xml_attr_rewrite(xml, name, value)
528    xml.find("//osm/relation").first[name] = value.to_s
529    return xml
530  end
531
532  ##
533  # parse some xml
534  def xml_parse(xml)
535    parser = XML::Parser.string(xml)
536    parser.parse
537  end
538end
Note: See TracBrowser for help on using the repository browser.