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