]> git.openstreetmap.org Git - rails.git/blob - test/functional/changeset_controller_test.rb
Renumbered sections to be more consistent with subsection numbering style
[rails.git] / test / functional / changeset_controller_test.rb
1 require File.dirname(__FILE__) + '/../test_helper'
2 require 'changeset_controller'
3
4 class 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>
202 EOF
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>
234 EOF
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>
266 EOF
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>
306 EOF
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>
456 EOF
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   # test that a conditional delete of an in use object works.
502   def test_upload_delete_if_unused
503     basic_authorization users(:public_user).email, "test"
504
505     diff = XML::Document.new
506     diff.root = XML::Node.new "osmChange"
507     delete = XML::Node.new "delete"
508     diff.root << delete
509     delete["if-unused"] = ""
510     delete << current_relations(:public_used_relation).to_xml_node
511     delete << current_ways(:used_way).to_xml_node
512     delete << current_nodes(:node_used_by_relationship).to_xml_node
513
514     # upload it
515     content diff
516     post :upload, :id => 2
517     assert_response :success, 
518       "can't do a conditional delete of in use objects: #{@response.body}"
519
520     # check the returned payload
521     assert_select "diffResult[version=#{API_VERSION}][generator=\"OpenStreetMap server\"]", 1
522     assert_select "diffResult>node", 1
523     assert_select "diffresult>way", 1
524     assert_select "diffResult>relation", 1
525
526     # parse the response
527     doc = XML::Parser.string(@response.body).parse
528
529     # check the old IDs are all present and what we expect
530     assert_equal current_nodes(:node_used_by_relationship).id, doc.find("//diffResult/node").first["old_id"].to_i
531     assert_equal current_ways(:used_way).id, doc.find("//diffResult/way").first["old_id"].to_i
532     assert_equal current_relations(:public_used_relation).id, doc.find("//diffResult/relation").first["old_id"].to_i
533
534     # check the new IDs are all present and unchanged
535     assert_equal current_nodes(:node_used_by_relationship).id, doc.find("//diffResult/node").first["new_id"].to_i
536     assert_equal current_ways(:used_way).id, doc.find("//diffResult/way").first["new_id"].to_i
537     assert_equal current_relations(:public_used_relation).id, doc.find("//diffResult/relation").first["new_id"].to_i
538
539     # check the new versions are all present and unchanged
540     assert_equal current_nodes(:node_used_by_relationship).version, doc.find("//diffResult/node").first["new_version"].to_i
541     assert_equal current_ways(:used_way).version, doc.find("//diffResult/way").first["new_version"].to_i
542     assert_equal current_relations(:public_used_relation).version, doc.find("//diffResult/relation").first["new_version"].to_i
543
544     # check that nothing was, in fact, deleted
545     assert_equal true, Node.find(current_nodes(:node_used_by_relationship).id).visible
546     assert_equal true, Way.find(current_ways(:used_way).id).visible
547     assert_equal true, Relation.find(current_relations(:public_used_relation).id).visible
548   end
549
550   ##
551   # upload an element with a really long tag value
552   def test_upload_invalid_too_long_tag
553     basic_authorization users(:public_user).email, "test"
554     cs_id = changesets(:public_user_first_change).id
555
556     # simple diff to create a node way and relation using placeholders
557     diff = <<EOF
558 <osmChange>
559  <create>
560   <node id='-1' lon='0' lat='0' changeset='#{cs_id}'>
561    <tag k='foo' v='#{"x"*256}'/>
562   </node>
563  </create>
564 </osmChange>
565 EOF
566
567     # upload it
568     content diff
569     post :upload, :id => cs_id
570     assert_response :bad_request, 
571       "shoudln't be able to upload too long a tag to changeset: #{@response.body}"
572
573   end
574     
575   ##
576   # upload something which creates new objects and inserts them into
577   # existing containers using placeholders.
578   def test_upload_complex
579     basic_authorization users(:public_user).email, "test"
580     cs_id = changesets(:public_user_first_change).id
581
582     # simple diff to create a node way and relation using placeholders
583     diff = <<EOF
584 <osmChange>
585  <create>
586   <node id='-1' lon='0' lat='0' changeset='#{cs_id}'>
587    <tag k='foo' v='bar'/>
588    <tag k='baz' v='bat'/>
589   </node>
590  </create>
591  <modify>
592   <way id='1' changeset='#{cs_id}' version='1'>
593    <nd ref='-1'/>
594    <nd ref='3'/>
595   </way>
596   <relation id='1' changeset='#{cs_id}' version='1'>
597    <member type='way' role='some' ref='3'/>
598    <member type='node' role='some' ref='-1'/>
599    <member type='relation' role='some' ref='3'/>
600   </relation>
601  </modify>
602 </osmChange>
603 EOF
604
605     # upload it
606     content diff
607     post :upload, :id => cs_id
608     assert_response :success, 
609       "can't upload a complex diff to changeset: #{@response.body}"
610
611     # check the returned payload
612     assert_select "diffResult[version=#{API_VERSION}][generator=\"#{GENERATOR}\"]", 1
613     assert_select "diffResult>node", 1
614     assert_select "diffResult>way", 1
615     assert_select "diffResult>relation", 1
616
617     # inspect the response to find out what the new element IDs are
618     doc = XML::Parser.string(@response.body).parse
619     new_node_id = doc.find("//diffResult/node").first["new_id"].to_i
620
621     # check that the changes made it into the database
622     assert_equal 2, Node.find(new_node_id).tags.size, "new node should have two tags"
623     assert_equal [new_node_id, 3], Way.find(1).nds, "way nodes should match"
624     Relation.find(1).members.each do |type,id,role|
625       if type == 'node'
626         assert_equal new_node_id, id, "relation should contain new node"
627       end
628     end
629   end
630     
631   ##
632   # create a diff which references several changesets, which should cause
633   # a rollback and none of the diff gets committed
634   def test_upload_invalid_changesets
635     basic_authorization users(:public_user).email, "test"
636     cs_id = changesets(:public_user_first_change).id
637
638     # simple diff to create a node way and relation using placeholders
639     diff = <<EOF
640 <osmChange>
641  <modify>
642   <node id='1' lon='0' lat='0' changeset='#{cs_id}' version='1'/>
643   <way id='1' changeset='#{cs_id}' version='1'>
644    <nd ref='3'/>
645   </way>
646  </modify>
647  <modify>
648   <relation id='1' changeset='#{cs_id}' version='1'>
649    <member type='way' role='some' ref='3'/>
650    <member type='node' role='some' ref='5'/>
651    <member type='relation' role='some' ref='3'/>
652   </relation>
653  </modify>
654  <create>
655   <node id='-1' lon='0' lat='0' changeset='4'>
656    <tag k='foo' v='bar'/>
657    <tag k='baz' v='bat'/>
658   </node>
659  </create>
660 </osmChange>
661 EOF
662     # cache the objects before uploading them
663     node = current_nodes(:visible_node)
664     way = current_ways(:visible_way)
665     rel = current_relations(:visible_relation)
666
667     # upload it
668     content diff
669     post :upload, :id => cs_id
670     assert_response :conflict, 
671       "uploading a diff with multiple changsets should have failed"
672
673     # check that objects are unmodified
674     assert_nodes_are_equal(node, Node.find(1))
675     assert_ways_are_equal(way, Way.find(1))
676   end
677     
678   ##
679   # upload multiple versions of the same element in the same diff.
680   def test_upload_multiple_valid
681     basic_authorization users(:public_user).email, "test"
682     cs_id = changesets(:public_user_first_change).id
683
684     # change the location of a node multiple times, each time referencing
685     # the last version. doesn't this depend on version numbers being
686     # sequential?
687     diff = <<EOF
688 <osmChange>
689  <modify>
690   <node id='1' lon='0' lat='0' changeset='#{cs_id}' version='1'/>
691   <node id='1' lon='1' lat='0' changeset='#{cs_id}' version='2'/>
692   <node id='1' lon='1' lat='1' changeset='#{cs_id}' version='3'/>
693   <node id='1' lon='1' lat='2' changeset='#{cs_id}' version='4'/>
694   <node id='1' lon='2' lat='2' changeset='#{cs_id}' version='5'/>
695   <node id='1' lon='3' lat='2' changeset='#{cs_id}' version='6'/>
696   <node id='1' lon='3' lat='3' changeset='#{cs_id}' version='7'/>
697   <node id='1' lon='9' lat='9' changeset='#{cs_id}' version='8'/>
698  </modify>
699 </osmChange>
700 EOF
701
702     # upload it
703     content diff
704     post :upload, :id => cs_id
705     assert_response :success, 
706       "can't upload multiple versions of an element in a diff: #{@response.body}"
707     
708     # check the response is well-formed. its counter-intuitive, but the
709     # API will return multiple elements with the same ID and different
710     # version numbers for each change we made.
711     assert_select "diffResult>node", 8
712   end
713
714   ##
715   # upload multiple versions of the same element in the same diff, but
716   # keep the version numbers the same.
717   def test_upload_multiple_duplicate
718     basic_authorization users(:public_user).email, "test"
719     cs_id = changesets(:public_user_first_change).id
720
721     diff = <<EOF
722 <osmChange>
723  <modify>
724   <node id='1' lon='0' lat='0' changeset='#{cs_id}' version='1'/>
725   <node id='1' lon='1' lat='1' changeset='#{cs_id}' version='1'/>
726  </modify>
727 </osmChange>
728 EOF
729
730     # upload it
731     content diff
732     post :upload, :id => cs_id
733     assert_response :conflict, 
734       "shouldn't be able to upload the same element twice in a diff: #{@response.body}"
735   end
736
737   ##
738   # try to upload some elements without specifying the version
739   def test_upload_missing_version
740     basic_authorization users(:public_user).email, "test"
741     cs_id = changesets(:public_user_first_change).id
742
743     diff = <<EOF
744 <osmChange>
745  <modify>
746  <node id='1' lon='1' lat='1' changeset='cs_id'/>
747  </modify>
748 </osmChange>
749 EOF
750
751     # upload it
752     content diff
753     post :upload, :id => cs_id
754     assert_response :bad_request, 
755       "shouldn't be able to upload an element without version: #{@response.body}"
756   end
757   
758   ##
759   # try to upload with commands other than create, modify, or delete
760   def test_action_upload_invalid
761     basic_authorization users(:public_user).email, "test"
762     cs_id = changesets(:public_user_first_change).id
763     
764     diff = <<EOF
765 <osmChange>
766   <ping>
767    <node id='1' lon='1' lat='1' changeset='#{cs_id}' />
768   </ping>
769 </osmChange>
770 EOF
771   content diff
772   post :upload, :id => cs_id
773   assert_response :bad_request, "Shouldn't be able to upload a diff with the action ping"
774   assert_equal @response.body, "Unknown action ping, choices are create, modify, delete"
775   end
776
777   ##
778   # upload a valid changeset which has a mixture of whitespace
779   # to check a bug reported by ivansanchez (#1565).
780   def test_upload_whitespace_valid
781     basic_authorization users(:public_user).email, "test"
782     changeset_id = changesets(:public_user_first_change).id
783
784     diff = <<EOF
785 <osmChange>
786  <modify><node id='1' lon='0' lat='0' changeset='#{changeset_id}' 
787   version='1'></node>
788   <node id='1' lon='1' lat='1' changeset='#{changeset_id}' version='2'><tag k='k' v='v'/></node></modify>
789  <modify>
790  <relation id='1' changeset='#{changeset_id}' version='1'><member 
791    type='way' role='some' ref='3'/><member 
792     type='node' role='some' ref='5'/>
793    <member type='relation' role='some' ref='3'/>
794   </relation>
795  </modify></osmChange>
796 EOF
797
798     # upload it
799     content diff
800     post :upload, :id => changeset_id
801     assert_response :success, 
802       "can't upload a valid diff with whitespace variations to changeset: #{@response.body}"
803
804     # check the response is well-formed
805     assert_select "diffResult>node", 2
806     assert_select "diffResult>relation", 1
807
808     # check that the changes made it into the database
809     assert_equal 1, Node.find(1).tags.size, "node 1 should now have one tag"
810     assert_equal 0, Relation.find(1).tags.size, "relation 1 should now have no tags"
811   end
812
813   ##
814   # upload a valid changeset which has a mixture of whitespace
815   # to check a bug reported by ivansanchez.
816   def test_upload_reuse_placeholder_valid
817     basic_authorization users(:public_user).email, "test"
818     changeset_id = changesets(:public_user_first_change).id
819
820     diff = <<EOF
821 <osmChange>
822  <create>
823   <node id='-1' lon='0' lat='0' changeset='#{changeset_id}'>
824    <tag k="foo" v="bar"/>
825   </node>
826  </create>
827  <modify>
828   <node id='-1' lon='1' lat='1' changeset='#{changeset_id}' version='1'/>
829  </modify>
830  <delete>
831   <node id='-1' lon='2' lat='2' changeset='#{changeset_id}' version='2'/>
832  </delete>
833 </osmChange>
834 EOF
835
836     # upload it
837     content diff
838     post :upload, :id => changeset_id
839     assert_response :success, 
840       "can't upload a valid diff with re-used placeholders to changeset: #{@response.body}"
841
842     # check the response is well-formed
843     assert_select "diffResult>node", 3
844     assert_select "diffResult>node[old_id=-1]", 3
845   end
846
847   ##
848   # test what happens if a diff upload re-uses placeholder IDs in an
849   # illegal way.
850   def test_upload_placeholder_invalid
851     basic_authorization users(:public_user).email, "test"
852     changeset_id = changesets(:public_user_first_change).id
853
854     diff = <<EOF
855 <osmChange>
856  <create>
857   <node id='-1' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
858   <node id='-1' lon='1' lat='1' changeset='#{changeset_id}' version='1'/>
859   <node id='-1' lon='2' lat='2' changeset='#{changeset_id}' version='2'/>
860  </create>
861 </osmChange>
862 EOF
863
864     # upload it
865     content diff
866     post :upload, :id => changeset_id
867     assert_response :bad_request, 
868       "shouldn't be able to re-use placeholder IDs"
869   end
870
871   ##
872   # test that uploading a way referencing invalid placeholders gives a 
873   # proper error, not a 500.
874   def test_upload_placeholder_invalid_way
875     basic_authorization users(:public_user).email, "test"
876     changeset_id = changesets(:public_user_first_change).id
877
878     diff = <<EOF
879 <osmChange>
880  <create>
881   <node id="-1" lon="0" lat="0" changeset="#{changeset_id}" version="1"/>
882   <node id="-2" lon="1" lat="1" changeset="#{changeset_id}" version="1"/>
883   <node id="-3" lon="2" lat="2" changeset="#{changeset_id}" version="1"/>
884   <way id="-1" changeset="#{changeset_id}" version="1">
885    <nd ref="-1"/>
886    <nd ref="-2"/>
887    <nd ref="-3"/>
888    <nd ref="-4"/>
889   </way>
890  </create>
891 </osmChange>
892 EOF
893
894     # upload it
895     content diff
896     post :upload, :id => changeset_id
897     assert_response :bad_request, 
898       "shouldn't be able to use invalid placeholder IDs"
899     assert_equal "Placeholder node not found for reference -4 in way -1", @response.body
900
901     # the same again, but this time use an existing way
902     diff = <<EOF
903 <osmChange>
904  <create>
905   <node id="-1" lon="0" lat="0" changeset="#{changeset_id}" version="1"/>
906   <node id="-2" lon="1" lat="1" changeset="#{changeset_id}" version="1"/>
907   <node id="-3" lon="2" lat="2" changeset="#{changeset_id}" version="1"/>
908   <way id="1" changeset="#{changeset_id}" version="1">
909    <nd ref="-1"/>
910    <nd ref="-2"/>
911    <nd ref="-3"/>
912    <nd ref="-4"/>
913   </way>
914  </create>
915 </osmChange>
916 EOF
917
918     # upload it
919     content diff
920     post :upload, :id => changeset_id
921     assert_response :bad_request, 
922       "shouldn't be able to use invalid placeholder IDs"
923     assert_equal "Placeholder node not found for reference -4 in way 1", @response.body
924   end
925
926   ##
927   # test that uploading a relation referencing invalid placeholders gives a 
928   # proper error, not a 500.
929   def test_upload_placeholder_invalid_relation
930     basic_authorization users(:public_user).email, "test"
931     changeset_id = changesets(:public_user_first_change).id
932
933     diff = <<EOF
934 <osmChange>
935  <create>
936   <node id="-1" lon="0" lat="0" changeset="#{changeset_id}" version="1"/>
937   <node id="-2" lon="1" lat="1" changeset="#{changeset_id}" version="1"/>
938   <node id="-3" lon="2" lat="2" changeset="#{changeset_id}" version="1"/>
939   <relation id="-1" changeset="#{changeset_id}" version="1">
940    <member type="node" role="foo" ref="-1"/>
941    <member type="node" role="foo" ref="-2"/>
942    <member type="node" role="foo" ref="-3"/>
943    <member type="node" role="foo" ref="-4"/>
944   </relation>
945  </create>
946 </osmChange>
947 EOF
948
949     # upload it
950     content diff
951     post :upload, :id => changeset_id
952     assert_response :bad_request, 
953       "shouldn't be able to use invalid placeholder IDs"
954     assert_equal "Placeholder Node not found for reference -4 in relation -1.", @response.body
955
956     # the same again, but this time use an existing way
957     diff = <<EOF
958 <osmChange>
959  <create>
960   <node id="-1" lon="0" lat="0" changeset="#{changeset_id}" version="1"/>
961   <node id="-2" lon="1" lat="1" changeset="#{changeset_id}" version="1"/>
962   <node id="-3" lon="2" lat="2" changeset="#{changeset_id}" version="1"/>
963   <relation id="1" changeset="#{changeset_id}" version="1">
964    <member type="node" role="foo" ref="-1"/>
965    <member type="node" role="foo" ref="-2"/>
966    <member type="node" role="foo" ref="-3"/>
967    <member type="way" role="bar" ref="-1"/>
968   </relation>
969  </create>
970 </osmChange>
971 EOF
972
973     # upload it
974     content diff
975     post :upload, :id => changeset_id
976     assert_response :bad_request, 
977       "shouldn't be able to use invalid placeholder IDs"
978     assert_equal "Placeholder Way not found for reference -1 in relation 1.", @response.body
979   end
980
981   ##
982   # test what happens if a diff is uploaded containing only a node
983   # move.
984   def test_upload_node_move
985     basic_authorization users(:public_user).email, "test"
986
987     content "<osm><changeset>" +
988       "<tag k='created_by' v='osm test suite checking changesets'/>" + 
989       "</changeset></osm>"
990     put :create
991     assert_response :success
992     changeset_id = @response.body.to_i
993
994     old_node = current_nodes(:visible_node)
995
996     diff = XML::Document.new
997     diff.root = XML::Node.new "osmChange"
998     modify = XML::Node.new "modify"
999     xml_old_node = old_node.to_xml_node
1000     xml_old_node["lat"] = (2.0).to_s
1001     xml_old_node["lon"] = (2.0).to_s
1002     xml_old_node["changeset"] = changeset_id.to_s
1003     modify << xml_old_node
1004     diff.root << modify
1005
1006     # upload it
1007     content diff
1008     post :upload, :id => changeset_id
1009     assert_response :success, 
1010       "diff should have uploaded OK"
1011
1012     # check the bbox
1013     changeset = Changeset.find(changeset_id)
1014     assert_equal 1*SCALE, changeset.min_lon, "min_lon should be 1 degree"
1015     assert_equal 2*SCALE, changeset.max_lon, "max_lon should be 2 degrees"
1016     assert_equal 1*SCALE, changeset.min_lat, "min_lat should be 1 degree"
1017     assert_equal 2*SCALE, changeset.max_lat, "max_lat should be 2 degrees"
1018   end
1019
1020   ##
1021   # test what happens if a diff is uploaded adding a node to a way.
1022   def test_upload_way_extend
1023     basic_authorization users(:public_user).email, "test"
1024
1025     content "<osm><changeset>" +
1026       "<tag k='created_by' v='osm test suite checking changesets'/>" + 
1027       "</changeset></osm>"
1028     put :create
1029     assert_response :success
1030     changeset_id = @response.body.to_i
1031
1032     old_way = current_ways(:visible_way)
1033
1034     diff = XML::Document.new
1035     diff.root = XML::Node.new "osmChange"
1036     modify = XML::Node.new "modify"
1037     xml_old_way = old_way.to_xml_node
1038     nd_ref = XML::Node.new "nd"
1039     nd_ref["ref"] = current_nodes(:visible_node).id.to_s
1040     xml_old_way << nd_ref
1041     xml_old_way["changeset"] = changeset_id.to_s
1042     modify << xml_old_way
1043     diff.root << modify
1044
1045     # upload it
1046     content diff
1047     post :upload, :id => changeset_id
1048     assert_response :success, 
1049       "diff should have uploaded OK"
1050
1051     # check the bbox
1052     changeset = Changeset.find(changeset_id)
1053     assert_equal 1*SCALE, changeset.min_lon, "min_lon should be 1 degree"
1054     assert_equal 3*SCALE, changeset.max_lon, "max_lon should be 3 degrees"
1055     assert_equal 1*SCALE, changeset.min_lat, "min_lat should be 1 degree"
1056     assert_equal 3*SCALE, changeset.max_lat, "max_lat should be 3 degrees"
1057   end
1058
1059   ##
1060   # test for more issues in #1568
1061   def test_upload_empty_invalid
1062     basic_authorization users(:public_user).email, "test"
1063
1064     [ "<osmChange/>",
1065       "<osmChange></osmChange>",
1066       "<osmChange><modify/></osmChange>",
1067       "<osmChange><modify></modify></osmChange>"
1068     ].each do |diff|
1069       # upload it
1070       content diff
1071       post :upload, :id => changesets(:public_user_first_change).id
1072       assert_response(:success, "should be able to upload " +
1073                       "empty changeset: " + diff)
1074     end
1075   end
1076
1077   ##
1078   # test that the X-Error-Format header works to request XML errors
1079   def test_upload_xml_errors
1080     basic_authorization users(:public_user).email, "test"
1081
1082     # try and delete a node that is in use
1083     diff = XML::Document.new
1084     diff.root = XML::Node.new "osmChange"
1085     delete = XML::Node.new "delete"
1086     diff.root << delete
1087     delete << current_nodes(:node_used_by_relationship).to_xml_node
1088
1089     # upload it
1090     content diff
1091     error_format "xml"
1092     post :upload, :id => 2
1093     assert_response :success, 
1094       "failed to return error in XML format"
1095
1096     # check the returned payload
1097     assert_select "osmError[version=#{API_VERSION}][generator=\"OpenStreetMap server\"]", 1
1098     assert_select "osmError>status", 1
1099     assert_select "osmError>message", 1
1100
1101   end
1102
1103   ##
1104   # when we make some simple changes we get the same changes back from the 
1105   # diff download.
1106   def test_diff_download_simple
1107     ## First try with the normal user, which should get a forbidden
1108     basic_authorization(users(:normal_user).email, "test")
1109
1110     # create a temporary changeset
1111     content "<osm><changeset>" +
1112       "<tag k='created_by' v='osm test suite checking changesets'/>" + 
1113       "</changeset></osm>"
1114     put :create
1115     assert_response :forbidden
1116     
1117     
1118     
1119     ## Now try with the public user
1120     basic_authorization(users(:public_user).email, "test")
1121
1122     # create a temporary changeset
1123     content "<osm><changeset>" +
1124       "<tag k='created_by' v='osm test suite checking changesets'/>" + 
1125       "</changeset></osm>"
1126     put :create
1127     assert_response :success
1128     changeset_id = @response.body.to_i
1129
1130     # add a diff to it
1131     diff = <<EOF
1132 <osmChange>
1133  <modify>
1134   <node id='1' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
1135   <node id='1' lon='1' lat='0' changeset='#{changeset_id}' version='2'/>
1136   <node id='1' lon='1' lat='1' changeset='#{changeset_id}' version='3'/>
1137   <node id='1' lon='1' lat='2' changeset='#{changeset_id}' version='4'/>
1138   <node id='1' lon='2' lat='2' changeset='#{changeset_id}' version='5'/>
1139   <node id='1' lon='3' lat='2' changeset='#{changeset_id}' version='6'/>
1140   <node id='1' lon='3' lat='3' changeset='#{changeset_id}' version='7'/>
1141   <node id='1' lon='9' lat='9' changeset='#{changeset_id}' version='8'/>
1142  </modify>
1143 </osmChange>
1144 EOF
1145
1146     # upload it
1147     content diff
1148     post :upload, :id => changeset_id
1149     assert_response :success, 
1150       "can't upload multiple versions of an element in a diff: #{@response.body}"
1151     
1152     get :download, :id => changeset_id
1153     assert_response :success
1154
1155     assert_select "osmChange", 1
1156     assert_select "osmChange>modify", 8
1157     assert_select "osmChange>modify>node", 8
1158   end
1159   
1160   ##
1161   # culled this from josm to ensure that nothing in the way that josm
1162   # is formatting the request is causing it to fail.
1163   #
1164   # NOTE: the error turned out to be something else completely!
1165   def test_josm_upload
1166     basic_authorization(users(:public_user).email, "test")
1167
1168     # create a temporary changeset
1169     content "<osm><changeset>" +
1170       "<tag k='created_by' v='osm test suite checking changesets'/>" + 
1171       "</changeset></osm>"
1172     put :create
1173     assert_response :success
1174     changeset_id = @response.body.to_i
1175
1176     diff = <<OSMFILE
1177 <osmChange version="0.6" generator="JOSM">
1178 <create version="0.6" generator="JOSM">
1179   <node id='-1' visible='true' changeset='#{changeset_id}' lat='51.49619982187321' lon='-0.18722061869438314' />
1180   <node id='-2' visible='true' changeset='#{changeset_id}' lat='51.496359883909605' lon='-0.18653093576241928' />
1181   <node id='-3' visible='true' changeset='#{changeset_id}' lat='51.49598132358285' lon='-0.18719613290981638' />
1182   <node id='-4' visible='true' changeset='#{changeset_id}' lat='51.4961591711078' lon='-0.18629015888084607' />
1183   <node id='-5' visible='true' changeset='#{changeset_id}' lat='51.49582126021711' lon='-0.18708186591517145' />
1184   <node id='-6' visible='true' changeset='#{changeset_id}' lat='51.49591018437858' lon='-0.1861432441734455' />
1185   <node id='-7' visible='true' changeset='#{changeset_id}' lat='51.49560784152179' lon='-0.18694719410005425' />
1186   <node id='-8' visible='true' changeset='#{changeset_id}' lat='51.49567389979617' lon='-0.1860289771788006' />
1187   <node id='-9' visible='true' changeset='#{changeset_id}' lat='51.49543761398892' lon='-0.186820684213126' />
1188   <way id='-10' action='modiy' visible='true' changeset='#{changeset_id}'>
1189     <nd ref='-1' />
1190     <nd ref='-2' />
1191     <nd ref='-3' />
1192     <nd ref='-4' />
1193     <nd ref='-5' />
1194     <nd ref='-6' />
1195     <nd ref='-7' />
1196     <nd ref='-8' />
1197     <nd ref='-9' />
1198     <tag k='highway' v='residential' />
1199     <tag k='name' v='Foobar Street' />
1200   </way>
1201 </create>
1202 </osmChange>
1203 OSMFILE
1204
1205     # upload it
1206     content diff
1207     post :upload, :id => changeset_id
1208     assert_response :success, 
1209       "can't upload a diff from JOSM: #{@response.body}"
1210     
1211     get :download, :id => changeset_id
1212     assert_response :success
1213
1214     assert_select "osmChange", 1
1215     assert_select "osmChange>create>node", 9
1216     assert_select "osmChange>create>way", 1
1217     assert_select "osmChange>create>way>nd", 9
1218     assert_select "osmChange>create>way>tag", 2
1219   end
1220
1221   ##
1222   # when we make some complex changes we get the same changes back from the 
1223   # diff download.
1224   def test_diff_download_complex
1225     basic_authorization(users(:public_user).email, "test")
1226
1227     # create a temporary changeset
1228     content "<osm><changeset>" +
1229       "<tag k='created_by' v='osm test suite checking changesets'/>" + 
1230       "</changeset></osm>"
1231     put :create
1232     assert_response :success
1233     changeset_id = @response.body.to_i
1234
1235     # add a diff to it
1236     diff = <<EOF
1237 <osmChange>
1238  <delete>
1239   <node id='1' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
1240  </delete>
1241  <create>
1242   <node id='-1' lon='9' lat='9' changeset='#{changeset_id}' version='0'/>
1243   <node id='-2' lon='8' lat='9' changeset='#{changeset_id}' version='0'/>
1244   <node id='-3' lon='7' lat='9' changeset='#{changeset_id}' version='0'/>
1245  </create>
1246  <modify>
1247   <node id='3' lon='20' lat='15' changeset='#{changeset_id}' version='1'/>
1248   <way id='1' changeset='#{changeset_id}' version='1'>
1249    <nd ref='3'/>
1250    <nd ref='-1'/>
1251    <nd ref='-2'/>
1252    <nd ref='-3'/>
1253   </way>
1254  </modify>
1255 </osmChange>
1256 EOF
1257
1258     # upload it
1259     content diff
1260     post :upload, :id => changeset_id
1261     assert_response :success, 
1262       "can't upload multiple versions of an element in a diff: #{@response.body}"
1263     
1264     get :download, :id => changeset_id
1265     assert_response :success
1266
1267     assert_select "osmChange", 1
1268     assert_select "osmChange>create", 3
1269     assert_select "osmChange>delete", 1
1270     assert_select "osmChange>modify", 2
1271     assert_select "osmChange>create>node", 3
1272     assert_select "osmChange>delete>node", 1 
1273     assert_select "osmChange>modify>node", 1
1274     assert_select "osmChange>modify>way", 1
1275   end
1276
1277   def test_changeset_download
1278     get :download, :id => changesets(:normal_user_first_change).id
1279     assert_response :success
1280     assert_template nil
1281     #print @response.body
1282     # FIXME needs more assert_select tests
1283     assert_select "osmChange[version='#{API_VERSION}'][generator='#{GENERATOR}']" do
1284       assert_select "create", :count => 5
1285       assert_select "create>node[id=#{nodes(:used_node_2).id}][visible=#{nodes(:used_node_2).visible?}][version=#{nodes(:used_node_2).version}]" do
1286         assert_select "tag[k=#{node_tags(:t3).k}][v=#{node_tags(:t3).v}]"
1287       end
1288       assert_select "create>node[id=#{nodes(:visible_node).id}]"
1289     end
1290   end
1291   
1292   ##
1293   # check that the bounding box of a changeset gets updated correctly
1294   ## FIXME: This should really be moded to a integration test due to the with_controller
1295   def test_changeset_bbox
1296     basic_authorization users(:public_user).email, "test"
1297
1298     # create a new changeset
1299     content "<osm><changeset/></osm>"
1300     put :create
1301     assert_response :success, "Creating of changeset failed."
1302     changeset_id = @response.body.to_i
1303     
1304     # add a single node to it
1305     with_controller(NodeController.new) do
1306       content "<osm><node lon='1' lat='2' changeset='#{changeset_id}'/></osm>"
1307       put :create
1308       assert_response :success, "Couldn't create node."
1309     end
1310
1311     # get the bounding box back from the changeset
1312     get :read, :id => changeset_id
1313     assert_response :success, "Couldn't read back changeset."
1314     assert_select "osm>changeset[min_lon=1.0]", 1
1315     assert_select "osm>changeset[max_lon=1.0]", 1
1316     assert_select "osm>changeset[min_lat=2.0]", 1
1317     assert_select "osm>changeset[max_lat=2.0]", 1
1318
1319     # add another node to it
1320     with_controller(NodeController.new) do
1321       content "<osm><node lon='2' lat='1' changeset='#{changeset_id}'/></osm>"
1322       put :create
1323       assert_response :success, "Couldn't create second node."
1324     end
1325
1326     # get the bounding box back from the changeset
1327     get :read, :id => changeset_id
1328     assert_response :success, "Couldn't read back changeset for the second time."
1329     assert_select "osm>changeset[min_lon=1.0]", 1
1330     assert_select "osm>changeset[max_lon=2.0]", 1
1331     assert_select "osm>changeset[min_lat=1.0]", 1
1332     assert_select "osm>changeset[max_lat=2.0]", 1
1333
1334     # add (delete) a way to it, which contains a point at (3,3)
1335     with_controller(WayController.new) do
1336       content update_changeset(current_ways(:visible_way).to_xml,
1337                                changeset_id)
1338       put :delete, :id => current_ways(:visible_way).id
1339       assert_response :success, "Couldn't delete a way."
1340     end
1341
1342     # get the bounding box back from the changeset
1343     get :read, :id => changeset_id
1344     assert_response :success, "Couldn't read back changeset for the third time."
1345     # note that the 3.1 here is because of the bbox overexpansion
1346     assert_select "osm>changeset[min_lon=1.0]", 1
1347     assert_select "osm>changeset[max_lon=3.1]", 1
1348     assert_select "osm>changeset[min_lat=1.0]", 1
1349     assert_select "osm>changeset[max_lat=3.1]", 1    
1350   end
1351
1352   ##
1353   # test that the changeset :include method works as it should
1354   def test_changeset_include
1355     basic_authorization users(:public_user).display_name, "test"
1356
1357     # create a new changeset
1358     content "<osm><changeset/></osm>"
1359     put :create
1360     assert_response :success, "Creating of changeset failed."
1361     changeset_id = @response.body.to_i
1362
1363     # NOTE: the include method doesn't over-expand, like inserting
1364     # a real method does. this is because we expect the client to 
1365     # know what it is doing!
1366     check_after_include(changeset_id,  1,  1, [ 1,  1,  1,  1])
1367     check_after_include(changeset_id,  3,  3, [ 1,  1,  3,  3])
1368     check_after_include(changeset_id,  4,  2, [ 1,  1,  4,  3])
1369     check_after_include(changeset_id,  2,  2, [ 1,  1,  4,  3])
1370     check_after_include(changeset_id, -1, -1, [-1, -1,  4,  3])
1371     check_after_include(changeset_id, -2,  5, [-2, -1,  4,  5])
1372   end
1373   
1374   ##
1375   # test that a not found, wrong method with the expand bbox works as expected
1376   def test_changeset_expand_bbox_error
1377     basic_authorization users(:public_user).display_name, "test"
1378     
1379     # create a new changeset
1380     content "<osm><changeset/></osm>"
1381     put :create
1382     assert_response :success, "Creating of changeset failed."
1383     changeset_id = @response.body.to_i
1384     
1385     lon=58.2
1386     lat=-0.45
1387     
1388     # Try and put
1389     content "<osm><node lon='#{lon}' lat='#{lat}'/></osm>"
1390     put :expand_bbox, :id => changeset_id
1391     assert_response :method_not_allowed, "shouldn't be able to put a bbox expand"
1392
1393     # Try to get the update
1394     content "<osm><node lon='#{lon}' lat='#{lat}'/></osm>"
1395     get :expand_bbox, :id => changeset_id
1396     assert_response :method_not_allowed, "shouldn't be able to get a bbox expand"
1397     
1398     # Try to use a hopefully missing changeset
1399     content "<osm><node lon='#{lon}' lat='#{lat}'/></osm>"
1400     post :expand_bbox, :id => changeset_id+13245
1401     assert_response :not_found, "shouldn't be able to do a bbox expand on a nonexistant changeset"
1402
1403   end
1404
1405   ##
1406   # test the query functionality of changesets
1407   def test_query
1408     get :query, :bbox => "-10,-10, 10, 10"
1409     assert_response :success, "can't get changesets in bbox"
1410     assert_changesets [1,4,6]
1411
1412     get :query, :bbox => "4.5,4.5,4.6,4.6"
1413     assert_response :success, "can't get changesets in bbox"
1414     assert_changesets [1]
1415
1416     # not found when looking for changesets of non-existing users
1417     get :query, :user => User.maximum(:id) + 1
1418     assert_response :not_found
1419     get :query, :display_name => " "
1420     assert_response :not_found
1421
1422     # can't get changesets of user 1 without authenticating
1423     get :query, :user => users(:normal_user).id
1424     assert_response :not_found, "shouldn't be able to get changesets by non-public user (ID)"
1425     get :query, :display_name => users(:normal_user).display_name
1426     assert_response :not_found, "shouldn't be able to get changesets by non-public user (name)"
1427
1428     # but this should work
1429     basic_authorization "test@openstreetmap.org", "test"
1430     get :query, :user => users(:normal_user).id
1431     assert_response :success, "can't get changesets by user ID"
1432     assert_changesets [1,3,6]
1433
1434     get :query, :display_name => users(:normal_user).display_name
1435     assert_response :success, "can't get changesets by user name"
1436     assert_changesets [1,3,6]
1437
1438     # check that the correct error is given when we provide both UID and name
1439     get :query, :user => users(:normal_user).id, :display_name => users(:normal_user).display_name
1440     assert_response :bad_request, "should be a bad request to have both ID and name specified"
1441
1442     get :query, :user => users(:normal_user).id, :open => true
1443     assert_response :success, "can't get changesets by user and open"
1444     assert_changesets [1]
1445
1446     get :query, :time => '2007-12-31'
1447     assert_response :success, "can't get changesets by time-since"
1448     assert_changesets [1,2,4,5,6]
1449
1450     get :query, :time => '2008-01-01T12:34Z'
1451     assert_response :success, "can't get changesets by time-since with hour"
1452     assert_changesets [1,2,4,5,6]
1453
1454     get :query, :time => '2007-12-31T23:59Z,2008-01-01T00:01Z'
1455     assert_response :success, "can't get changesets by time-range"
1456     assert_changesets [1,5,6]
1457
1458     get :query, :open => 'true'
1459     assert_response :success, "can't get changesets by open-ness"
1460     assert_changesets [1,2,4]
1461
1462     get :query, :closed => 'true'
1463     assert_response :success, "can't get changesets by closed-ness"
1464     assert_changesets [3,5,6,7]
1465
1466     get :query, :closed => 'true', :user => users(:normal_user).id
1467     assert_response :success, "can't get changesets by closed-ness and user"
1468     assert_changesets [3,6]
1469
1470     get :query, :closed => 'true', :user => users(:public_user).id
1471     assert_response :success, "can't get changesets by closed-ness and user"
1472     assert_changesets [7]
1473   end
1474
1475   ##
1476   # check that errors are returned if garbage is inserted 
1477   # into query strings
1478   def test_query_invalid
1479     [ "abracadabra!",
1480       "1,2,3,F",
1481       ";drop table users;"
1482       ].each do |bbox|
1483       get :query, :bbox => bbox
1484       assert_response :bad_request, "'#{bbox}' isn't a bbox"
1485     end
1486
1487     [ "now()",
1488       "00-00-00",
1489       ";drop table users;",
1490       ",",
1491       "-,-"
1492       ].each do |time|
1493       get :query, :time => time
1494       assert_response :bad_request, "'#{time}' isn't a valid time range"
1495     end
1496
1497     [ "me",
1498       "foobar",
1499       "-1",
1500       "0"
1501       ].each do |uid|
1502       get :query, :user => uid
1503       assert_response :bad_request, "'#{uid}' isn't a valid user ID"
1504     end
1505   end
1506
1507   ##
1508   # check updating tags on a changeset
1509   def test_changeset_update
1510     ## First try with the non-public user
1511     changeset = changesets(:normal_user_first_change)
1512     new_changeset = changeset.to_xml
1513     new_tag = XML::Node.new "tag"
1514     new_tag['k'] = "tagtesting"
1515     new_tag['v'] = "valuetesting"
1516     new_changeset.find("//osm/changeset").first << new_tag
1517     content new_changeset
1518
1519     # try without any authorization
1520     put :update, :id => changeset.id
1521     assert_response :unauthorized
1522
1523     # try with the wrong authorization
1524     basic_authorization users(:public_user).email, "test"
1525     put :update, :id => changeset.id
1526     assert_response :conflict
1527
1528     # now this should get an unauthorized
1529     basic_authorization users(:normal_user).email, "test"
1530     put :update, :id => changeset.id
1531     assert_require_public_data "user with their data non-public, shouldn't be able to edit their changeset"
1532     
1533     
1534     ## Now try with the public user
1535     changeset = changesets(:public_user_first_change)
1536     new_changeset = changeset.to_xml
1537     new_tag = XML::Node.new "tag"
1538     new_tag['k'] = "tagtesting"
1539     new_tag['v'] = "valuetesting"
1540     new_changeset.find("//osm/changeset").first << new_tag
1541     content new_changeset
1542     
1543     # try without any authorization
1544     @request.env["HTTP_AUTHORIZATION"] = nil
1545     put :update, :id => changeset.id
1546     assert_response :unauthorized
1547
1548     # try with the wrong authorization
1549     basic_authorization users(:second_public_user).email, "test"
1550     put :update, :id => changeset.id
1551     assert_response :conflict
1552
1553     # now this should work...
1554     basic_authorization users(:public_user).email, "test"
1555     put :update, :id => changeset.id
1556     assert_response :success
1557
1558     assert_select "osm>changeset[id=#{changeset.id}]", 1
1559     assert_select "osm>changeset>tag", 2
1560     assert_select "osm>changeset>tag[k=tagtesting][v=valuetesting]", 1
1561   end
1562   
1563   ##
1564   # check that a user different from the one who opened the changeset
1565   # can't modify it.
1566   def test_changeset_update_invalid
1567     basic_authorization users(:public_user).email, "test"
1568
1569     changeset = changesets(:normal_user_first_change)
1570     new_changeset = changeset.to_xml
1571     new_tag = XML::Node.new "tag"
1572     new_tag['k'] = "testing"
1573     new_tag['v'] = "testing"
1574     new_changeset.find("//osm/changeset").first << new_tag
1575
1576     content new_changeset
1577     put :update, :id => changeset.id
1578     assert_response :conflict
1579   end
1580
1581   ##
1582   # check that a changeset can contain a certain max number of changes.
1583   ## FIXME should be changed to an integration test due to the with_controller
1584   def test_changeset_limits
1585     basic_authorization users(:public_user).email, "test"
1586
1587     # open a new changeset
1588     content "<osm><changeset/></osm>"
1589     put :create
1590     assert_response :success, "can't create a new changeset"
1591     cs_id = @response.body.to_i
1592
1593     # start the counter just short of where the changeset should finish.
1594     offset = 10
1595     # alter the database to set the counter on the changeset directly, 
1596     # otherwise it takes about 6 minutes to fill all of them.
1597     changeset = Changeset.find(cs_id)
1598     changeset.num_changes = Changeset::MAX_ELEMENTS - offset
1599     changeset.save!
1600
1601     with_controller(NodeController.new) do
1602       # create a new node
1603       content "<osm><node changeset='#{cs_id}' lat='0.0' lon='0.0'/></osm>"
1604       put :create
1605       assert_response :success, "can't create a new node"
1606       node_id = @response.body.to_i
1607
1608       get :read, :id => node_id
1609       assert_response :success, "can't read back new node"
1610       node_doc = XML::Parser.string(@response.body).parse
1611       node_xml = node_doc.find("//osm/node").first
1612
1613       # loop until we fill the changeset with nodes
1614       offset.times do |i|
1615         node_xml['lat'] = rand.to_s
1616         node_xml['lon'] = rand.to_s
1617         node_xml['version'] = (i+1).to_s
1618
1619         content node_doc
1620         put :update, :id => node_id
1621         assert_response :success, "attempt #{i} should have succeeded"
1622       end
1623
1624       # trying again should fail
1625       node_xml['lat'] = rand.to_s
1626       node_xml['lon'] = rand.to_s
1627       node_xml['version'] = offset.to_s
1628       
1629       content node_doc
1630       put :update, :id => node_id
1631       assert_response :conflict, "final attempt should have failed"
1632     end
1633
1634     changeset = Changeset.find(cs_id)
1635     assert_equal Changeset::MAX_ELEMENTS + 1, changeset.num_changes
1636
1637     # check that the changeset is now closed as well
1638     assert(!changeset.is_open?, 
1639            "changeset should have been auto-closed by exceeding " + 
1640            "element limit.")
1641   end
1642   
1643   ##
1644   # This should display the last 20 changesets closed.
1645   def test_list
1646     changesets = Changeset.find(:all, :order => "created_at DESC", :conditions => ['min_lat IS NOT NULL'], :limit=> 20)
1647     assert changesets.size <= 20
1648     get :list, {:format => "html"}
1649     assert_response :success
1650     assert_template "list"
1651     # Now check that all 20 (or however many were returned) changesets are in the html
1652     assert_select "h1", :text => "Changesets", :count => 1
1653     assert_select "table[id='changeset_list'] tr", :count => changesets.size + 1
1654     changesets.each do |changeset|
1655       # FIXME this test needs rewriting - test for table contents
1656     end
1657   end
1658   
1659   ##
1660   # Checks the display of the user changesets listing
1661   def test_list_user
1662     user = users(:public_user)
1663     get :list, {:format => "html", :display_name => user.display_name}
1664     assert_response :success
1665     assert_template "changeset/_user"
1666     ## FIXME need to add more checks to see which if edits are actually shown if your data is public
1667   end
1668   
1669   ##
1670   # Check the not found of the list user changesets
1671   def test_list_user_not_found
1672     get :list, {:format => "html", :display_name => "Some random user"}
1673     assert_response :not_found
1674     assert_template 'user/no_such_user'
1675   end
1676   
1677   #------------------------------------------------------------
1678   # utility functions
1679   #------------------------------------------------------------
1680
1681   ##
1682   # boilerplate for checking that certain changesets exist in the
1683   # output.
1684   def assert_changesets(ids)
1685     assert_select "osm>changeset", ids.size
1686     ids.each do |id|
1687       assert_select "osm>changeset[id=#{id}]", 1
1688     end
1689   end
1690
1691   ##
1692   # call the include method and assert properties of the bbox
1693   def check_after_include(changeset_id, lon, lat, bbox)
1694     content "<osm><node lon='#{lon}' lat='#{lat}'/></osm>"
1695     post :expand_bbox, :id => changeset_id
1696     assert_response :success, "Setting include of changeset failed: #{@response.body}"
1697
1698     # check exactly one changeset
1699     assert_select "osm>changeset", 1
1700     assert_select "osm>changeset[id=#{changeset_id}]", 1
1701
1702     # check the bbox
1703     doc = XML::Parser.string(@response.body).parse
1704     changeset = doc.find("//osm/changeset").first
1705     assert_equal bbox[0], changeset['min_lon'].to_f, "min lon"
1706     assert_equal bbox[1], changeset['min_lat'].to_f, "min lat"
1707     assert_equal bbox[2], changeset['max_lon'].to_f, "max lon"
1708     assert_equal bbox[3], changeset['max_lat'].to_f, "max lat"
1709   end
1710
1711   ##
1712   # update the changeset_id of a way element
1713   def update_changeset(xml, changeset_id)
1714     xml_attr_rewrite(xml, 'changeset', changeset_id)
1715   end
1716
1717   ##
1718   # update an attribute in a way element
1719   def xml_attr_rewrite(xml, name, value)
1720     xml.find("//osm/way").first[name] = value.to_s
1721     return xml
1722   end
1723
1724 end