Fix the test I just broke...
[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}.")
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   ##
423   # test that deleting stuff in a transaction doesn't bypass the checks
424   # to ensure that used elements are not deleted.
425   def test_upload_delete_invalid
426     basic_authorization users(:public_user).email, "test"
427
428     diff = XML::Document.new
429     diff.root = XML::Node.new "osmChange"
430     delete = XML::Node.new "delete"
431     diff.root << delete
432     delete << current_relations(:public_visible_relation).to_xml_node
433     delete << current_ways(:used_way).to_xml_node
434     delete << current_nodes(:node_used_by_relationship).to_xml_node
435
436     # upload it
437     content diff
438     post :upload, :id => 2
439     assert_response :precondition_failed, 
440       "shouldn't be able to upload a invalid deletion diff: #{@response.body}"
441     assert_equal "Precondition failed: Way 3 still used by relation 1.", @response.body
442
443     # check that nothing was, in fact, deleted
444     assert_equal true, Node.find(current_nodes(:node_used_by_relationship).id).visible
445     assert_equal true, Way.find(current_ways(:used_way).id).visible
446     assert_equal true, Relation.find(current_relations(:visible_relation).id).visible
447   end
448
449   ##
450   # upload something which creates new objects and inserts them into
451   # existing containers using placeholders.
452   def test_upload_complex
453     basic_authorization users(:public_user).email, "test"
454     cs_id = changesets(:public_user_first_change).id
455
456     # simple diff to create a node way and relation using placeholders
457     diff = <<EOF
458 <osmChange>
459  <create>
460   <node id='-1' lon='0' lat='0' changeset='#{cs_id}'>
461    <tag k='foo' v='bar'/>
462    <tag k='baz' v='bat'/>
463   </node>
464  </create>
465  <modify>
466   <way id='1' changeset='#{cs_id}' version='1'>
467    <nd ref='-1'/>
468    <nd ref='3'/>
469   </way>
470   <relation id='1' changeset='#{cs_id}' version='1'>
471    <member type='way' role='some' ref='3'/>
472    <member type='node' role='some' ref='-1'/>
473    <member type='relation' role='some' ref='3'/>
474   </relation>
475  </modify>
476 </osmChange>
477 EOF
478
479     # upload it
480     content diff
481     post :upload, :id => cs_id
482     assert_response :success, 
483       "can't upload a complex diff to changeset: #{@response.body}"
484
485     # check the returned payload
486     assert_select "diffResult[version=#{API_VERSION}][generator=\"#{GENERATOR}\"]", 1
487     assert_select "diffResult>node", 1
488     assert_select "diffResult>way", 1
489     assert_select "diffResult>relation", 1
490
491     # inspect the response to find out what the new element IDs are
492     doc = XML::Parser.string(@response.body).parse
493     new_node_id = doc.find("//diffResult/node").first["new_id"].to_i
494
495     # check that the changes made it into the database
496     assert_equal 2, Node.find(new_node_id).tags.size, "new node should have two tags"
497     assert_equal [new_node_id, 3], Way.find(1).nds, "way nodes should match"
498     Relation.find(1).members.each do |type,id,role|
499       if type == 'node'
500         assert_equal new_node_id, id, "relation should contain new node"
501       end
502     end
503   end
504     
505   ##
506   # create a diff which references several changesets, which should cause
507   # a rollback and none of the diff gets committed
508   def test_upload_invalid_changesets
509     basic_authorization users(:public_user).email, "test"
510     cs_id = changesets(:public_user_first_change).id
511
512     # simple diff to create a node way and relation using placeholders
513     diff = <<EOF
514 <osmChange>
515  <modify>
516   <node id='1' lon='0' lat='0' changeset='#{cs_id}' version='1'/>
517   <way id='1' changeset='#{cs_id}' version='1'>
518    <nd ref='3'/>
519   </way>
520  </modify>
521  <modify>
522   <relation id='1' changeset='#{cs_id}' version='1'>
523    <member type='way' role='some' ref='3'/>
524    <member type='node' role='some' ref='5'/>
525    <member type='relation' role='some' ref='3'/>
526   </relation>
527  </modify>
528  <create>
529   <node id='-1' lon='0' lat='0' changeset='4'>
530    <tag k='foo' v='bar'/>
531    <tag k='baz' v='bat'/>
532   </node>
533  </create>
534 </osmChange>
535 EOF
536     # cache the objects before uploading them
537     node = current_nodes(:visible_node)
538     way = current_ways(:visible_way)
539     rel = current_relations(:visible_relation)
540
541     # upload it
542     content diff
543     post :upload, :id => cs_id
544     assert_response :conflict, 
545       "uploading a diff with multiple changsets should have failed"
546
547     # check that objects are unmodified
548     assert_nodes_are_equal(node, Node.find(1))
549     assert_ways_are_equal(way, Way.find(1))
550   end
551     
552   ##
553   # upload multiple versions of the same element in the same diff.
554   def test_upload_multiple_valid
555     basic_authorization users(:public_user).email, "test"
556     cs_id = changesets(:public_user_first_change).id
557
558     # change the location of a node multiple times, each time referencing
559     # the last version. doesn't this depend on version numbers being
560     # sequential?
561     diff = <<EOF
562 <osmChange>
563  <modify>
564   <node id='1' lon='0' lat='0' changeset='#{cs_id}' version='1'/>
565   <node id='1' lon='1' lat='0' changeset='#{cs_id}' version='2'/>
566   <node id='1' lon='1' lat='1' changeset='#{cs_id}' version='3'/>
567   <node id='1' lon='1' lat='2' changeset='#{cs_id}' version='4'/>
568   <node id='1' lon='2' lat='2' changeset='#{cs_id}' version='5'/>
569   <node id='1' lon='3' lat='2' changeset='#{cs_id}' version='6'/>
570   <node id='1' lon='3' lat='3' changeset='#{cs_id}' version='7'/>
571   <node id='1' lon='9' lat='9' changeset='#{cs_id}' version='8'/>
572  </modify>
573 </osmChange>
574 EOF
575
576     # upload it
577     content diff
578     post :upload, :id => cs_id
579     assert_response :success, 
580       "can't upload multiple versions of an element in a diff: #{@response.body}"
581     
582     # check the response is well-formed. its counter-intuitive, but the
583     # API will return multiple elements with the same ID and different
584     # version numbers for each change we made.
585     assert_select "diffResult>node", 8
586   end
587
588   ##
589   # upload multiple versions of the same element in the same diff, but
590   # keep the version numbers the same.
591   def test_upload_multiple_duplicate
592     basic_authorization users(:public_user).email, "test"
593     cs_id = changesets(:public_user_first_change).id
594
595     diff = <<EOF
596 <osmChange>
597  <modify>
598   <node id='1' lon='0' lat='0' changeset='#{cs_id}' version='1'/>
599   <node id='1' lon='1' lat='1' changeset='#{cs_id}' version='1'/>
600  </modify>
601 </osmChange>
602 EOF
603
604     # upload it
605     content diff
606     post :upload, :id => cs_id
607     assert_response :conflict, 
608       "shouldn't be able to upload the same element twice in a diff: #{@response.body}"
609   end
610
611   ##
612   # try to upload some elements without specifying the version
613   def test_upload_missing_version
614     basic_authorization users(:public_user).email, "test"
615     cs_id = changesets(:public_user_first_change).id
616
617     diff = <<EOF
618 <osmChange>
619  <modify>
620  <node id='1' lon='1' lat='1' changeset='cs_id'/>
621  </modify>
622 </osmChange>
623 EOF
624
625     # upload it
626     content diff
627     post :upload, :id => cs_id
628     assert_response :bad_request, 
629       "shouldn't be able to upload an element without version: #{@response.body}"
630   end
631   
632   ##
633   # try to upload with commands other than create, modify, or delete
634   def test_action_upload_invalid
635     basic_authorization users(:public_user).email, "test"
636     cs_id = changesets(:public_user_first_change).id
637     
638     diff = <<EOF
639 <osmChange>
640   <ping>
641    <node id='1' lon='1' lat='1' changeset='#{cs_id}' />
642   </ping>
643 </osmChange>
644 EOF
645   content diff
646   post :upload, :id => cs_id
647   assert_response :bad_request, "Shouldn't be able to upload a diff with the action ping"
648   assert_equal @response.body, "Unknown action ping, choices are create, modify, delete."
649   end
650
651   ##
652   # upload a valid changeset which has a mixture of whitespace
653   # to check a bug reported by ivansanchez (#1565).
654   def test_upload_whitespace_valid
655     basic_authorization users(:public_user).email, "test"
656     changeset_id = changesets(:public_user_first_change).id
657
658     diff = <<EOF
659 <osmChange>
660  <modify><node id='1' lon='0' lat='0' changeset='#{changeset_id}' 
661   version='1'></node>
662   <node id='1' lon='1' lat='1' changeset='#{changeset_id}' version='2'><tag k='k' v='v'/></node></modify>
663  <modify>
664  <relation id='1' changeset='#{changeset_id}' version='1'><member 
665    type='way' role='some' ref='3'/><member 
666     type='node' role='some' ref='5'/>
667    <member type='relation' role='some' ref='3'/>
668   </relation>
669  </modify></osmChange>
670 EOF
671
672     # upload it
673     content diff
674     post :upload, :id => changeset_id
675     assert_response :success, 
676       "can't upload a valid diff with whitespace variations to changeset: #{@response.body}"
677
678     # check the response is well-formed
679     assert_select "diffResult>node", 2
680     assert_select "diffResult>relation", 1
681
682     # check that the changes made it into the database
683     assert_equal 1, Node.find(1).tags.size, "node 1 should now have one tag"
684     assert_equal 0, Relation.find(1).tags.size, "relation 1 should now have no tags"
685   end
686
687   ##
688   # upload a valid changeset which has a mixture of whitespace
689   # to check a bug reported by ivansanchez.
690   def test_upload_reuse_placeholder_valid
691     basic_authorization users(:public_user).email, "test"
692     changeset_id = changesets(:public_user_first_change).id
693
694     diff = <<EOF
695 <osmChange>
696  <create>
697   <node id='-1' lon='0' lat='0' changeset='#{changeset_id}'>
698    <tag k="foo" v="bar"/>
699   </node>
700  </create>
701  <modify>
702   <node id='-1' lon='1' lat='1' changeset='#{changeset_id}' version='1'/>
703  </modify>
704  <delete>
705   <node id='-1' lon='2' lat='2' changeset='#{changeset_id}' version='2'/>
706  </delete>
707 </osmChange>
708 EOF
709
710     # upload it
711     content diff
712     post :upload, :id => changeset_id
713     assert_response :success, 
714       "can't upload a valid diff with re-used placeholders to changeset: #{@response.body}"
715
716     # check the response is well-formed
717     assert_select "diffResult>node", 3
718     assert_select "diffResult>node[old_id=-1]", 3
719   end
720
721   ##
722   # test what happens if a diff upload re-uses placeholder IDs in an
723   # illegal way.
724   def test_upload_placeholder_invalid
725     basic_authorization users(:public_user).email, "test"
726     changeset_id = changesets(:public_user_first_change).id
727
728     diff = <<EOF
729 <osmChange>
730  <create>
731   <node id='-1' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
732   <node id='-1' lon='1' lat='1' changeset='#{changeset_id}' version='1'/>
733   <node id='-1' lon='2' lat='2' changeset='#{changeset_id}' version='2'/>
734  </create>
735 </osmChange>
736 EOF
737
738     # upload it
739     content diff
740     post :upload, :id => changeset_id
741     assert_response :bad_request, 
742       "shouldn't be able to re-use placeholder IDs"
743   end
744
745   ##
746   # test that uploading a way referencing invalid placeholders gives a 
747   # proper error, not a 500.
748   def test_upload_placeholder_invalid_way
749     basic_authorization users(:public_user).email, "test"
750     changeset_id = changesets(:public_user_first_change).id
751
752     diff = <<EOF
753 <osmChange>
754  <create>
755   <node id="-1" lon="0" lat="0" changeset="#{changeset_id}" version="1"/>
756   <node id="-2" lon="1" lat="1" changeset="#{changeset_id}" version="1"/>
757   <node id="-3" lon="2" lat="2" changeset="#{changeset_id}" version="1"/>
758   <way id="-1" changeset="#{changeset_id}" version="1">
759    <nd ref="-1"/>
760    <nd ref="-2"/>
761    <nd ref="-3"/>
762    <nd ref="-4"/>
763   </way>
764  </create>
765 </osmChange>
766 EOF
767
768     # upload it
769     content diff
770     post :upload, :id => changeset_id
771     assert_response :bad_request, 
772       "shouldn't be able to use invalid placeholder IDs"
773     assert_equal "Placeholder node not found for reference -4 in way -1", @response.body
774
775     # the same again, but this time use an existing way
776     diff = <<EOF
777 <osmChange>
778  <create>
779   <node id="-1" lon="0" lat="0" changeset="#{changeset_id}" version="1"/>
780   <node id="-2" lon="1" lat="1" changeset="#{changeset_id}" version="1"/>
781   <node id="-3" lon="2" lat="2" changeset="#{changeset_id}" version="1"/>
782   <way id="1" changeset="#{changeset_id}" version="1">
783    <nd ref="-1"/>
784    <nd ref="-2"/>
785    <nd ref="-3"/>
786    <nd ref="-4"/>
787   </way>
788  </create>
789 </osmChange>
790 EOF
791
792     # upload it
793     content diff
794     post :upload, :id => changeset_id
795     assert_response :bad_request, 
796       "shouldn't be able to use invalid placeholder IDs"
797     assert_equal "Placeholder node not found for reference -4 in way 1", @response.body
798   end
799
800   ##
801   # test that uploading a relation referencing invalid placeholders gives a 
802   # proper error, not a 500.
803   def test_upload_placeholder_invalid_relation
804     basic_authorization users(:public_user).email, "test"
805     changeset_id = changesets(:public_user_first_change).id
806
807     diff = <<EOF
808 <osmChange>
809  <create>
810   <node id="-1" lon="0" lat="0" changeset="#{changeset_id}" version="1"/>
811   <node id="-2" lon="1" lat="1" changeset="#{changeset_id}" version="1"/>
812   <node id="-3" lon="2" lat="2" changeset="#{changeset_id}" version="1"/>
813   <relation id="-1" changeset="#{changeset_id}" version="1">
814    <member type="node" role="foo" ref="-1"/>
815    <member type="node" role="foo" ref="-2"/>
816    <member type="node" role="foo" ref="-3"/>
817    <member type="node" role="foo" ref="-4"/>
818   </relation>
819  </create>
820 </osmChange>
821 EOF
822
823     # upload it
824     content diff
825     post :upload, :id => changeset_id
826     assert_response :bad_request, 
827       "shouldn't be able to use invalid placeholder IDs"
828     assert_equal "Placeholder Node not found for reference -4 in relation -1.", @response.body
829
830     # the same again, but this time use an existing way
831     diff = <<EOF
832 <osmChange>
833  <create>
834   <node id="-1" lon="0" lat="0" changeset="#{changeset_id}" version="1"/>
835   <node id="-2" lon="1" lat="1" changeset="#{changeset_id}" version="1"/>
836   <node id="-3" lon="2" lat="2" changeset="#{changeset_id}" version="1"/>
837   <relation id="1" changeset="#{changeset_id}" version="1">
838    <member type="node" role="foo" ref="-1"/>
839    <member type="node" role="foo" ref="-2"/>
840    <member type="node" role="foo" ref="-3"/>
841    <member type="way" role="bar" ref="-1"/>
842   </relation>
843  </create>
844 </osmChange>
845 EOF
846
847     # upload it
848     content diff
849     post :upload, :id => changeset_id
850     assert_response :bad_request, 
851       "shouldn't be able to use invalid placeholder IDs"
852     assert_equal "Placeholder Way not found for reference -1 in relation 1.", @response.body
853   end
854
855   ##
856   # test what happens if a diff is uploaded containing only a node
857   # move.
858   def test_upload_node_move
859     basic_authorization users(:public_user).email, "test"
860
861     content "<osm><changeset>" +
862       "<tag k='created_by' v='osm test suite checking changesets'/>" + 
863       "</changeset></osm>"
864     put :create
865     assert_response :success
866     changeset_id = @response.body.to_i
867
868     old_node = current_nodes(:visible_node)
869
870     diff = XML::Document.new
871     diff.root = XML::Node.new "osmChange"
872     modify = XML::Node.new "modify"
873     xml_old_node = old_node.to_xml_node
874     xml_old_node["lat"] = (2.0).to_s
875     xml_old_node["lon"] = (2.0).to_s
876     xml_old_node["changeset"] = changeset_id.to_s
877     modify << xml_old_node
878     diff.root << modify
879
880     # upload it
881     content diff
882     post :upload, :id => changeset_id
883     assert_response :success, 
884       "diff should have uploaded OK"
885
886     # check the bbox
887     changeset = Changeset.find(changeset_id)
888     assert_equal 1*SCALE, changeset.min_lon, "min_lon should be 1 degree"
889     assert_equal 2*SCALE, changeset.max_lon, "max_lon should be 2 degrees"
890     assert_equal 1*SCALE, changeset.min_lat, "min_lat should be 1 degree"
891     assert_equal 2*SCALE, changeset.max_lat, "max_lat should be 2 degrees"
892   end
893
894   ##
895   # test what happens if a diff is uploaded adding a node to a way.
896   def test_upload_way_extend
897     basic_authorization users(:public_user).email, "test"
898
899     content "<osm><changeset>" +
900       "<tag k='created_by' v='osm test suite checking changesets'/>" + 
901       "</changeset></osm>"
902     put :create
903     assert_response :success
904     changeset_id = @response.body.to_i
905
906     old_way = current_ways(:visible_way)
907
908     diff = XML::Document.new
909     diff.root = XML::Node.new "osmChange"
910     modify = XML::Node.new "modify"
911     xml_old_way = old_way.to_xml_node
912     nd_ref = XML::Node.new "nd"
913     nd_ref["ref"] = current_nodes(:visible_node).id.to_s
914     xml_old_way << nd_ref
915     xml_old_way["changeset"] = changeset_id.to_s
916     modify << xml_old_way
917     diff.root << modify
918
919     # upload it
920     content diff
921     post :upload, :id => changeset_id
922     assert_response :success, 
923       "diff should have uploaded OK"
924
925     # check the bbox
926     changeset = Changeset.find(changeset_id)
927     assert_equal 1*SCALE, changeset.min_lon, "min_lon should be 1 degree"
928     assert_equal 3*SCALE, changeset.max_lon, "max_lon should be 3 degrees"
929     assert_equal 1*SCALE, changeset.min_lat, "min_lat should be 1 degree"
930     assert_equal 3*SCALE, changeset.max_lat, "max_lat should be 3 degrees"
931   end
932
933   ##
934   # test for more issues in #1568
935   def test_upload_empty_invalid
936     basic_authorization users(:public_user).email, "test"
937
938     [ "<osmChange/>",
939       "<osmChange></osmChange>",
940       "<osmChange><modify/></osmChange>",
941       "<osmChange><modify></modify></osmChange>"
942     ].each do |diff|
943       # upload it
944       content diff
945       post :upload, :id => changesets(:public_user_first_change).id
946       assert_response(:success, "should be able to upload " +
947                       "empty changeset: " + diff)
948     end
949   end
950
951   ##
952   # when we make some simple changes we get the same changes back from the 
953   # diff download.
954   def test_diff_download_simple
955     ## First try with the normal user, which should get a forbidden
956     basic_authorization(users(:normal_user).email, "test")
957
958     # create a temporary changeset
959     content "<osm><changeset>" +
960       "<tag k='created_by' v='osm test suite checking changesets'/>" + 
961       "</changeset></osm>"
962     put :create
963     assert_response :forbidden
964     
965     
966     
967     ## Now try with the public user
968     basic_authorization(users(:public_user).email, "test")
969
970     # create a temporary changeset
971     content "<osm><changeset>" +
972       "<tag k='created_by' v='osm test suite checking changesets'/>" + 
973       "</changeset></osm>"
974     put :create
975     assert_response :success
976     changeset_id = @response.body.to_i
977
978     # add a diff to it
979     diff = <<EOF
980 <osmChange>
981  <modify>
982   <node id='1' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
983   <node id='1' lon='1' lat='0' changeset='#{changeset_id}' version='2'/>
984   <node id='1' lon='1' lat='1' changeset='#{changeset_id}' version='3'/>
985   <node id='1' lon='1' lat='2' changeset='#{changeset_id}' version='4'/>
986   <node id='1' lon='2' lat='2' changeset='#{changeset_id}' version='5'/>
987   <node id='1' lon='3' lat='2' changeset='#{changeset_id}' version='6'/>
988   <node id='1' lon='3' lat='3' changeset='#{changeset_id}' version='7'/>
989   <node id='1' lon='9' lat='9' changeset='#{changeset_id}' version='8'/>
990  </modify>
991 </osmChange>
992 EOF
993
994     # upload it
995     content diff
996     post :upload, :id => changeset_id
997     assert_response :success, 
998       "can't upload multiple versions of an element in a diff: #{@response.body}"
999     
1000     get :download, :id => changeset_id
1001     assert_response :success
1002
1003     assert_select "osmChange", 1
1004     assert_select "osmChange>modify", 8
1005     assert_select "osmChange>modify>node", 8
1006   end
1007   
1008   ##
1009   # culled this from josm to ensure that nothing in the way that josm
1010   # is formatting the request is causing it to fail.
1011   #
1012   # NOTE: the error turned out to be something else completely!
1013   def test_josm_upload
1014     basic_authorization(users(:public_user).email, "test")
1015
1016     # create a temporary changeset
1017     content "<osm><changeset>" +
1018       "<tag k='created_by' v='osm test suite checking changesets'/>" + 
1019       "</changeset></osm>"
1020     put :create
1021     assert_response :success
1022     changeset_id = @response.body.to_i
1023
1024     diff = <<OSMFILE
1025 <osmChange version="0.6" generator="JOSM">
1026 <create version="0.6" generator="JOSM">
1027   <node id='-1' visible='true' changeset='#{changeset_id}' lat='51.49619982187321' lon='-0.18722061869438314' />
1028   <node id='-2' visible='true' changeset='#{changeset_id}' lat='51.496359883909605' lon='-0.18653093576241928' />
1029   <node id='-3' visible='true' changeset='#{changeset_id}' lat='51.49598132358285' lon='-0.18719613290981638' />
1030   <node id='-4' visible='true' changeset='#{changeset_id}' lat='51.4961591711078' lon='-0.18629015888084607' />
1031   <node id='-5' visible='true' changeset='#{changeset_id}' lat='51.49582126021711' lon='-0.18708186591517145' />
1032   <node id='-6' visible='true' changeset='#{changeset_id}' lat='51.49591018437858' lon='-0.1861432441734455' />
1033   <node id='-7' visible='true' changeset='#{changeset_id}' lat='51.49560784152179' lon='-0.18694719410005425' />
1034   <node id='-8' visible='true' changeset='#{changeset_id}' lat='51.49567389979617' lon='-0.1860289771788006' />
1035   <node id='-9' visible='true' changeset='#{changeset_id}' lat='51.49543761398892' lon='-0.186820684213126' />
1036   <way id='-10' action='modiy' visible='true' changeset='#{changeset_id}'>
1037     <nd ref='-1' />
1038     <nd ref='-2' />
1039     <nd ref='-3' />
1040     <nd ref='-4' />
1041     <nd ref='-5' />
1042     <nd ref='-6' />
1043     <nd ref='-7' />
1044     <nd ref='-8' />
1045     <nd ref='-9' />
1046     <tag k='highway' v='residential' />
1047     <tag k='name' v='Foobar Street' />
1048   </way>
1049 </create>
1050 </osmChange>
1051 OSMFILE
1052
1053     # upload it
1054     content diff
1055     post :upload, :id => changeset_id
1056     assert_response :success, 
1057       "can't upload a diff from JOSM: #{@response.body}"
1058     
1059     get :download, :id => changeset_id
1060     assert_response :success
1061
1062     assert_select "osmChange", 1
1063     assert_select "osmChange>create>node", 9
1064     assert_select "osmChange>create>way", 1
1065     assert_select "osmChange>create>way>nd", 9
1066     assert_select "osmChange>create>way>tag", 2
1067   end
1068
1069   ##
1070   # when we make some complex changes we get the same changes back from the 
1071   # diff download.
1072   def test_diff_download_complex
1073     basic_authorization(users(:public_user).email, "test")
1074
1075     # create a temporary changeset
1076     content "<osm><changeset>" +
1077       "<tag k='created_by' v='osm test suite checking changesets'/>" + 
1078       "</changeset></osm>"
1079     put :create
1080     assert_response :success
1081     changeset_id = @response.body.to_i
1082
1083     # add a diff to it
1084     diff = <<EOF
1085 <osmChange>
1086  <delete>
1087   <node id='1' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
1088  </delete>
1089  <create>
1090   <node id='-1' lon='9' lat='9' changeset='#{changeset_id}' version='0'/>
1091   <node id='-2' lon='8' lat='9' changeset='#{changeset_id}' version='0'/>
1092   <node id='-3' lon='7' lat='9' changeset='#{changeset_id}' version='0'/>
1093  </create>
1094  <modify>
1095   <node id='3' lon='20' lat='15' changeset='#{changeset_id}' version='1'/>
1096   <way id='1' changeset='#{changeset_id}' version='1'>
1097    <nd ref='3'/>
1098    <nd ref='-1'/>
1099    <nd ref='-2'/>
1100    <nd ref='-3'/>
1101   </way>
1102  </modify>
1103 </osmChange>
1104 EOF
1105
1106     # upload it
1107     content diff
1108     post :upload, :id => changeset_id
1109     assert_response :success, 
1110       "can't upload multiple versions of an element in a diff: #{@response.body}"
1111     
1112     get :download, :id => changeset_id
1113     assert_response :success
1114
1115     assert_select "osmChange", 1
1116     assert_select "osmChange>create", 3
1117     assert_select "osmChange>delete", 1
1118     assert_select "osmChange>modify", 2
1119     assert_select "osmChange>create>node", 3
1120     assert_select "osmChange>delete>node", 1 
1121     assert_select "osmChange>modify>node", 1
1122     assert_select "osmChange>modify>way", 1
1123   end
1124
1125   ##
1126   # check that the bounding box of a changeset gets updated correctly
1127   ## FIXME: This should really be moded to a integration test due to the with_controller
1128   def test_changeset_bbox
1129     basic_authorization users(:public_user).email, "test"
1130
1131     # create a new changeset
1132     content "<osm><changeset/></osm>"
1133     put :create
1134     assert_response :success, "Creating of changeset failed."
1135     changeset_id = @response.body.to_i
1136     
1137     # add a single node to it
1138     with_controller(NodeController.new) do
1139       content "<osm><node lon='1' lat='2' changeset='#{changeset_id}'/></osm>"
1140       put :create
1141       assert_response :success, "Couldn't create node."
1142     end
1143
1144     # get the bounding box back from the changeset
1145     get :read, :id => changeset_id
1146     assert_response :success, "Couldn't read back changeset."
1147     assert_select "osm>changeset[min_lon=1.0]", 1
1148     assert_select "osm>changeset[max_lon=1.0]", 1
1149     assert_select "osm>changeset[min_lat=2.0]", 1
1150     assert_select "osm>changeset[max_lat=2.0]", 1
1151
1152     # add another node to it
1153     with_controller(NodeController.new) do
1154       content "<osm><node lon='2' lat='1' changeset='#{changeset_id}'/></osm>"
1155       put :create
1156       assert_response :success, "Couldn't create second node."
1157     end
1158
1159     # get the bounding box back from the changeset
1160     get :read, :id => changeset_id
1161     assert_response :success, "Couldn't read back changeset for the second time."
1162     assert_select "osm>changeset[min_lon=1.0]", 1
1163     assert_select "osm>changeset[max_lon=2.0]", 1
1164     assert_select "osm>changeset[min_lat=1.0]", 1
1165     assert_select "osm>changeset[max_lat=2.0]", 1
1166
1167     # add (delete) a way to it, which contains a point at (3,3)
1168     with_controller(WayController.new) do
1169       content update_changeset(current_ways(:visible_way).to_xml,
1170                                changeset_id)
1171       put :delete, :id => current_ways(:visible_way).id
1172       assert_response :success, "Couldn't delete a way."
1173     end
1174
1175     # get the bounding box back from the changeset
1176     get :read, :id => changeset_id
1177     assert_response :success, "Couldn't read back changeset for the third time."
1178     # note that the 3.1 here is because of the bbox overexpansion
1179     assert_select "osm>changeset[min_lon=1.0]", 1
1180     assert_select "osm>changeset[max_lon=3.1]", 1
1181     assert_select "osm>changeset[min_lat=1.0]", 1
1182     assert_select "osm>changeset[max_lat=3.1]", 1    
1183   end
1184
1185   ##
1186   # test that the changeset :include method works as it should
1187   def test_changeset_include
1188     basic_authorization users(:public_user).display_name, "test"
1189
1190     # create a new changeset
1191     content "<osm><changeset/></osm>"
1192     put :create
1193     assert_response :success, "Creating of changeset failed."
1194     changeset_id = @response.body.to_i
1195
1196     # NOTE: the include method doesn't over-expand, like inserting
1197     # a real method does. this is because we expect the client to 
1198     # know what it is doing!
1199     check_after_include(changeset_id,  1,  1, [ 1,  1,  1,  1])
1200     check_after_include(changeset_id,  3,  3, [ 1,  1,  3,  3])
1201     check_after_include(changeset_id,  4,  2, [ 1,  1,  4,  3])
1202     check_after_include(changeset_id,  2,  2, [ 1,  1,  4,  3])
1203     check_after_include(changeset_id, -1, -1, [-1, -1,  4,  3])
1204     check_after_include(changeset_id, -2,  5, [-2, -1,  4,  5])
1205   end
1206
1207   ##
1208   # test the query functionality of changesets
1209   def test_query
1210     get :query, :bbox => "-10,-10, 10, 10"
1211     assert_response :success, "can't get changesets in bbox"
1212     assert_changesets [1,4,6]
1213
1214     get :query, :bbox => "4.5,4.5,4.6,4.6"
1215     assert_response :success, "can't get changesets in bbox"
1216     assert_changesets [1]
1217
1218     # can't get changesets of user 1 without authenticating
1219     get :query, :user => users(:normal_user).id
1220     assert_response :not_found, "shouldn't be able to get changesets by non-public user"
1221
1222     # but this should work
1223     basic_authorization "test@openstreetmap.org", "test"
1224     get :query, :user => users(:normal_user).id
1225     assert_response :success, "can't get changesets by user"
1226     assert_changesets [1,3,6]
1227
1228     get :query, :user => users(:normal_user).id, :open => true
1229     assert_response :success, "can't get changesets by user and open"
1230     assert_changesets [1]
1231
1232     get :query, :time => '2007-12-31'
1233     assert_response :success, "can't get changesets by time-since"
1234     assert_changesets [1,2,4,5,6]
1235
1236     get :query, :time => '2008-01-01T12:34Z'
1237     assert_response :success, "can't get changesets by time-since with hour"
1238     assert_changesets [1,2,4,5,6]
1239
1240     get :query, :time => '2007-12-31T23:59Z,2008-01-01T00:01Z'
1241     assert_response :success, "can't get changesets by time-range"
1242     assert_changesets [1,4,5,6]
1243
1244     get :query, :open => 'true'
1245     assert_response :success, "can't get changesets by open-ness"
1246     assert_changesets [1,2,4]
1247   end
1248
1249   ##
1250   # check that errors are returned if garbage is inserted 
1251   # into query strings
1252   def test_query_invalid
1253     [ "abracadabra!",
1254       "1,2,3,F",
1255       ";drop table users;"
1256       ].each do |bbox|
1257       get :query, :bbox => bbox
1258       assert_response :bad_request, "'#{bbox}' isn't a bbox"
1259     end
1260
1261     [ "now()",
1262       "00-00-00",
1263       ";drop table users;",
1264       ",",
1265       "-,-"
1266       ].each do |time|
1267       get :query, :time => time
1268       assert_response :bad_request, "'#{time}' isn't a valid time range"
1269     end
1270
1271     [ "me",
1272       "foobar",
1273       "-1",
1274       "0"
1275       ].each do |uid|
1276       get :query, :user => uid
1277       assert_response :bad_request, "'#{uid}' isn't a valid user ID"
1278     end
1279   end
1280
1281   ##
1282   # check updating tags on a changeset
1283   def test_changeset_update
1284     ## First try with the non-public user
1285     changeset = changesets(:normal_user_first_change)
1286     new_changeset = changeset.to_xml
1287     new_tag = XML::Node.new "tag"
1288     new_tag['k'] = "tagtesting"
1289     new_tag['v'] = "valuetesting"
1290     new_changeset.find("//osm/changeset").first << new_tag
1291     content new_changeset
1292
1293     # try without any authorization
1294     put :update, :id => changeset.id
1295     assert_response :unauthorized
1296
1297     # try with the wrong authorization
1298     basic_authorization users(:public_user).email, "test"
1299     put :update, :id => changeset.id
1300     assert_response :conflict
1301
1302     # now this should get an unauthorized
1303     basic_authorization users(:normal_user).email, "test"
1304     put :update, :id => changeset.id
1305     assert_require_public_data "user with their data non-public, shouldn't be able to edit their changeset"
1306     
1307     
1308     ## Now try with the public user
1309     changeset = changesets(:public_user_first_change)
1310     new_changeset = changeset.to_xml
1311     new_tag = XML::Node.new "tag"
1312     new_tag['k'] = "tagtesting"
1313     new_tag['v'] = "valuetesting"
1314     new_changeset.find("//osm/changeset").first << new_tag
1315     content new_changeset
1316     
1317     # try without any authorization
1318     @request.env["HTTP_AUTHORIZATION"] = nil
1319     put :update, :id => changeset.id
1320     assert_response :unauthorized
1321
1322     # try with the wrong authorization
1323     basic_authorization users(:second_public_user).email, "test"
1324     put :update, :id => changeset.id
1325     assert_response :conflict
1326
1327     # now this should work...
1328     basic_authorization users(:public_user).email, "test"
1329     put :update, :id => changeset.id
1330     assert_response :success
1331
1332     assert_select "osm>changeset[id=#{changeset.id}]", 1
1333     assert_select "osm>changeset>tag", 2
1334     assert_select "osm>changeset>tag[k=tagtesting][v=valuetesting]", 1
1335   end
1336   
1337   ##
1338   # check that a user different from the one who opened the changeset
1339   # can't modify it.
1340   def test_changeset_update_invalid
1341     basic_authorization users(:public_user).email, "test"
1342
1343     changeset = changesets(:normal_user_first_change)
1344     new_changeset = changeset.to_xml
1345     new_tag = XML::Node.new "tag"
1346     new_tag['k'] = "testing"
1347     new_tag['v'] = "testing"
1348     new_changeset.find("//osm/changeset").first << new_tag
1349
1350     content new_changeset
1351     put :update, :id => changeset.id
1352     assert_response :conflict
1353   end
1354
1355   ##
1356   # check that a changeset can contain a certain max number of changes.
1357   ## FIXME should be changed to an integration test due to the with_controller
1358   def test_changeset_limits
1359     basic_authorization users(:public_user).email, "test"
1360
1361     # open a new changeset
1362     content "<osm><changeset/></osm>"
1363     put :create
1364     assert_response :success, "can't create a new changeset"
1365     cs_id = @response.body.to_i
1366
1367     # start the counter just short of where the changeset should finish.
1368     offset = 10
1369     # alter the database to set the counter on the changeset directly, 
1370     # otherwise it takes about 6 minutes to fill all of them.
1371     changeset = Changeset.find(cs_id)
1372     changeset.num_changes = Changeset::MAX_ELEMENTS - offset
1373     changeset.save!
1374
1375     with_controller(NodeController.new) do
1376       # create a new node
1377       content "<osm><node changeset='#{cs_id}' lat='0.0' lon='0.0'/></osm>"
1378       put :create
1379       assert_response :success, "can't create a new node"
1380       node_id = @response.body.to_i
1381
1382       get :read, :id => node_id
1383       assert_response :success, "can't read back new node"
1384       node_doc = XML::Parser.string(@response.body).parse
1385       node_xml = node_doc.find("//osm/node").first
1386
1387       # loop until we fill the changeset with nodes
1388       offset.times do |i|
1389         node_xml['lat'] = rand.to_s
1390         node_xml['lon'] = rand.to_s
1391         node_xml['version'] = (i+1).to_s
1392
1393         content node_doc
1394         put :update, :id => node_id
1395         assert_response :success, "attempt #{i} should have succeeded"
1396       end
1397
1398       # trying again should fail
1399       node_xml['lat'] = rand.to_s
1400       node_xml['lon'] = rand.to_s
1401       node_xml['version'] = offset.to_s
1402       
1403       content node_doc
1404       put :update, :id => node_id
1405       assert_response :conflict, "final attempt should have failed"
1406     end
1407
1408     changeset = Changeset.find(cs_id)
1409     assert_equal Changeset::MAX_ELEMENTS + 1, changeset.num_changes
1410
1411     # check that the changeset is now closed as well
1412     assert(!changeset.is_open?, 
1413            "changeset should have been auto-closed by exceeding " + 
1414            "element limit.")
1415   end
1416   
1417   # This should display the last 20 changesets closed.
1418   def test_list
1419     @changesets = Changeset.find(:all, :order => "created_at DESC", :conditions => ['min_lat IS NOT NULL'], :limit=> 20)
1420     assert @changesets.size <= 20
1421     get :list
1422     assert_response :success
1423     assert_template "list"
1424     # Now check that all 20 (or however many were returned) changesets are in the html
1425     assert_select "h1", :text => "Recent Changes", :count => 1
1426     assert_select "table[id='changeset_list'] tr", :count => @changesets.size + 1
1427     @changesets.each do |changeset|
1428       # FIXME this test needs rewriting - test for table contents
1429     end
1430   end
1431   
1432   #------------------------------------------------------------
1433   # utility functions
1434   #------------------------------------------------------------
1435
1436   ##
1437   # boilerplate for checking that certain changesets exist in the
1438   # output.
1439   def assert_changesets(ids)
1440     assert_select "osm>changeset", ids.size
1441     ids.each do |id|
1442       assert_select "osm>changeset[id=#{id}]", 1
1443     end
1444   end
1445
1446   ##
1447   # call the include method and assert properties of the bbox
1448   def check_after_include(changeset_id, lon, lat, bbox)
1449     content "<osm><node lon='#{lon}' lat='#{lat}'/></osm>"
1450     post :expand_bbox, :id => changeset_id
1451     assert_response :success, "Setting include of changeset failed: #{@response.body}"
1452
1453     # check exactly one changeset
1454     assert_select "osm>changeset", 1
1455     assert_select "osm>changeset[id=#{changeset_id}]", 1
1456
1457     # check the bbox
1458     doc = XML::Parser.string(@response.body).parse
1459     changeset = doc.find("//osm/changeset").first
1460     assert_equal bbox[0], changeset['min_lon'].to_f, "min lon"
1461     assert_equal bbox[1], changeset['min_lat'].to_f, "min lat"
1462     assert_equal bbox[2], changeset['max_lon'].to_f, "max lon"
1463     assert_equal bbox[3], changeset['max_lat'].to_f, "max lat"
1464   end
1465
1466   ##
1467   # update the changeset_id of a way element
1468   def update_changeset(xml, changeset_id)
1469     xml_attr_rewrite(xml, 'changeset', changeset_id)
1470   end
1471
1472   ##
1473   # update an attribute in a way element
1474   def xml_attr_rewrite(xml, name, value)
1475     xml.find("//osm/way").first[name] = value.to_s
1476     return xml
1477   end
1478
1479 end