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