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