]> git.openstreetmap.org Git - rails.git/blob - test/functional/changeset_controller_test.rb
Make the changeset navigation use the new fancy «/» arrows
[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   # 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>
515 EOF
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>
553 EOF
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>
611 EOF
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>
650 EOF
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>
678 EOF
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>
699 EOF
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>
720 EOF
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>
746 EOF
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>
784 EOF
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>
812 EOF
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>
842 EOF
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>
866 EOF
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>
897 EOF
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>
921 EOF
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>
1068 EOF
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>
1127 OSMFILE
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>
1180 EOF
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     # not found when looking for changesets of non-existing users
1341     get :query, :user => User.maximum(:id) + 1
1342     assert_response :not_found
1343     get :query, :display_name => " "
1344     assert_response :not_found
1345
1346     # can't get changesets of user 1 without authenticating
1347     get :query, :user => users(:normal_user).id
1348     assert_response :not_found, "shouldn't be able to get changesets by non-public user (ID)"
1349     get :query, :display_name => users(:normal_user).display_name
1350     assert_response :not_found, "shouldn't be able to get changesets by non-public user (name)"
1351
1352     # but this should work
1353     basic_authorization "test@openstreetmap.org", "test"
1354     get :query, :user => users(:normal_user).id
1355     assert_response :success, "can't get changesets by user ID"
1356     assert_changesets [1,3,6]
1357
1358     get :query, :display_name => users(:normal_user).display_name
1359     assert_response :success, "can't get changesets by user name"
1360     assert_changesets [1,3,6]
1361
1362     # check that the correct error is given when we provide both UID and name
1363     get :query, :user => users(:normal_user).id, :display_name => users(:normal_user).display_name
1364     assert_response :bad_request, "should be a bad request to have both ID and name specified"
1365
1366     get :query, :user => users(:normal_user).id, :open => true
1367     assert_response :success, "can't get changesets by user and open"
1368     assert_changesets [1]
1369
1370     get :query, :time => '2007-12-31'
1371     assert_response :success, "can't get changesets by time-since"
1372     assert_changesets [1,2,4,5,6]
1373
1374     get :query, :time => '2008-01-01T12:34Z'
1375     assert_response :success, "can't get changesets by time-since with hour"
1376     assert_changesets [1,2,4,5,6]
1377
1378     get :query, :time => '2007-12-31T23:59Z,2008-01-01T00:01Z'
1379     assert_response :success, "can't get changesets by time-range"
1380     assert_changesets [1,5,6]
1381
1382     get :query, :open => 'true'
1383     assert_response :success, "can't get changesets by open-ness"
1384     assert_changesets [1,2,4]
1385   end
1386
1387   ##
1388   # check that errors are returned if garbage is inserted 
1389   # into query strings
1390   def test_query_invalid
1391     [ "abracadabra!",
1392       "1,2,3,F",
1393       ";drop table users;"
1394       ].each do |bbox|
1395       get :query, :bbox => bbox
1396       assert_response :bad_request, "'#{bbox}' isn't a bbox"
1397     end
1398
1399     [ "now()",
1400       "00-00-00",
1401       ";drop table users;",
1402       ",",
1403       "-,-"
1404       ].each do |time|
1405       get :query, :time => time
1406       assert_response :bad_request, "'#{time}' isn't a valid time range"
1407     end
1408
1409     [ "me",
1410       "foobar",
1411       "-1",
1412       "0"
1413       ].each do |uid|
1414       get :query, :user => uid
1415       assert_response :bad_request, "'#{uid}' isn't a valid user ID"
1416     end
1417   end
1418
1419   ##
1420   # check updating tags on a changeset
1421   def test_changeset_update
1422     ## First try with the non-public user
1423     changeset = changesets(:normal_user_first_change)
1424     new_changeset = changeset.to_xml
1425     new_tag = XML::Node.new "tag"
1426     new_tag['k'] = "tagtesting"
1427     new_tag['v'] = "valuetesting"
1428     new_changeset.find("//osm/changeset").first << new_tag
1429     content new_changeset
1430
1431     # try without any authorization
1432     put :update, :id => changeset.id
1433     assert_response :unauthorized
1434
1435     # try with the wrong authorization
1436     basic_authorization users(:public_user).email, "test"
1437     put :update, :id => changeset.id
1438     assert_response :conflict
1439
1440     # now this should get an unauthorized
1441     basic_authorization users(:normal_user).email, "test"
1442     put :update, :id => changeset.id
1443     assert_require_public_data "user with their data non-public, shouldn't be able to edit their changeset"
1444     
1445     
1446     ## Now try with the public user
1447     changeset = changesets(:public_user_first_change)
1448     new_changeset = changeset.to_xml
1449     new_tag = XML::Node.new "tag"
1450     new_tag['k'] = "tagtesting"
1451     new_tag['v'] = "valuetesting"
1452     new_changeset.find("//osm/changeset").first << new_tag
1453     content new_changeset
1454     
1455     # try without any authorization
1456     @request.env["HTTP_AUTHORIZATION"] = nil
1457     put :update, :id => changeset.id
1458     assert_response :unauthorized
1459
1460     # try with the wrong authorization
1461     basic_authorization users(:second_public_user).email, "test"
1462     put :update, :id => changeset.id
1463     assert_response :conflict
1464
1465     # now this should work...
1466     basic_authorization users(:public_user).email, "test"
1467     put :update, :id => changeset.id
1468     assert_response :success
1469
1470     assert_select "osm>changeset[id=#{changeset.id}]", 1
1471     assert_select "osm>changeset>tag", 2
1472     assert_select "osm>changeset>tag[k=tagtesting][v=valuetesting]", 1
1473   end
1474   
1475   ##
1476   # check that a user different from the one who opened the changeset
1477   # can't modify it.
1478   def test_changeset_update_invalid
1479     basic_authorization users(:public_user).email, "test"
1480
1481     changeset = changesets(:normal_user_first_change)
1482     new_changeset = changeset.to_xml
1483     new_tag = XML::Node.new "tag"
1484     new_tag['k'] = "testing"
1485     new_tag['v'] = "testing"
1486     new_changeset.find("//osm/changeset").first << new_tag
1487
1488     content new_changeset
1489     put :update, :id => changeset.id
1490     assert_response :conflict
1491   end
1492
1493   ##
1494   # check that a changeset can contain a certain max number of changes.
1495   ## FIXME should be changed to an integration test due to the with_controller
1496   def test_changeset_limits
1497     basic_authorization users(:public_user).email, "test"
1498
1499     # open a new changeset
1500     content "<osm><changeset/></osm>"
1501     put :create
1502     assert_response :success, "can't create a new changeset"
1503     cs_id = @response.body.to_i
1504
1505     # start the counter just short of where the changeset should finish.
1506     offset = 10
1507     # alter the database to set the counter on the changeset directly, 
1508     # otherwise it takes about 6 minutes to fill all of them.
1509     changeset = Changeset.find(cs_id)
1510     changeset.num_changes = Changeset::MAX_ELEMENTS - offset
1511     changeset.save!
1512
1513     with_controller(NodeController.new) do
1514       # create a new node
1515       content "<osm><node changeset='#{cs_id}' lat='0.0' lon='0.0'/></osm>"
1516       put :create
1517       assert_response :success, "can't create a new node"
1518       node_id = @response.body.to_i
1519
1520       get :read, :id => node_id
1521       assert_response :success, "can't read back new node"
1522       node_doc = XML::Parser.string(@response.body).parse
1523       node_xml = node_doc.find("//osm/node").first
1524
1525       # loop until we fill the changeset with nodes
1526       offset.times do |i|
1527         node_xml['lat'] = rand.to_s
1528         node_xml['lon'] = rand.to_s
1529         node_xml['version'] = (i+1).to_s
1530
1531         content node_doc
1532         put :update, :id => node_id
1533         assert_response :success, "attempt #{i} should have succeeded"
1534       end
1535
1536       # trying again should fail
1537       node_xml['lat'] = rand.to_s
1538       node_xml['lon'] = rand.to_s
1539       node_xml['version'] = offset.to_s
1540       
1541       content node_doc
1542       put :update, :id => node_id
1543       assert_response :conflict, "final attempt should have failed"
1544     end
1545
1546     changeset = Changeset.find(cs_id)
1547     assert_equal Changeset::MAX_ELEMENTS + 1, changeset.num_changes
1548
1549     # check that the changeset is now closed as well
1550     assert(!changeset.is_open?, 
1551            "changeset should have been auto-closed by exceeding " + 
1552            "element limit.")
1553   end
1554   
1555   ##
1556   # This should display the last 20 changesets closed.
1557   def test_list
1558     changesets = Changeset.find(:all, :order => "created_at DESC", :conditions => ['min_lat IS NOT NULL'], :limit=> 20)
1559     assert changesets.size <= 20
1560     get :list, {:format => "html"}
1561     assert_response :success
1562     assert_template "list"
1563     # Now check that all 20 (or however many were returned) changesets are in the html
1564     assert_select "h1", :text => "Changesets", :count => 1
1565     assert_select "table[id='changeset_list'] tr", :count => changesets.size + 1
1566     changesets.each do |changeset|
1567       # FIXME this test needs rewriting - test for table contents
1568     end
1569   end
1570   
1571   ##
1572   # Checks the display of the user changesets listing
1573   def test_list_user
1574     user = users(:public_user)
1575     get :list, {:format => "html", :display_name => user.display_name}
1576     assert_response :success
1577     assert_template "changeset/_user"
1578     ## FIXME need to add more checks to see which if edits are actually shown if your data is public
1579   end
1580   
1581   ##
1582   # Check the not found of the list user changesets
1583   def test_list_user_not_found
1584     get :list, {:format => "html", :display_name => "Some random user"}
1585     assert_response :not_found
1586     assert_template 'user/no_such_user'
1587   end
1588   
1589   #------------------------------------------------------------
1590   # utility functions
1591   #------------------------------------------------------------
1592
1593   ##
1594   # boilerplate for checking that certain changesets exist in the
1595   # output.
1596   def assert_changesets(ids)
1597     assert_select "osm>changeset", ids.size
1598     ids.each do |id|
1599       assert_select "osm>changeset[id=#{id}]", 1
1600     end
1601   end
1602
1603   ##
1604   # call the include method and assert properties of the bbox
1605   def check_after_include(changeset_id, lon, lat, bbox)
1606     content "<osm><node lon='#{lon}' lat='#{lat}'/></osm>"
1607     post :expand_bbox, :id => changeset_id
1608     assert_response :success, "Setting include of changeset failed: #{@response.body}"
1609
1610     # check exactly one changeset
1611     assert_select "osm>changeset", 1
1612     assert_select "osm>changeset[id=#{changeset_id}]", 1
1613
1614     # check the bbox
1615     doc = XML::Parser.string(@response.body).parse
1616     changeset = doc.find("//osm/changeset").first
1617     assert_equal bbox[0], changeset['min_lon'].to_f, "min lon"
1618     assert_equal bbox[1], changeset['min_lat'].to_f, "min lat"
1619     assert_equal bbox[2], changeset['max_lon'].to_f, "max lon"
1620     assert_equal bbox[3], changeset['max_lat'].to_f, "max lat"
1621   end
1622
1623   ##
1624   # update the changeset_id of a way element
1625   def update_changeset(xml, changeset_id)
1626     xml_attr_rewrite(xml, 'changeset', changeset_id)
1627   end
1628
1629   ##
1630   # update an attribute in a way element
1631   def xml_attr_rewrite(xml, name, value)
1632     xml.find("//osm/way").first[name] = value.to_s
1633     return xml
1634   end
1635
1636 end