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