source: subversion/sites/rails_port/test/functional/changeset_controller_test.rb @ 17073

Revision 17073, 53.6 KB checked in by zere, 5 years ago (diff)

Fixes bug #2152 by preventing over-expansion of bboxes beyond the world range.

Line 
1require File.dirname(__FILE__) + '/../test_helper'
2require 'changeset_controller'
3
4class ChangesetControllerTest < ActionController::TestCase
5  api_fixtures
6
7  # -----------------------
8  # Test simple changeset creation
9  # -----------------------
10 
11  def test_create
12    basic_authorization users(:normal_user).email, "test"
13    # Create the first user's changeset
14    content "<osm><changeset>" +
15      "<tag k='created_by' v='osm test suite checking changesets'/>" + 
16      "</changeset></osm>"
17    put :create
18    assert_require_public_data
19   
20   
21    basic_authorization users(:public_user).email, "test"
22    # Create the first user's changeset
23    content "<osm><changeset>" +
24      "<tag k='created_by' v='osm test suite checking changesets'/>" + 
25      "</changeset></osm>"
26    put :create
27   
28    assert_response :success, "Creation of changeset did not return sucess status"
29    newid = @response.body.to_i
30
31    # check end time, should be an hour ahead of creation time
32    cs = Changeset.find(newid)
33    duration = cs.closed_at - cs.created_at
34    # the difference can either be a rational, or a floating point number
35    # of seconds, depending on the code path taken :-(
36    if duration.class == Rational
37      assert_equal Rational(1,24), duration , "initial idle timeout should be an hour (#{cs.created_at} -> #{cs.closed_at})"
38    else
39      # must be number of seconds...
40      assert_equal 3600, duration.round, "initial idle timeout should be an hour (#{cs.created_at} -> #{cs.closed_at})"
41    end
42  end
43 
44  def test_create_invalid
45    basic_authorization users(:normal_user).email, "test"
46    content "<osm><changeset></osm>"
47    put :create
48    assert_require_public_data
49
50    ## Try the public user
51    basic_authorization users(:public_user).email, "test"
52    content "<osm><changeset></osm>"
53    put :create
54    assert_response :bad_request, "creating a invalid changeset should fail"
55  end
56
57  def test_create_invalid_no_content
58    ## First check with no auth
59    put :create
60    assert_response :unauthorized, "shouldn't be able to create a changeset with no auth"
61   
62    ## Now try to with the non-public user
63    basic_authorization users(:normal_user).email, "test"
64    put :create
65    assert_require_public_data
66   
67    ## Try the inactive user
68    basic_authorization users(:inactive_user).email, "test"
69    put :create
70    assert_inactive_user
71   
72    ## Now try to use the public user
73    basic_authorization users(:public_user).email, "test"
74    put :create
75    assert_response :bad_request, "creating a changeset with no content should fail"
76  end
77 
78  def test_create_wrong_method
79    basic_authorization users(:public_user).email, "test"
80    get :create
81    assert_response :method_not_allowed
82    post :create
83    assert_response :method_not_allowed
84  end
85
86  ##
87  # check that the changeset can be read and returns the correct
88  # document structure.
89  def test_read
90    changeset_id = changesets(:normal_user_first_change).id
91    get :read, :id => changeset_id
92    assert_response :success, "cannot get first changeset"
93   
94    assert_select "osm[version=#{API_VERSION}][generator=\"OpenStreetMap server\"]", 1
95    assert_select "osm>changeset[id=#{changeset_id}]", 1
96  end
97 
98  ##
99  # check that a changeset that doesn't exist returns an appropriate message
100  def test_read_not_found
101    [0, -32, 233455644, "afg", "213"].each do |id|
102      get :read, :id => id
103      assert_response :not_found, "should get a not found"
104    end
105  end
106 
107  ##
108  # test that the user who opened a change can close it
109  def test_close
110    ## Try without authentication
111    put :close, :id => changesets(:public_user_first_change).id
112    assert_response :unauthorized
113   
114   
115    ## Try using the non-public user
116    basic_authorization users(:normal_user).email, "test"
117    put :close, :id => changesets(:normal_user_first_change).id
118    assert_require_public_data
119   
120   
121    ## The try with the public user
122    basic_authorization users(:public_user).email, "test"
123
124    cs_id = changesets(:public_user_first_change).id
125    put :close, :id => cs_id
126    assert_response :success
127
128    # test that it really is closed now
129    cs = Changeset.find(cs_id)
130    assert(!cs.is_open?, 
131           "changeset should be closed now (#{cs.closed_at} > #{Time.now.getutc}.")
132  end
133
134  ##
135  # test that a different user can't close another user's changeset
136  def test_close_invalid
137    basic_authorization users(:public_user).email, "test"
138
139    put :close, :id => changesets(:normal_user_first_change).id
140    assert_response :conflict
141    assert_equal "The user doesn't own that changeset", @response.body
142  end
143 
144  ##
145  # test that you can't close using another method
146  def test_close_method_invalid
147    basic_authorization users(:public_user).email, "test"
148   
149    cs_id = changesets(:public_user_first_change).id
150    get :close, :id => cs_id
151    assert_response :method_not_allowed
152   
153    post :close, :id => cs_id
154    assert_response :method_not_allowed
155  end
156 
157  ##
158  # check that you can't close a changeset that isn't found
159  def test_close_not_found
160    cs_ids = [0, -132, "123"]
161   
162    # First try to do it with no auth
163    cs_ids.each do |id|
164      put :close, :id => id
165      assert_response :unauthorized, "Shouldn't be able close the non-existant changeset #{id}, when not authorized"
166    end
167   
168    # Now try with auth
169    basic_authorization users(:public_user).email, "test"
170    cs_ids.each do |id|
171      put :close, :id => id
172      assert_response :not_found, "The changeset #{id} doesn't exist, so can't be closed"
173    end
174  end
175
176  ##
177  # upload something simple, but valid and check that it can
178  # be read back ok
179  # Also try without auth and another user.
180  def test_upload_simple_valid
181    ## Try with no auth
182    changeset_id = changesets(:public_user_first_change).id
183
184    # simple diff to change a node, way and relation by removing
185    # their tags
186    diff = <<EOF
187<osmChange>
188 <modify>
189  <node id='1' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
190  <way id='1' changeset='#{changeset_id}' version='1'>
191   <nd ref='3'/>
192  </way>
193 </modify>
194 <modify>
195  <relation id='1' changeset='#{changeset_id}' version='1'>
196   <member type='way' role='some' ref='3'/>
197   <member type='node' role='some' ref='5'/>
198   <member type='relation' role='some' ref='3'/>
199  </relation>
200 </modify>
201</osmChange>
202EOF
203
204    # upload it
205    content diff
206    post :upload, :id => changeset_id
207    assert_response :unauthorized, 
208      "shouldnn't be able to upload a simple valid diff to changeset: #{@response.body}"
209     
210     
211   
212    ## Now try with a private user
213    basic_authorization users(:normal_user).email, "test"
214    changeset_id = changesets(:normal_user_first_change).id
215
216    # simple diff to change a node, way and relation by removing
217    # their tags
218    diff = <<EOF
219<osmChange>
220 <modify>
221  <node id='1' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
222  <way id='1' changeset='#{changeset_id}' version='1'>
223   <nd ref='3'/>
224  </way>
225 </modify>
226 <modify>
227  <relation id='1' changeset='#{changeset_id}' version='1'>
228   <member type='way' role='some' ref='3'/>
229   <member type='node' role='some' ref='5'/>
230   <member type='relation' role='some' ref='3'/>
231  </relation>
232 </modify>
233</osmChange>
234EOF
235
236    # upload it
237    content diff
238    post :upload, :id => changeset_id
239    assert_response :forbidden, 
240      "can't upload a simple valid diff to changeset: #{@response.body}"   
241   
242     
243     
244    ## Now try with the public user
245    basic_authorization users(:public_user).email, "test"
246    changeset_id = changesets(:public_user_first_change).id
247
248    # simple diff to change a node, way and relation by removing
249    # their tags
250    diff = <<EOF
251<osmChange>
252 <modify>
253  <node id='1' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
254  <way id='1' changeset='#{changeset_id}' version='1'>
255   <nd ref='3'/>
256  </way>
257 </modify>
258 <modify>
259  <relation id='1' changeset='#{changeset_id}' version='1'>
260   <member type='way' role='some' ref='3'/>
261   <member type='node' role='some' ref='5'/>
262   <member type='relation' role='some' ref='3'/>
263  </relation>
264 </modify>
265</osmChange>
266EOF
267
268    # upload it
269    content diff
270    post :upload, :id => changeset_id
271    assert_response :success, 
272      "can't upload a simple valid diff to changeset: #{@response.body}"
273
274    # check that the changes made it into the database
275    assert_equal 0, Node.find(1).tags.size, "node 1 should now have no tags"
276    assert_equal 0, Way.find(1).tags.size, "way 1 should now have no tags"
277    assert_equal 0, Relation.find(1).tags.size, "relation 1 should now have no tags"
278  end
279   
280  ##
281  # upload something which creates new objects using placeholders
282  def test_upload_create_valid
283    basic_authorization users(:public_user).email, "test"
284    cs_id = changesets(:public_user_first_change).id
285
286    # simple diff to create a node way and relation using placeholders
287    diff = <<EOF
288<osmChange>
289 <create>
290  <node id='-1' lon='0' lat='0' changeset='#{cs_id}'>
291   <tag k='foo' v='bar'/>
292   <tag k='baz' v='bat'/>
293  </node>
294  <way id='-1' changeset='#{cs_id}'>
295   <nd ref='3'/>
296  </way>
297 </create>
298 <create>
299  <relation id='-1' changeset='#{cs_id}'>
300   <member type='way' role='some' ref='3'/>
301   <member type='node' role='some' ref='5'/>
302   <member type='relation' role='some' ref='3'/>
303  </relation>
304 </create>
305</osmChange>
306EOF
307
308    # upload it
309    content diff
310    post :upload, :id => cs_id
311    assert_response :success, 
312      "can't upload a simple valid creation to changeset: #{@response.body}"
313
314    # check the returned payload
315    assert_select "diffResult[version=#{API_VERSION}][generator=\"OpenStreetMap server\"]", 1
316    assert_select "diffResult>node", 1
317    assert_select "diffresult>way", 1
318    assert_select "diffResult>relation", 1
319
320    # inspect the response to find out what the new element IDs are
321    doc = XML::Parser.string(@response.body).parse
322    new_node_id = doc.find("//diffResult/node").first["new_id"].to_i
323    new_way_id = doc.find("//diffResult/way").first["new_id"].to_i
324    new_rel_id = doc.find("//diffResult/relation").first["new_id"].to_i
325
326    # check the old IDs are all present and negative one
327    assert_equal -1, doc.find("//diffResult/node").first["old_id"].to_i
328    assert_equal -1, doc.find("//diffResult/way").first["old_id"].to_i
329    assert_equal -1, doc.find("//diffResult/relation").first["old_id"].to_i
330
331    # check the versions are present and equal one
332    assert_equal 1, doc.find("//diffResult/node").first["new_version"].to_i
333    assert_equal 1, doc.find("//diffResult/way").first["new_version"].to_i
334    assert_equal 1, doc.find("//diffResult/relation").first["new_version"].to_i
335
336    # check that the changes made it into the database
337    assert_equal 2, Node.find(new_node_id).tags.size, "new node should have two tags"
338    assert_equal 0, Way.find(new_way_id).tags.size, "new way should have no tags"
339    assert_equal 0, Relation.find(new_rel_id).tags.size, "new relation should have no tags"
340  end
341   
342  ##
343  # test a complex delete where we delete elements which rely on eachother
344  # in the same transaction.
345  def test_upload_delete
346    basic_authorization users(:public_user).display_name, "test"
347
348    diff = XML::Document.new
349    diff.root = XML::Node.new "osmChange"
350    delete = XML::Node.new "delete"
351    diff.root << delete
352    delete << current_relations(:visible_relation).to_xml_node
353    delete << current_relations(:used_relation).to_xml_node
354    delete << current_ways(:used_way).to_xml_node
355    delete << current_nodes(:node_used_by_relationship).to_xml_node
356
357    # update the changeset to one that this user owns
358    changeset_id = changesets(:public_user_first_change).id
359    ["node", "way", "relation"].each do |type|
360      delete.find("//osmChange/delete/#{type}").each do |n| 
361        n['changeset'] = changeset_id.to_s
362      end
363    end
364
365    # upload it
366    content diff
367    post :upload, :id => changeset_id
368    assert_response :success, 
369      "can't upload a deletion diff to changeset: #{@response.body}"
370
371    # check the response is well-formed
372    assert_select "diffResult>node", 1
373    assert_select "diffResult>way", 1
374    assert_select "diffResult>relation", 2
375
376    # check that everything was deleted
377    assert_equal false, Node.find(current_nodes(:node_used_by_relationship).id).visible
378    assert_equal false, Way.find(current_ways(:used_way).id).visible
379    assert_equal false, Relation.find(current_relations(:visible_relation).id).visible
380    assert_equal false, Relation.find(current_relations(:used_relation).id).visible
381  end
382
383  ##
384  # test uploading a delete with no lat/lon, as they are optional in
385  # the osmChange spec.
386  def test_upload_nolatlon_delete
387    basic_authorization users(:public_user).display_name, "test"
388
389    node = current_nodes(:public_visible_node)
390    cs = changesets(:public_user_first_change)
391    diff = "<osmChange><delete><node id='#{node.id}' version='#{node.version}' changeset='#{cs.id}'/></delete></osmChange>"
392
393    # upload it
394    content diff
395    post :upload, :id => cs.id
396    assert_response :success, 
397      "can't upload a deletion diff to changeset: #{@response.body}"
398
399    # check the response is well-formed
400    assert_select "diffResult>node", 1
401
402    # check that everything was deleted
403    assert_equal false, Node.find(node.id).visible
404  end
405
406  def test_repeated_changeset_create
407    30.times do
408      basic_authorization users(:public_user).email, "test"
409   
410      # create a temporary changeset
411      content "<osm><changeset>" +
412        "<tag k='created_by' v='osm test suite checking changesets'/>" + 
413        "</changeset></osm>"
414      assert_difference('Changeset.count', 1) do
415        put :create
416      end
417      assert_response :success
418      changeset_id = @response.body.to_i
419    end
420  end
421
422  def test_upload_large_changeset
423    basic_authorization users(:public_user).email, "test"
424
425    # create a changeset
426    content "<osm><changeset/></osm>"
427    put :create
428    assert_response :success, "Should be able to create a changeset: #{@response.body}"
429    changeset_id = @response.body.to_i
430   
431    # upload some widely-spaced nodes, spiralling positive and negative to cause
432    # largest bbox over-expansion possible.
433    diff = <<EOF
434<osmChange>
435 <create>
436  <node id='-1' lon='-20' lat='-10' changeset='#{changeset_id}'/>
437  <node id='-10' lon='20'  lat='10' changeset='#{changeset_id}'/>
438  <node id='-2' lon='-40' lat='-20' changeset='#{changeset_id}'/>
439  <node id='-11' lon='40'  lat='20' changeset='#{changeset_id}'/>
440  <node id='-3' lon='-60' lat='-30' changeset='#{changeset_id}'/>
441  <node id='-12' lon='60'  lat='30' changeset='#{changeset_id}'/>
442  <node id='-4' lon='-80' lat='-40' changeset='#{changeset_id}'/>
443  <node id='-13' lon='80'  lat='40' changeset='#{changeset_id}'/>
444  <node id='-5' lon='-100' lat='-50' changeset='#{changeset_id}'/>
445  <node id='-14' lon='100'  lat='50' changeset='#{changeset_id}'/>
446  <node id='-6' lon='-120' lat='-60' changeset='#{changeset_id}'/>
447  <node id='-15' lon='120'  lat='60' changeset='#{changeset_id}'/>
448  <node id='-7' lon='-140' lat='-70' changeset='#{changeset_id}'/>
449  <node id='-16' lon='140'  lat='70' changeset='#{changeset_id}'/>
450  <node id='-8' lon='-160' lat='-80' changeset='#{changeset_id}'/>
451  <node id='-17' lon='160'  lat='80' changeset='#{changeset_id}'/>
452  <node id='-9' lon='-179.9' lat='-89.9' changeset='#{changeset_id}'/>
453  <node id='-18' lon='179.9'  lat='89.9' changeset='#{changeset_id}'/>
454 </create>
455</osmChange>
456EOF
457
458    # upload it, which used to cause an error like "PGError: ERROR:
459    # integer out of range" (bug #2152). but shouldn't any more.
460    content diff
461    post :upload, :id => changeset_id
462    assert_response :success, 
463      "can't upload a spatially-large diff to changeset: #{@response.body}"
464
465    # check that the changeset bbox is within bounds
466    cs = Changeset.find(changeset_id)
467    assert cs.min_lon >= -180 * SCALE, "Minimum longitude (#{cs.min_lon / SCALE}) should be >= -180 to be valid."
468    assert cs.max_lon <=  180 * SCALE, "Maximum longitude (#{cs.max_lon / SCALE}) should be <= 180 to be valid."
469    assert cs.min_lat >=  -90 * SCALE, "Minimum latitude (#{cs.min_lat / SCALE}) should be >= -90 to be valid."
470    assert cs.max_lat >=   90 * SCALE, "Maximum latitude (#{cs.max_lat / SCALE}) should be <= 90 to be valid."
471  end
472
473  ##
474  # test that deleting stuff in a transaction doesn't bypass the checks
475  # to ensure that used elements are not deleted.
476  def test_upload_delete_invalid
477    basic_authorization users(:public_user).email, "test"
478
479    diff = XML::Document.new
480    diff.root = XML::Node.new "osmChange"
481    delete = XML::Node.new "delete"
482    diff.root << delete
483    delete << current_relations(:public_visible_relation).to_xml_node
484    delete << current_ways(:used_way).to_xml_node
485    delete << current_nodes(:node_used_by_relationship).to_xml_node
486
487    # upload it
488    content diff
489    post :upload, :id => 2
490    assert_response :precondition_failed, 
491      "shouldn't be able to upload a invalid deletion diff: #{@response.body}"
492    assert_equal "Precondition failed: Way 3 still used by relation 1.", @response.body
493
494    # check that nothing was, in fact, deleted
495    assert_equal true, Node.find(current_nodes(:node_used_by_relationship).id).visible
496    assert_equal true, Way.find(current_ways(:used_way).id).visible
497    assert_equal true, Relation.find(current_relations(:visible_relation).id).visible
498  end
499
500  ##
501  # upload an element with a really long tag value
502  def test_upload_invalid_too_long_tag
503    basic_authorization users(:public_user).email, "test"
504    cs_id = changesets(:public_user_first_change).id
505
506    # simple diff to create a node way and relation using placeholders
507    diff = <<EOF
508<osmChange>
509 <create>
510  <node id='-1' lon='0' lat='0' changeset='#{cs_id}'>
511   <tag k='foo' v='#{"x"*256}'/>
512  </node>
513 </create>
514</osmChange>
515EOF
516
517    # upload it
518    content diff
519    post :upload, :id => cs_id
520    assert_response :bad_request, 
521      "shoudln't be able to upload too long a tag to changeset: #{@response.body}"
522
523  end
524   
525  ##
526  # upload something which creates new objects and inserts them into
527  # existing containers using placeholders.
528  def test_upload_complex
529    basic_authorization users(:public_user).email, "test"
530    cs_id = changesets(:public_user_first_change).id
531
532    # simple diff to create a node way and relation using placeholders
533    diff = <<EOF
534<osmChange>
535 <create>
536  <node id='-1' lon='0' lat='0' changeset='#{cs_id}'>
537   <tag k='foo' v='bar'/>
538   <tag k='baz' v='bat'/>
539  </node>
540 </create>
541 <modify>
542  <way id='1' changeset='#{cs_id}' version='1'>
543   <nd ref='-1'/>
544   <nd ref='3'/>
545  </way>
546  <relation id='1' changeset='#{cs_id}' version='1'>
547   <member type='way' role='some' ref='3'/>
548   <member type='node' role='some' ref='-1'/>
549   <member type='relation' role='some' ref='3'/>
550  </relation>
551 </modify>
552</osmChange>
553EOF
554
555    # upload it
556    content diff
557    post :upload, :id => cs_id
558    assert_response :success, 
559      "can't upload a complex diff to changeset: #{@response.body}"
560
561    # check the returned payload
562    assert_select "diffResult[version=#{API_VERSION}][generator=\"#{GENERATOR}\"]", 1
563    assert_select "diffResult>node", 1
564    assert_select "diffResult>way", 1
565    assert_select "diffResult>relation", 1
566
567    # inspect the response to find out what the new element IDs are
568    doc = XML::Parser.string(@response.body).parse
569    new_node_id = doc.find("//diffResult/node").first["new_id"].to_i
570
571    # check that the changes made it into the database
572    assert_equal 2, Node.find(new_node_id).tags.size, "new node should have two tags"
573    assert_equal [new_node_id, 3], Way.find(1).nds, "way nodes should match"
574    Relation.find(1).members.each do |type,id,role|
575      if type == 'node'
576        assert_equal new_node_id, id, "relation should contain new node"
577      end
578    end
579  end
580   
581  ##
582  # create a diff which references several changesets, which should cause
583  # a rollback and none of the diff gets committed
584  def test_upload_invalid_changesets
585    basic_authorization users(:public_user).email, "test"
586    cs_id = changesets(:public_user_first_change).id
587
588    # simple diff to create a node way and relation using placeholders
589    diff = <<EOF
590<osmChange>
591 <modify>
592  <node id='1' lon='0' lat='0' changeset='#{cs_id}' version='1'/>
593  <way id='1' changeset='#{cs_id}' version='1'>
594   <nd ref='3'/>
595  </way>
596 </modify>
597 <modify>
598  <relation id='1' changeset='#{cs_id}' version='1'>
599   <member type='way' role='some' ref='3'/>
600   <member type='node' role='some' ref='5'/>
601   <member type='relation' role='some' ref='3'/>
602  </relation>
603 </modify>
604 <create>
605  <node id='-1' lon='0' lat='0' changeset='4'>
606   <tag k='foo' v='bar'/>
607   <tag k='baz' v='bat'/>
608  </node>
609 </create>
610</osmChange>
611EOF
612    # cache the objects before uploading them
613    node = current_nodes(:visible_node)
614    way = current_ways(:visible_way)
615    rel = current_relations(:visible_relation)
616
617    # upload it
618    content diff
619    post :upload, :id => cs_id
620    assert_response :conflict, 
621      "uploading a diff with multiple changsets should have failed"
622
623    # check that objects are unmodified
624    assert_nodes_are_equal(node, Node.find(1))
625    assert_ways_are_equal(way, Way.find(1))
626  end
627   
628  ##
629  # upload multiple versions of the same element in the same diff.
630  def test_upload_multiple_valid
631    basic_authorization users(:public_user).email, "test"
632    cs_id = changesets(:public_user_first_change).id
633
634    # change the location of a node multiple times, each time referencing
635    # the last version. doesn't this depend on version numbers being
636    # sequential?
637    diff = <<EOF
638<osmChange>
639 <modify>
640  <node id='1' lon='0' lat='0' changeset='#{cs_id}' version='1'/>
641  <node id='1' lon='1' lat='0' changeset='#{cs_id}' version='2'/>
642  <node id='1' lon='1' lat='1' changeset='#{cs_id}' version='3'/>
643  <node id='1' lon='1' lat='2' changeset='#{cs_id}' version='4'/>
644  <node id='1' lon='2' lat='2' changeset='#{cs_id}' version='5'/>
645  <node id='1' lon='3' lat='2' changeset='#{cs_id}' version='6'/>
646  <node id='1' lon='3' lat='3' changeset='#{cs_id}' version='7'/>
647  <node id='1' lon='9' lat='9' changeset='#{cs_id}' version='8'/>
648 </modify>
649</osmChange>
650EOF
651
652    # upload it
653    content diff
654    post :upload, :id => cs_id
655    assert_response :success, 
656      "can't upload multiple versions of an element in a diff: #{@response.body}"
657   
658    # check the response is well-formed. its counter-intuitive, but the
659    # API will return multiple elements with the same ID and different
660    # version numbers for each change we made.
661    assert_select "diffResult>node", 8
662  end
663
664  ##
665  # upload multiple versions of the same element in the same diff, but
666  # keep the version numbers the same.
667  def test_upload_multiple_duplicate
668    basic_authorization users(:public_user).email, "test"
669    cs_id = changesets(:public_user_first_change).id
670
671    diff = <<EOF
672<osmChange>
673 <modify>
674  <node id='1' lon='0' lat='0' changeset='#{cs_id}' version='1'/>
675  <node id='1' lon='1' lat='1' changeset='#{cs_id}' version='1'/>
676 </modify>
677</osmChange>
678EOF
679
680    # upload it
681    content diff
682    post :upload, :id => cs_id
683    assert_response :conflict, 
684      "shouldn't be able to upload the same element twice in a diff: #{@response.body}"
685  end
686
687  ##
688  # try to upload some elements without specifying the version
689  def test_upload_missing_version
690    basic_authorization users(:public_user).email, "test"
691    cs_id = changesets(:public_user_first_change).id
692
693    diff = <<EOF
694<osmChange>
695 <modify>
696 <node id='1' lon='1' lat='1' changeset='cs_id'/>
697 </modify>
698</osmChange>
699EOF
700
701    # upload it
702    content diff
703    post :upload, :id => cs_id
704    assert_response :bad_request, 
705      "shouldn't be able to upload an element without version: #{@response.body}"
706  end
707 
708  ##
709  # try to upload with commands other than create, modify, or delete
710  def test_action_upload_invalid
711    basic_authorization users(:public_user).email, "test"
712    cs_id = changesets(:public_user_first_change).id
713   
714    diff = <<EOF
715<osmChange>
716  <ping>
717   <node id='1' lon='1' lat='1' changeset='#{cs_id}' />
718  </ping>
719</osmChange>
720EOF
721  content diff
722  post :upload, :id => cs_id
723  assert_response :bad_request, "Shouldn't be able to upload a diff with the action ping"
724  assert_equal @response.body, "Unknown action ping, choices are create, modify, delete"
725  end
726
727  ##
728  # upload a valid changeset which has a mixture of whitespace
729  # to check a bug reported by ivansanchez (#1565).
730  def test_upload_whitespace_valid
731    basic_authorization users(:public_user).email, "test"
732    changeset_id = changesets(:public_user_first_change).id
733
734    diff = <<EOF
735<osmChange>
736 <modify><node id='1' lon='0' lat='0' changeset='#{changeset_id}'
737  version='1'></node>
738  <node id='1' lon='1' lat='1' changeset='#{changeset_id}' version='2'><tag k='k' v='v'/></node></modify>
739 <modify>
740 <relation id='1' changeset='#{changeset_id}' version='1'><member
741   type='way' role='some' ref='3'/><member
742    type='node' role='some' ref='5'/>
743   <member type='relation' role='some' ref='3'/>
744  </relation>
745 </modify></osmChange>
746EOF
747
748    # upload it
749    content diff
750    post :upload, :id => changeset_id
751    assert_response :success, 
752      "can't upload a valid diff with whitespace variations to changeset: #{@response.body}"
753
754    # check the response is well-formed
755    assert_select "diffResult>node", 2
756    assert_select "diffResult>relation", 1
757
758    # check that the changes made it into the database
759    assert_equal 1, Node.find(1).tags.size, "node 1 should now have one tag"
760    assert_equal 0, Relation.find(1).tags.size, "relation 1 should now have no tags"
761  end
762
763  ##
764  # upload a valid changeset which has a mixture of whitespace
765  # to check a bug reported by ivansanchez.
766  def test_upload_reuse_placeholder_valid
767    basic_authorization users(:public_user).email, "test"
768    changeset_id = changesets(:public_user_first_change).id
769
770    diff = <<EOF
771<osmChange>
772 <create>
773  <node id='-1' lon='0' lat='0' changeset='#{changeset_id}'>
774   <tag k="foo" v="bar"/>
775  </node>
776 </create>
777 <modify>
778  <node id='-1' lon='1' lat='1' changeset='#{changeset_id}' version='1'/>
779 </modify>
780 <delete>
781  <node id='-1' lon='2' lat='2' changeset='#{changeset_id}' version='2'/>
782 </delete>
783</osmChange>
784EOF
785
786    # upload it
787    content diff
788    post :upload, :id => changeset_id
789    assert_response :success, 
790      "can't upload a valid diff with re-used placeholders to changeset: #{@response.body}"
791
792    # check the response is well-formed
793    assert_select "diffResult>node", 3
794    assert_select "diffResult>node[old_id=-1]", 3
795  end
796
797  ##
798  # test what happens if a diff upload re-uses placeholder IDs in an
799  # illegal way.
800  def test_upload_placeholder_invalid
801    basic_authorization users(:public_user).email, "test"
802    changeset_id = changesets(:public_user_first_change).id
803
804    diff = <<EOF
805<osmChange>
806 <create>
807  <node id='-1' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
808  <node id='-1' lon='1' lat='1' changeset='#{changeset_id}' version='1'/>
809  <node id='-1' lon='2' lat='2' changeset='#{changeset_id}' version='2'/>
810 </create>
811</osmChange>
812EOF
813
814    # upload it
815    content diff
816    post :upload, :id => changeset_id
817    assert_response :bad_request, 
818      "shouldn't be able to re-use placeholder IDs"
819  end
820
821  ##
822  # test that uploading a way referencing invalid placeholders gives a
823  # proper error, not a 500.
824  def test_upload_placeholder_invalid_way
825    basic_authorization users(:public_user).email, "test"
826    changeset_id = changesets(:public_user_first_change).id
827
828    diff = <<EOF
829<osmChange>
830 <create>
831  <node id="-1" lon="0" lat="0" changeset="#{changeset_id}" version="1"/>
832  <node id="-2" lon="1" lat="1" changeset="#{changeset_id}" version="1"/>
833  <node id="-3" lon="2" lat="2" changeset="#{changeset_id}" version="1"/>
834  <way id="-1" changeset="#{changeset_id}" version="1">
835   <nd ref="-1"/>
836   <nd ref="-2"/>
837   <nd ref="-3"/>
838   <nd ref="-4"/>
839  </way>
840 </create>
841</osmChange>
842EOF
843
844    # upload it
845    content diff
846    post :upload, :id => changeset_id
847    assert_response :bad_request, 
848      "shouldn't be able to use invalid placeholder IDs"
849    assert_equal "Placeholder node not found for reference -4 in way -1", @response.body
850
851    # the same again, but this time use an existing way
852    diff = <<EOF
853<osmChange>
854 <create>
855  <node id="-1" lon="0" lat="0" changeset="#{changeset_id}" version="1"/>
856  <node id="-2" lon="1" lat="1" changeset="#{changeset_id}" version="1"/>
857  <node id="-3" lon="2" lat="2" changeset="#{changeset_id}" version="1"/>
858  <way id="1" changeset="#{changeset_id}" version="1">
859   <nd ref="-1"/>
860   <nd ref="-2"/>
861   <nd ref="-3"/>
862   <nd ref="-4"/>
863  </way>
864 </create>
865</osmChange>
866EOF
867
868    # upload it
869    content diff
870    post :upload, :id => changeset_id
871    assert_response :bad_request, 
872      "shouldn't be able to use invalid placeholder IDs"
873    assert_equal "Placeholder node not found for reference -4 in way 1", @response.body
874  end
875
876  ##
877  # test that uploading a relation referencing invalid placeholders gives a
878  # proper error, not a 500.
879  def test_upload_placeholder_invalid_relation
880    basic_authorization users(:public_user).email, "test"
881    changeset_id = changesets(:public_user_first_change).id
882
883    diff = <<EOF
884<osmChange>
885 <create>
886  <node id="-1" lon="0" lat="0" changeset="#{changeset_id}" version="1"/>
887  <node id="-2" lon="1" lat="1" changeset="#{changeset_id}" version="1"/>
888  <node id="-3" lon="2" lat="2" changeset="#{changeset_id}" version="1"/>
889  <relation id="-1" changeset="#{changeset_id}" version="1">
890   <member type="node" role="foo" ref="-1"/>
891   <member type="node" role="foo" ref="-2"/>
892   <member type="node" role="foo" ref="-3"/>
893   <member type="node" role="foo" ref="-4"/>
894  </relation>
895 </create>
896</osmChange>
897EOF
898
899    # upload it
900    content diff
901    post :upload, :id => changeset_id
902    assert_response :bad_request, 
903      "shouldn't be able to use invalid placeholder IDs"
904    assert_equal "Placeholder Node not found for reference -4 in relation -1.", @response.body
905
906    # the same again, but this time use an existing way
907    diff = <<EOF
908<osmChange>
909 <create>
910  <node id="-1" lon="0" lat="0" changeset="#{changeset_id}" version="1"/>
911  <node id="-2" lon="1" lat="1" changeset="#{changeset_id}" version="1"/>
912  <node id="-3" lon="2" lat="2" changeset="#{changeset_id}" version="1"/>
913  <relation id="1" changeset="#{changeset_id}" version="1">
914   <member type="node" role="foo" ref="-1"/>
915   <member type="node" role="foo" ref="-2"/>
916   <member type="node" role="foo" ref="-3"/>
917   <member type="way" role="bar" ref="-1"/>
918  </relation>
919 </create>
920</osmChange>
921EOF
922
923    # upload it
924    content diff
925    post :upload, :id => changeset_id
926    assert_response :bad_request, 
927      "shouldn't be able to use invalid placeholder IDs"
928    assert_equal "Placeholder Way not found for reference -1 in relation 1.", @response.body
929  end
930
931  ##
932  # test what happens if a diff is uploaded containing only a node
933  # move.
934  def test_upload_node_move
935    basic_authorization users(:public_user).email, "test"
936
937    content "<osm><changeset>" +
938      "<tag k='created_by' v='osm test suite checking changesets'/>" + 
939      "</changeset></osm>"
940    put :create
941    assert_response :success
942    changeset_id = @response.body.to_i
943
944    old_node = current_nodes(:visible_node)
945
946    diff = XML::Document.new
947    diff.root = XML::Node.new "osmChange"
948    modify = XML::Node.new "modify"
949    xml_old_node = old_node.to_xml_node
950    xml_old_node["lat"] = (2.0).to_s
951    xml_old_node["lon"] = (2.0).to_s
952    xml_old_node["changeset"] = changeset_id.to_s
953    modify << xml_old_node
954    diff.root << modify
955
956    # upload it
957    content diff
958    post :upload, :id => changeset_id
959    assert_response :success, 
960      "diff should have uploaded OK"
961
962    # check the bbox
963    changeset = Changeset.find(changeset_id)
964    assert_equal 1*SCALE, changeset.min_lon, "min_lon should be 1 degree"
965    assert_equal 2*SCALE, changeset.max_lon, "max_lon should be 2 degrees"
966    assert_equal 1*SCALE, changeset.min_lat, "min_lat should be 1 degree"
967    assert_equal 2*SCALE, changeset.max_lat, "max_lat should be 2 degrees"
968  end
969
970  ##
971  # test what happens if a diff is uploaded adding a node to a way.
972  def test_upload_way_extend
973    basic_authorization users(:public_user).email, "test"
974
975    content "<osm><changeset>" +
976      "<tag k='created_by' v='osm test suite checking changesets'/>" + 
977      "</changeset></osm>"
978    put :create
979    assert_response :success
980    changeset_id = @response.body.to_i
981
982    old_way = current_ways(:visible_way)
983
984    diff = XML::Document.new
985    diff.root = XML::Node.new "osmChange"
986    modify = XML::Node.new "modify"
987    xml_old_way = old_way.to_xml_node
988    nd_ref = XML::Node.new "nd"
989    nd_ref["ref"] = current_nodes(:visible_node).id.to_s
990    xml_old_way << nd_ref
991    xml_old_way["changeset"] = changeset_id.to_s
992    modify << xml_old_way
993    diff.root << modify
994
995    # upload it
996    content diff
997    post :upload, :id => changeset_id
998    assert_response :success, 
999      "diff should have uploaded OK"
1000
1001    # check the bbox
1002    changeset = Changeset.find(changeset_id)
1003    assert_equal 1*SCALE, changeset.min_lon, "min_lon should be 1 degree"
1004    assert_equal 3*SCALE, changeset.max_lon, "max_lon should be 3 degrees"
1005    assert_equal 1*SCALE, changeset.min_lat, "min_lat should be 1 degree"
1006    assert_equal 3*SCALE, changeset.max_lat, "max_lat should be 3 degrees"
1007  end
1008
1009  ##
1010  # test for more issues in #1568
1011  def test_upload_empty_invalid
1012    basic_authorization users(:public_user).email, "test"
1013
1014    [ "<osmChange/>",
1015      "<osmChange></osmChange>",
1016      "<osmChange><modify/></osmChange>",
1017      "<osmChange><modify></modify></osmChange>"
1018    ].each do |diff|
1019      # upload it
1020      content diff
1021      post :upload, :id => changesets(:public_user_first_change).id
1022      assert_response(:success, "should be able to upload " +
1023                      "empty changeset: " + diff)
1024    end
1025  end
1026
1027  ##
1028  # when we make some simple changes we get the same changes back from the
1029  # diff download.
1030  def test_diff_download_simple
1031    ## First try with the normal user, which should get a forbidden
1032    basic_authorization(users(:normal_user).email, "test")
1033
1034    # create a temporary changeset
1035    content "<osm><changeset>" +
1036      "<tag k='created_by' v='osm test suite checking changesets'/>" + 
1037      "</changeset></osm>"
1038    put :create
1039    assert_response :forbidden
1040   
1041   
1042   
1043    ## Now try with the public user
1044    basic_authorization(users(:public_user).email, "test")
1045
1046    # create a temporary changeset
1047    content "<osm><changeset>" +
1048      "<tag k='created_by' v='osm test suite checking changesets'/>" + 
1049      "</changeset></osm>"
1050    put :create
1051    assert_response :success
1052    changeset_id = @response.body.to_i
1053
1054    # add a diff to it
1055    diff = <<EOF
1056<osmChange>
1057 <modify>
1058  <node id='1' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
1059  <node id='1' lon='1' lat='0' changeset='#{changeset_id}' version='2'/>
1060  <node id='1' lon='1' lat='1' changeset='#{changeset_id}' version='3'/>
1061  <node id='1' lon='1' lat='2' changeset='#{changeset_id}' version='4'/>
1062  <node id='1' lon='2' lat='2' changeset='#{changeset_id}' version='5'/>
1063  <node id='1' lon='3' lat='2' changeset='#{changeset_id}' version='6'/>
1064  <node id='1' lon='3' lat='3' changeset='#{changeset_id}' version='7'/>
1065  <node id='1' lon='9' lat='9' changeset='#{changeset_id}' version='8'/>
1066 </modify>
1067</osmChange>
1068EOF
1069
1070    # upload it
1071    content diff
1072    post :upload, :id => changeset_id
1073    assert_response :success, 
1074      "can't upload multiple versions of an element in a diff: #{@response.body}"
1075   
1076    get :download, :id => changeset_id
1077    assert_response :success
1078
1079    assert_select "osmChange", 1
1080    assert_select "osmChange>modify", 8
1081    assert_select "osmChange>modify>node", 8
1082  end
1083 
1084  ##
1085  # culled this from josm to ensure that nothing in the way that josm
1086  # is formatting the request is causing it to fail.
1087  #
1088  # NOTE: the error turned out to be something else completely!
1089  def test_josm_upload
1090    basic_authorization(users(:public_user).email, "test")
1091
1092    # create a temporary changeset
1093    content "<osm><changeset>" +
1094      "<tag k='created_by' v='osm test suite checking changesets'/>" + 
1095      "</changeset></osm>"
1096    put :create
1097    assert_response :success
1098    changeset_id = @response.body.to_i
1099
1100    diff = <<OSMFILE
1101<osmChange version="0.6" generator="JOSM">
1102<create version="0.6" generator="JOSM">
1103  <node id='-1' visible='true' changeset='#{changeset_id}' lat='51.49619982187321' lon='-0.18722061869438314' />
1104  <node id='-2' visible='true' changeset='#{changeset_id}' lat='51.496359883909605' lon='-0.18653093576241928' />
1105  <node id='-3' visible='true' changeset='#{changeset_id}' lat='51.49598132358285' lon='-0.18719613290981638' />
1106  <node id='-4' visible='true' changeset='#{changeset_id}' lat='51.4961591711078' lon='-0.18629015888084607' />
1107  <node id='-5' visible='true' changeset='#{changeset_id}' lat='51.49582126021711' lon='-0.18708186591517145' />
1108  <node id='-6' visible='true' changeset='#{changeset_id}' lat='51.49591018437858' lon='-0.1861432441734455' />
1109  <node id='-7' visible='true' changeset='#{changeset_id}' lat='51.49560784152179' lon='-0.18694719410005425' />
1110  <node id='-8' visible='true' changeset='#{changeset_id}' lat='51.49567389979617' lon='-0.1860289771788006' />
1111  <node id='-9' visible='true' changeset='#{changeset_id}' lat='51.49543761398892' lon='-0.186820684213126' />
1112  <way id='-10' action='modiy' visible='true' changeset='#{changeset_id}'>
1113    <nd ref='-1' />
1114    <nd ref='-2' />
1115    <nd ref='-3' />
1116    <nd ref='-4' />
1117    <nd ref='-5' />
1118    <nd ref='-6' />
1119    <nd ref='-7' />
1120    <nd ref='-8' />
1121    <nd ref='-9' />
1122    <tag k='highway' v='residential' />
1123    <tag k='name' v='Foobar Street' />
1124  </way>
1125</create>
1126</osmChange>
1127OSMFILE
1128
1129    # upload it
1130    content diff
1131    post :upload, :id => changeset_id
1132    assert_response :success, 
1133      "can't upload a diff from JOSM: #{@response.body}"
1134   
1135    get :download, :id => changeset_id
1136    assert_response :success
1137
1138    assert_select "osmChange", 1
1139    assert_select "osmChange>create>node", 9
1140    assert_select "osmChange>create>way", 1
1141    assert_select "osmChange>create>way>nd", 9
1142    assert_select "osmChange>create>way>tag", 2
1143  end
1144
1145  ##
1146  # when we make some complex changes we get the same changes back from the
1147  # diff download.
1148  def test_diff_download_complex
1149    basic_authorization(users(:public_user).email, "test")
1150
1151    # create a temporary changeset
1152    content "<osm><changeset>" +
1153      "<tag k='created_by' v='osm test suite checking changesets'/>" + 
1154      "</changeset></osm>"
1155    put :create
1156    assert_response :success
1157    changeset_id = @response.body.to_i
1158
1159    # add a diff to it
1160    diff = <<EOF
1161<osmChange>
1162 <delete>
1163  <node id='1' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
1164 </delete>
1165 <create>
1166  <node id='-1' lon='9' lat='9' changeset='#{changeset_id}' version='0'/>
1167  <node id='-2' lon='8' lat='9' changeset='#{changeset_id}' version='0'/>
1168  <node id='-3' lon='7' lat='9' changeset='#{changeset_id}' version='0'/>
1169 </create>
1170 <modify>
1171  <node id='3' lon='20' lat='15' changeset='#{changeset_id}' version='1'/>
1172  <way id='1' changeset='#{changeset_id}' version='1'>
1173   <nd ref='3'/>
1174   <nd ref='-1'/>
1175   <nd ref='-2'/>
1176   <nd ref='-3'/>
1177  </way>
1178 </modify>
1179</osmChange>
1180EOF
1181
1182    # upload it
1183    content diff
1184    post :upload, :id => changeset_id
1185    assert_response :success, 
1186      "can't upload multiple versions of an element in a diff: #{@response.body}"
1187   
1188    get :download, :id => changeset_id
1189    assert_response :success
1190
1191    assert_select "osmChange", 1
1192    assert_select "osmChange>create", 3
1193    assert_select "osmChange>delete", 1
1194    assert_select "osmChange>modify", 2
1195    assert_select "osmChange>create>node", 3
1196    assert_select "osmChange>delete>node", 1 
1197    assert_select "osmChange>modify>node", 1
1198    assert_select "osmChange>modify>way", 1
1199  end
1200
1201  def test_changeset_download
1202    get :download, :id => changesets(:normal_user_first_change).id
1203    assert_response :success
1204    assert_template nil
1205    #print @response.body
1206    # FIXME needs more assert_select tests
1207    assert_select "osmChange[version='#{API_VERSION}'][generator='#{GENERATOR}']" do
1208      assert_select "create", :count => 5
1209      assert_select "create>node[id=#{nodes(:used_node_2).id}][visible=#{nodes(:used_node_2).visible?}][version=#{nodes(:used_node_2).version}]" do
1210        assert_select "tag[k=#{node_tags(:t3).k}][v=#{node_tags(:t3).v}]"
1211      end
1212      assert_select "create>node[id=#{nodes(:visible_node).id}]"
1213    end
1214  end
1215 
1216  ##
1217  # check that the bounding box of a changeset gets updated correctly
1218  ## FIXME: This should really be moded to a integration test due to the with_controller
1219  def test_changeset_bbox
1220    basic_authorization users(:public_user).email, "test"
1221
1222    # create a new changeset
1223    content "<osm><changeset/></osm>"
1224    put :create
1225    assert_response :success, "Creating of changeset failed."
1226    changeset_id = @response.body.to_i
1227   
1228    # add a single node to it
1229    with_controller(NodeController.new) do
1230      content "<osm><node lon='1' lat='2' changeset='#{changeset_id}'/></osm>"
1231      put :create
1232      assert_response :success, "Couldn't create node."
1233    end
1234
1235    # get the bounding box back from the changeset
1236    get :read, :id => changeset_id
1237    assert_response :success, "Couldn't read back changeset."
1238    assert_select "osm>changeset[min_lon=1.0]", 1
1239    assert_select "osm>changeset[max_lon=1.0]", 1
1240    assert_select "osm>changeset[min_lat=2.0]", 1
1241    assert_select "osm>changeset[max_lat=2.0]", 1
1242
1243    # add another node to it
1244    with_controller(NodeController.new) do
1245      content "<osm><node lon='2' lat='1' changeset='#{changeset_id}'/></osm>"
1246      put :create
1247      assert_response :success, "Couldn't create second node."
1248    end
1249
1250    # get the bounding box back from the changeset
1251    get :read, :id => changeset_id
1252    assert_response :success, "Couldn't read back changeset for the second time."
1253    assert_select "osm>changeset[min_lon=1.0]", 1
1254    assert_select "osm>changeset[max_lon=2.0]", 1
1255    assert_select "osm>changeset[min_lat=1.0]", 1
1256    assert_select "osm>changeset[max_lat=2.0]", 1
1257
1258    # add (delete) a way to it, which contains a point at (3,3)
1259    with_controller(WayController.new) do
1260      content update_changeset(current_ways(:visible_way).to_xml,
1261                               changeset_id)
1262      put :delete, :id => current_ways(:visible_way).id
1263      assert_response :success, "Couldn't delete a way."
1264    end
1265
1266    # get the bounding box back from the changeset
1267    get :read, :id => changeset_id
1268    assert_response :success, "Couldn't read back changeset for the third time."
1269    # note that the 3.1 here is because of the bbox overexpansion
1270    assert_select "osm>changeset[min_lon=1.0]", 1
1271    assert_select "osm>changeset[max_lon=3.1]", 1
1272    assert_select "osm>changeset[min_lat=1.0]", 1
1273    assert_select "osm>changeset[max_lat=3.1]", 1   
1274  end
1275
1276  ##
1277  # test that the changeset :include method works as it should
1278  def test_changeset_include
1279    basic_authorization users(:public_user).display_name, "test"
1280
1281    # create a new changeset
1282    content "<osm><changeset/></osm>"
1283    put :create
1284    assert_response :success, "Creating of changeset failed."
1285    changeset_id = @response.body.to_i
1286
1287    # NOTE: the include method doesn't over-expand, like inserting
1288    # a real method does. this is because we expect the client to
1289    # know what it is doing!
1290    check_after_include(changeset_id,  1,  1, [ 1,  1,  1,  1])
1291    check_after_include(changeset_id,  3,  3, [ 1,  1,  3,  3])
1292    check_after_include(changeset_id,  4,  2, [ 1,  1,  4,  3])
1293    check_after_include(changeset_id,  2,  2, [ 1,  1,  4,  3])
1294    check_after_include(changeset_id, -1, -1, [-1, -1,  4,  3])
1295    check_after_include(changeset_id, -2,  5, [-2, -1,  4,  5])
1296  end
1297 
1298  ##
1299  # test that a not found, wrong method with the expand bbox works as expected
1300  def test_changeset_expand_bbox_error
1301    basic_authorization users(:public_user).display_name, "test"
1302   
1303    # create a new changeset
1304    content "<osm><changeset/></osm>"
1305    put :create
1306    assert_response :success, "Creating of changeset failed."
1307    changeset_id = @response.body.to_i
1308   
1309    lon=58.2
1310    lat=-0.45
1311   
1312    # Try and put
1313    content "<osm><node lon='#{lon}' lat='#{lat}'/></osm>"
1314    put :expand_bbox, :id => changeset_id
1315    assert_response :method_not_allowed, "shouldn't be able to put a bbox expand"
1316
1317    # Try to get the update
1318    content "<osm><node lon='#{lon}' lat='#{lat}'/></osm>"
1319    get :expand_bbox, :id => changeset_id
1320    assert_response :method_not_allowed, "shouldn't be able to get a bbox expand"
1321   
1322    # Try to use a hopefully missing changeset
1323    content "<osm><node lon='#{lon}' lat='#{lat}'/></osm>"
1324    post :expand_bbox, :id => changeset_id+13245
1325    assert_response :not_found, "shouldn't be able to do a bbox expand on a nonexistant changeset"
1326
1327  end
1328
1329  ##
1330  # test the query functionality of changesets
1331  def test_query
1332    get :query, :bbox => "-10,-10, 10, 10"
1333    assert_response :success, "can't get changesets in bbox"
1334    assert_changesets [1,4,6]
1335
1336    get :query, :bbox => "4.5,4.5,4.6,4.6"
1337    assert_response :success, "can't get changesets in bbox"
1338    assert_changesets [1]
1339
1340    # can't get changesets of user 1 without authenticating
1341    get :query, :user => users(:normal_user).id
1342    assert_response :not_found, "shouldn't be able to get changesets by non-public user"
1343
1344    # but this should work
1345    basic_authorization "test@openstreetmap.org", "test"
1346    get :query, :user => users(:normal_user).id
1347    assert_response :success, "can't get changesets by user"
1348    assert_changesets [1,3,6]
1349
1350    get :query, :user => users(:normal_user).id, :open => true
1351    assert_response :success, "can't get changesets by user and open"
1352    assert_changesets [1]
1353
1354    get :query, :time => '2007-12-31'
1355    assert_response :success, "can't get changesets by time-since"
1356    assert_changesets [1,2,4,5,6]
1357
1358    get :query, :time => '2008-01-01T12:34Z'
1359    assert_response :success, "can't get changesets by time-since with hour"
1360    assert_changesets [1,2,4,5,6]
1361
1362    get :query, :time => '2007-12-31T23:59Z,2008-01-01T00:01Z'
1363    assert_response :success, "can't get changesets by time-range"
1364    assert_changesets [1,5,6]
1365
1366    get :query, :open => 'true'
1367    assert_response :success, "can't get changesets by open-ness"
1368    assert_changesets [1,2,4]
1369  end
1370
1371  ##
1372  # check that errors are returned if garbage is inserted
1373  # into query strings
1374  def test_query_invalid
1375    [ "abracadabra!",
1376      "1,2,3,F",
1377      ";drop table users;"
1378      ].each do |bbox|
1379      get :query, :bbox => bbox
1380      assert_response :bad_request, "'#{bbox}' isn't a bbox"
1381    end
1382
1383    [ "now()",
1384      "00-00-00",
1385      ";drop table users;",
1386      ",",
1387      "-,-"
1388      ].each do |time|
1389      get :query, :time => time
1390      assert_response :bad_request, "'#{time}' isn't a valid time range"
1391    end
1392
1393    [ "me",
1394      "foobar",
1395      "-1",
1396      "0"
1397      ].each do |uid|
1398      get :query, :user => uid
1399      assert_response :bad_request, "'#{uid}' isn't a valid user ID"
1400    end
1401  end
1402
1403  ##
1404  # check updating tags on a changeset
1405  def test_changeset_update
1406    ## First try with the non-public user
1407    changeset = changesets(:normal_user_first_change)
1408    new_changeset = changeset.to_xml
1409    new_tag = XML::Node.new "tag"
1410    new_tag['k'] = "tagtesting"
1411    new_tag['v'] = "valuetesting"
1412    new_changeset.find("//osm/changeset").first << new_tag
1413    content new_changeset
1414
1415    # try without any authorization
1416    put :update, :id => changeset.id
1417    assert_response :unauthorized
1418
1419    # try with the wrong authorization
1420    basic_authorization users(:public_user).email, "test"
1421    put :update, :id => changeset.id
1422    assert_response :conflict
1423
1424    # now this should get an unauthorized
1425    basic_authorization users(:normal_user).email, "test"
1426    put :update, :id => changeset.id
1427    assert_require_public_data "user with their data non-public, shouldn't be able to edit their changeset"
1428   
1429   
1430    ## Now try with the public user
1431    changeset = changesets(:public_user_first_change)
1432    new_changeset = changeset.to_xml
1433    new_tag = XML::Node.new "tag"
1434    new_tag['k'] = "tagtesting"
1435    new_tag['v'] = "valuetesting"
1436    new_changeset.find("//osm/changeset").first << new_tag
1437    content new_changeset
1438   
1439    # try without any authorization
1440    @request.env["HTTP_AUTHORIZATION"] = nil
1441    put :update, :id => changeset.id
1442    assert_response :unauthorized
1443
1444    # try with the wrong authorization
1445    basic_authorization users(:second_public_user).email, "test"
1446    put :update, :id => changeset.id
1447    assert_response :conflict
1448
1449    # now this should work...
1450    basic_authorization users(:public_user).email, "test"
1451    put :update, :id => changeset.id
1452    assert_response :success
1453
1454    assert_select "osm>changeset[id=#{changeset.id}]", 1
1455    assert_select "osm>changeset>tag", 2
1456    assert_select "osm>changeset>tag[k=tagtesting][v=valuetesting]", 1
1457  end
1458 
1459  ##
1460  # check that a user different from the one who opened the changeset
1461  # can't modify it.
1462  def test_changeset_update_invalid
1463    basic_authorization users(:public_user).email, "test"
1464
1465    changeset = changesets(:normal_user_first_change)
1466    new_changeset = changeset.to_xml
1467    new_tag = XML::Node.new "tag"
1468    new_tag['k'] = "testing"
1469    new_tag['v'] = "testing"
1470    new_changeset.find("//osm/changeset").first << new_tag
1471
1472    content new_changeset
1473    put :update, :id => changeset.id
1474    assert_response :conflict
1475  end
1476
1477  ##
1478  # check that a changeset can contain a certain max number of changes.
1479  ## FIXME should be changed to an integration test due to the with_controller
1480  def test_changeset_limits
1481    basic_authorization users(:public_user).email, "test"
1482
1483    # open a new changeset
1484    content "<osm><changeset/></osm>"
1485    put :create
1486    assert_response :success, "can't create a new changeset"
1487    cs_id = @response.body.to_i
1488
1489    # start the counter just short of where the changeset should finish.
1490    offset = 10
1491    # alter the database to set the counter on the changeset directly,
1492    # otherwise it takes about 6 minutes to fill all of them.
1493    changeset = Changeset.find(cs_id)
1494    changeset.num_changes = Changeset::MAX_ELEMENTS - offset
1495    changeset.save!
1496
1497    with_controller(NodeController.new) do
1498      # create a new node
1499      content "<osm><node changeset='#{cs_id}' lat='0.0' lon='0.0'/></osm>"
1500      put :create
1501      assert_response :success, "can't create a new node"
1502      node_id = @response.body.to_i
1503
1504      get :read, :id => node_id
1505      assert_response :success, "can't read back new node"
1506      node_doc = XML::Parser.string(@response.body).parse
1507      node_xml = node_doc.find("//osm/node").first
1508
1509      # loop until we fill the changeset with nodes
1510      offset.times do |i|
1511        node_xml['lat'] = rand.to_s
1512        node_xml['lon'] = rand.to_s
1513        node_xml['version'] = (i+1).to_s
1514
1515        content node_doc
1516        put :update, :id => node_id
1517        assert_response :success, "attempt #{i} should have succeeded"
1518      end
1519
1520      # trying again should fail
1521      node_xml['lat'] = rand.to_s
1522      node_xml['lon'] = rand.to_s
1523      node_xml['version'] = offset.to_s
1524     
1525      content node_doc
1526      put :update, :id => node_id
1527      assert_response :conflict, "final attempt should have failed"
1528    end
1529
1530    changeset = Changeset.find(cs_id)
1531    assert_equal Changeset::MAX_ELEMENTS + 1, changeset.num_changes
1532
1533    # check that the changeset is now closed as well
1534    assert(!changeset.is_open?, 
1535           "changeset should have been auto-closed by exceeding " + 
1536           "element limit.")
1537  end
1538 
1539  ##
1540  # This should display the last 20 changesets closed.
1541  def test_list
1542    changesets = Changeset.find(:all, :order => "created_at DESC", :conditions => ['min_lat IS NOT NULL'], :limit=> 20)
1543    assert changesets.size <= 20
1544    get :list, {:format => "html"}
1545    assert_response :success
1546    assert_template "list"
1547    # Now check that all 20 (or however many were returned) changesets are in the html
1548    assert_select "h1", :text => "Changesets", :count => 1
1549    assert_select "table[id='changeset_list'] tr", :count => changesets.size + 1
1550    changesets.each do |changeset|
1551      # FIXME this test needs rewriting - test for table contents
1552    end
1553  end
1554 
1555  ##
1556  # Checks the display of the user changesets listing
1557  def test_list_user
1558    user = users(:public_user)
1559    get :list, {:format => "html", :display_name => user.display_name}
1560    assert_response :success
1561    assert_template "changeset/_user"
1562    ## FIXME need to add more checks to see which if edits are actually shown if your data is public
1563  end
1564 
1565  ##
1566  # Check the not found of the list user changesets
1567  def test_list_user_not_found
1568    get :list, {:format => "html", :display_name => "Some random user"}
1569    assert_response :not_found
1570    assert_template 'user/no_such_user'
1571  end
1572 
1573  #------------------------------------------------------------
1574  # utility functions
1575  #------------------------------------------------------------
1576
1577  ##
1578  # boilerplate for checking that certain changesets exist in the
1579  # output.
1580  def assert_changesets(ids)
1581    assert_select "osm>changeset", ids.size
1582    ids.each do |id|
1583      assert_select "osm>changeset[id=#{id}]", 1
1584    end
1585  end
1586
1587  ##
1588  # call the include method and assert properties of the bbox
1589  def check_after_include(changeset_id, lon, lat, bbox)
1590    content "<osm><node lon='#{lon}' lat='#{lat}'/></osm>"
1591    post :expand_bbox, :id => changeset_id
1592    assert_response :success, "Setting include of changeset failed: #{@response.body}"
1593
1594    # check exactly one changeset
1595    assert_select "osm>changeset", 1
1596    assert_select "osm>changeset[id=#{changeset_id}]", 1
1597
1598    # check the bbox
1599    doc = XML::Parser.string(@response.body).parse
1600    changeset = doc.find("//osm/changeset").first
1601    assert_equal bbox[0], changeset['min_lon'].to_f, "min lon"
1602    assert_equal bbox[1], changeset['min_lat'].to_f, "min lat"
1603    assert_equal bbox[2], changeset['max_lon'].to_f, "max lon"
1604    assert_equal bbox[3], changeset['max_lat'].to_f, "max lat"
1605  end
1606
1607  ##
1608  # update the changeset_id of a way element
1609  def update_changeset(xml, changeset_id)
1610    xml_attr_rewrite(xml, 'changeset', changeset_id)
1611  end
1612
1613  ##
1614  # update an attribute in a way element
1615  def xml_attr_rewrite(xml, name, value)
1616    xml.find("//osm/way").first[name] = value.to_s
1617    return xml
1618  end
1619
1620end
Note: See TracBrowser for help on using the repository browser.