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