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