]> git.openstreetmap.org Git - rails.git/blob - test/controllers/changeset_controller_test.rb
use a controller method to handle cancan denials
[rails.git] / test / controllers / changeset_controller_test.rb
1 require "test_helper"
2 require "changeset_controller"
3
4 class ChangesetControllerTest < ActionController::TestCase
5   ##
6   # test all routes which lead to this controller
7   def test_routes
8     assert_routing(
9       { :path => "/api/0.6/changeset/create", :method => :put },
10       { :controller => "changeset", :action => "create" }
11     )
12     assert_routing(
13       { :path => "/api/0.6/changeset/1/upload", :method => :post },
14       { :controller => "changeset", :action => "upload", :id => "1" }
15     )
16     assert_routing(
17       { :path => "/api/0.6/changeset/1/download", :method => :get },
18       { :controller => "changeset", :action => "download", :id => "1" }
19     )
20     assert_routing(
21       { :path => "/api/0.6/changeset/1/expand_bbox", :method => :post },
22       { :controller => "changeset", :action => "expand_bbox", :id => "1" }
23     )
24     assert_routing(
25       { :path => "/api/0.6/changeset/1", :method => :get },
26       { :controller => "changeset", :action => "read", :id => "1" }
27     )
28     assert_routing(
29       { :path => "/api/0.6/changeset/1/subscribe", :method => :post },
30       { :controller => "changeset", :action => "subscribe", :id => "1" }
31     )
32     assert_routing(
33       { :path => "/api/0.6/changeset/1/unsubscribe", :method => :post },
34       { :controller => "changeset", :action => "unsubscribe", :id => "1" }
35     )
36     assert_routing(
37       { :path => "/api/0.6/changeset/1", :method => :put },
38       { :controller => "changeset", :action => "update", :id => "1" }
39     )
40     assert_routing(
41       { :path => "/api/0.6/changeset/1/close", :method => :put },
42       { :controller => "changeset", :action => "close", :id => "1" }
43     )
44     assert_routing(
45       { :path => "/api/0.6/changeset/1/comment", :method => :post },
46       { :controller => "changeset", :action => "comment", :id => "1" }
47     )
48     assert_routing(
49       { :path => "/api/0.6/changeset/comment/1/hide", :method => :post },
50       { :controller => "changeset", :action => "hide_comment", :id => "1" }
51     )
52     assert_routing(
53       { :path => "/api/0.6/changeset/comment/1/unhide", :method => :post },
54       { :controller => "changeset", :action => "unhide_comment", :id => "1" }
55     )
56     assert_routing(
57       { :path => "/api/0.6/changesets", :method => :get },
58       { :controller => "changeset", :action => "query" }
59     )
60     assert_routing(
61       { :path => "/changeset/1/comments/feed", :method => :get },
62       { :controller => "changeset", :action => "comments_feed", :id => "1", :format => "rss" }
63     )
64     assert_routing(
65       { :path => "/user/name/history", :method => :get },
66       { :controller => "changeset", :action => "list", :display_name => "name" }
67     )
68     assert_routing(
69       { :path => "/user/name/history/feed", :method => :get },
70       { :controller => "changeset", :action => "feed", :display_name => "name", :format => :atom }
71     )
72     assert_routing(
73       { :path => "/history/friends", :method => :get },
74       { :controller => "changeset", :action => "list", :friends => true, :format => :html }
75     )
76     assert_routing(
77       { :path => "/history/nearby", :method => :get },
78       { :controller => "changeset", :action => "list", :nearby => true, :format => :html }
79     )
80     assert_routing(
81       { :path => "/history", :method => :get },
82       { :controller => "changeset", :action => "list" }
83     )
84     assert_routing(
85       { :path => "/history/feed", :method => :get },
86       { :controller => "changeset", :action => "feed", :format => :atom }
87     )
88     assert_routing(
89       { :path => "/history/comments/feed", :method => :get },
90       { :controller => "changeset", :action => "comments_feed", :format => "rss" }
91     )
92   end
93
94   # -----------------------
95   # Test simple changeset creation
96   # -----------------------
97
98   def test_create
99     basic_authorization create(:user, :data_public => false).email, "test"
100     # Create the first user's changeset
101     content "<osm><changeset>" \
102             "<tag k='created_by' v='osm test suite checking changesets'/>" \
103             "</changeset></osm>"
104     put :create
105     assert_require_public_data
106
107     basic_authorization create(:user).email, "test"
108     # Create the first user's changeset
109     content "<osm><changeset>" \
110             "<tag k='created_by' v='osm test suite checking changesets'/>" \
111             "</changeset></osm>"
112     put :create
113
114     assert_response :success, "Creation of changeset did not return sucess status"
115     newid = @response.body.to_i
116
117     # check end time, should be an hour ahead of creation time
118     cs = Changeset.find(newid)
119     duration = cs.closed_at - cs.created_at
120     # the difference can either be a rational, or a floating point number
121     # of seconds, depending on the code path taken :-(
122     if duration.class == Rational
123       assert_equal Rational(1, 24), duration, "initial idle timeout should be an hour (#{cs.created_at} -> #{cs.closed_at})"
124     else
125       # must be number of seconds...
126       assert_equal 3600, duration.round, "initial idle timeout should be an hour (#{cs.created_at} -> #{cs.closed_at})"
127     end
128
129     # checks if uploader was subscribed
130     assert_equal 1, cs.subscribers.length
131   end
132
133   def test_create_invalid
134     basic_authorization create(:user, :data_public => false).email, "test"
135     content "<osm><changeset></osm>"
136     put :create
137     assert_require_public_data
138
139     ## Try the public user
140     basic_authorization create(:user).email, "test"
141     content "<osm><changeset></osm>"
142     put :create
143     assert_response :bad_request, "creating a invalid changeset should fail"
144   end
145
146   def test_create_invalid_no_content
147     ## First check with no auth
148     put :create
149     assert_response :unauthorized, "shouldn't be able to create a changeset with no auth"
150
151     ## Now try to with a non-public user
152     basic_authorization create(:user, :data_public => false).email, "test"
153     put :create
154     assert_require_public_data
155
156     ## Try an inactive user
157     basic_authorization create(:user, :pending).email, "test"
158     put :create
159     assert_inactive_user
160
161     ## Now try to use a normal user
162     basic_authorization create(:user).email, "test"
163     put :create
164     assert_response :bad_request, "creating a changeset with no content should fail"
165   end
166
167   def test_create_wrong_method
168     basic_authorization create(:user).email, "test"
169     get :create
170     assert_response :method_not_allowed
171     post :create
172     assert_response :method_not_allowed
173   end
174
175   ##
176   # check that the changeset can be read and returns the correct
177   # document structure.
178   def test_read
179     changeset_id = create(:changeset).id
180
181     get :read, :params => { :id => changeset_id }
182     assert_response :success, "cannot get first changeset"
183
184     assert_select "osm[version='#{API_VERSION}'][generator='OpenStreetMap server']", 1
185     assert_select "osm>changeset[id='#{changeset_id}']", 1
186     assert_select "osm>changeset>discussion", 0
187
188     get :read, :params => { :id => changeset_id, :include_discussion => true }
189     assert_response :success, "cannot get first changeset with comments"
190
191     assert_select "osm[version='#{API_VERSION}'][generator='OpenStreetMap server']", 1
192     assert_select "osm>changeset[id='#{changeset_id}']", 1
193     assert_select "osm>changeset>discussion", 1
194     assert_select "osm>changeset>discussion>comment", 0
195
196     changeset_id = create(:changeset, :closed).id
197     create_list(:changeset_comment, 3, :changeset_id => changeset_id)
198
199     get :read, :params => { :id => changeset_id, :include_discussion => true }
200     assert_response :success, "cannot get closed changeset with comments"
201
202     assert_select "osm[version='#{API_VERSION}'][generator='OpenStreetMap server']", 1
203     assert_select "osm>changeset[id='#{changeset_id}']", 1
204     assert_select "osm>changeset>discussion", 1
205     assert_select "osm>changeset>discussion>comment", 3
206   end
207
208   ##
209   # check that a changeset that doesn't exist returns an appropriate message
210   def test_read_not_found
211     [0, -32, 233455644, "afg", "213"].each do |id|
212       begin
213         get :read, :params => { :id => id }
214         assert_response :not_found, "should get a not found"
215       rescue ActionController::UrlGenerationError => ex
216         assert_match /No route matches/, ex.to_s
217       end
218     end
219   end
220
221   ##
222   # test that the user who opened a change can close it
223   def test_close
224     private_user = create(:user, :data_public => false)
225     private_changeset = create(:changeset, :user => private_user)
226     user = create(:user)
227     changeset = create(:changeset, :user => user)
228
229     ## Try without authentication
230     put :close, :params => { :id => changeset.id }
231     assert_response :unauthorized
232
233     ## Try using the non-public user
234     basic_authorization private_user.email, "test"
235     put :close, :params => { :id => private_changeset.id }
236     assert_require_public_data
237
238     ## The try with the public user
239     basic_authorization user.email, "test"
240
241     cs_id = changeset.id
242     put :close, :params => { :id => cs_id }
243     assert_response :success
244
245     # test that it really is closed now
246     cs = Changeset.find(cs_id)
247     assert(!cs.is_open?,
248            "changeset should be closed now (#{cs.closed_at} > #{Time.now.getutc}.")
249   end
250
251   ##
252   # test that a different user can't close another user's changeset
253   def test_close_invalid
254     user = create(:user)
255     changeset = create(:changeset)
256
257     basic_authorization user.email, "test"
258
259     put :close, :params => { :id => changeset.id }
260     assert_response :conflict
261     assert_equal "The user doesn't own that changeset", @response.body
262   end
263
264   ##
265   # test that you can't close using another method
266   def test_close_method_invalid
267     user = create(:user)
268     changeset = create(:changeset, :user => user)
269
270     basic_authorization user.email, "test"
271
272     get :close, :params => { :id => changeset.id }
273     assert_response :method_not_allowed
274
275     post :close, :params => { :id => changeset.id }
276     assert_response :method_not_allowed
277   end
278
279   ##
280   # check that you can't close a changeset that isn't found
281   def test_close_not_found
282     cs_ids = [0, -132, "123"]
283
284     # First try to do it with no auth
285     cs_ids.each do |id|
286       begin
287         put :close, :params => { :id => id }
288         assert_response :unauthorized, "Shouldn't be able close the non-existant changeset #{id}, when not authorized"
289       rescue ActionController::UrlGenerationError => ex
290         assert_match /No route matches/, ex.to_s
291       end
292     end
293
294     # Now try with auth
295     basic_authorization create(:user).email, "test"
296     cs_ids.each do |id|
297       begin
298         put :close, :params => { :id => id }
299         assert_response :not_found, "The changeset #{id} doesn't exist, so can't be closed"
300       rescue ActionController::UrlGenerationError => ex
301         assert_match /No route matches/, ex.to_s
302       end
303     end
304   end
305
306   ##
307   # upload something simple, but valid and check that it can
308   # be read back ok
309   # Also try without auth and another user.
310   def test_upload_simple_valid
311     private_user = create(:user, :data_public => false)
312     private_changeset = create(:changeset, :user => private_user)
313     user = create(:user)
314     changeset = create(:changeset, :user => user)
315
316     node = create(:node)
317     way = create(:way)
318     relation = create(:relation)
319     other_relation = create(:relation)
320     # create some tags, since we test that they are removed later
321     create(:node_tag, :node => node)
322     create(:way_tag, :way => way)
323     create(:relation_tag, :relation => relation)
324
325     ## Try with no auth
326     changeset_id = changeset.id
327
328     # simple diff to change a node, way and relation by removing
329     # their tags
330     diff = <<CHANGESET.strip_heredoc
331       <osmChange>
332        <modify>
333         <node id='#{node.id}' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
334         <way id='#{way.id}' changeset='#{changeset_id}' version='1'>
335          <nd ref='#{node.id}'/>
336         </way>
337        </modify>
338        <modify>
339         <relation id='#{relation.id}' changeset='#{changeset_id}' version='1'>
340          <member type='way' role='some' ref='#{way.id}'/>
341          <member type='node' role='some' ref='#{node.id}'/>
342          <member type='relation' role='some' ref='#{other_relation.id}'/>
343         </relation>
344        </modify>
345       </osmChange>
346 CHANGESET
347
348     # upload it
349     content diff
350     post :upload, :params => { :id => changeset_id }
351     assert_response :unauthorized,
352                     "shouldn't be able to upload a simple valid diff to changeset: #{@response.body}"
353
354     ## Now try with a private user
355     basic_authorization private_user.email, "test"
356     changeset_id = private_changeset.id
357
358     # simple diff to change a node, way and relation by removing
359     # their tags
360     diff = <<CHANGESET.strip_heredoc
361       <osmChange>
362        <modify>
363         <node id='#{node.id}' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
364         <way id='#{way.id}' changeset='#{changeset_id}' version='1'>
365          <nd ref='#{node.id}'/>
366         </way>
367        </modify>
368        <modify>
369         <relation id='#{relation.id}' changeset='#{changeset_id}' version='1'>
370          <member type='way' role='some' ref='#{way.id}'/>
371          <member type='node' role='some' ref='#{node.id}'/>
372          <member type='relation' role='some' ref='#{other_relation.id}'/>
373         </relation>
374        </modify>
375       </osmChange>
376 CHANGESET
377
378     # upload it
379     content diff
380     post :upload, :params => { :id => changeset_id }
381     assert_response :forbidden,
382                     "can't upload a simple valid diff to changeset: #{@response.body}"
383
384     ## Now try with the public user
385     basic_authorization user.email, "test"
386     changeset_id = changeset.id
387
388     # simple diff to change a node, way and relation by removing
389     # their tags
390     diff = <<CHANGESET.strip_heredoc
391       <osmChange>
392        <modify>
393         <node id='#{node.id}' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
394         <way id='#{way.id}' changeset='#{changeset_id}' version='1'>
395          <nd ref='#{node.id}'/>
396         </way>
397        </modify>
398        <modify>
399         <relation id='#{relation.id}' changeset='#{changeset_id}' version='1'>
400          <member type='way' role='some' ref='#{way.id}'/>
401          <member type='node' role='some' ref='#{node.id}'/>
402          <member type='relation' role='some' ref='#{other_relation.id}'/>
403         </relation>
404        </modify>
405       </osmChange>
406 CHANGESET
407
408     # upload it
409     content diff
410     post :upload, :params => { :id => changeset_id }
411     assert_response :success,
412                     "can't upload a simple valid diff to changeset: #{@response.body}"
413
414     # check that the changes made it into the database
415     assert_equal 0, Node.find(node.id).tags.size, "node #{node.id} should now have no tags"
416     assert_equal 0, Way.find(way.id).tags.size, "way #{way.id} should now have no tags"
417     assert_equal 0, Relation.find(relation.id).tags.size, "relation #{relation.id} should now have no tags"
418   end
419
420   ##
421   # upload something which creates new objects using placeholders
422   def test_upload_create_valid
423     user = create(:user)
424     changeset = create(:changeset, :user => user)
425     node = create(:node)
426     way = create(:way_with_nodes, :nodes_count => 2)
427     relation = create(:relation)
428
429     basic_authorization user.email, "test"
430
431     # simple diff to create a node way and relation using placeholders
432     diff = <<CHANGESET.strip_heredoc
433       <osmChange>
434        <create>
435         <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
436          <tag k='foo' v='bar'/>
437          <tag k='baz' v='bat'/>
438         </node>
439         <way id='-1' changeset='#{changeset.id}'>
440          <nd ref='#{node.id}'/>
441         </way>
442        </create>
443        <create>
444         <relation id='-1' changeset='#{changeset.id}'>
445          <member type='way' role='some' ref='#{way.id}'/>
446          <member type='node' role='some' ref='#{node.id}'/>
447          <member type='relation' role='some' ref='#{relation.id}'/>
448         </relation>
449        </create>
450       </osmChange>
451 CHANGESET
452
453     # upload it
454     content diff
455     post :upload, :params => { :id => changeset.id }
456     assert_response :success,
457                     "can't upload a simple valid creation to changeset: #{@response.body}"
458
459     # check the returned payload
460     assert_select "diffResult[version='#{API_VERSION}'][generator='OpenStreetMap server']", 1
461     assert_select "diffResult>node", 1
462     assert_select "diffResult>way", 1
463     assert_select "diffResult>relation", 1
464
465     # inspect the response to find out what the new element IDs are
466     doc = XML::Parser.string(@response.body).parse
467     new_node_id = doc.find("//diffResult/node").first["new_id"].to_i
468     new_way_id = doc.find("//diffResult/way").first["new_id"].to_i
469     new_rel_id = doc.find("//diffResult/relation").first["new_id"].to_i
470
471     # check the old IDs are all present and negative one
472     assert_equal -1, doc.find("//diffResult/node").first["old_id"].to_i
473     assert_equal -1, doc.find("//diffResult/way").first["old_id"].to_i
474     assert_equal -1, doc.find("//diffResult/relation").first["old_id"].to_i
475
476     # check the versions are present and equal one
477     assert_equal 1, doc.find("//diffResult/node").first["new_version"].to_i
478     assert_equal 1, doc.find("//diffResult/way").first["new_version"].to_i
479     assert_equal 1, doc.find("//diffResult/relation").first["new_version"].to_i
480
481     # check that the changes made it into the database
482     assert_equal 2, Node.find(new_node_id).tags.size, "new node should have two tags"
483     assert_equal 0, Way.find(new_way_id).tags.size, "new way should have no tags"
484     assert_equal 0, Relation.find(new_rel_id).tags.size, "new relation should have no tags"
485   end
486
487   ##
488   # test a complex delete where we delete elements which rely on eachother
489   # in the same transaction.
490   def test_upload_delete
491     changeset = create(:changeset)
492     super_relation = create(:relation)
493     used_relation = create(:relation)
494     used_way = create(:way)
495     used_node = create(:node)
496     create(:relation_member, :relation => super_relation, :member => used_relation)
497     create(:relation_member, :relation => super_relation, :member => used_way)
498     create(:relation_member, :relation => super_relation, :member => used_node)
499
500     basic_authorization changeset.user.display_name, "test"
501
502     diff = XML::Document.new
503     diff.root = XML::Node.new "osmChange"
504     delete = XML::Node.new "delete"
505     diff.root << delete
506     delete << super_relation.to_xml_node
507     delete << used_relation.to_xml_node
508     delete << used_way.to_xml_node
509     delete << used_node.to_xml_node
510
511     # update the changeset to one that this user owns
512     %w[node way relation].each do |type|
513       delete.find("//osmChange/delete/#{type}").each do |n|
514         n["changeset"] = changeset.id.to_s
515       end
516     end
517
518     # upload it
519     content diff
520     post :upload, :params => { :id => changeset.id }
521     assert_response :success,
522                     "can't upload a deletion diff to changeset: #{@response.body}"
523
524     # check the response is well-formed
525     assert_select "diffResult>node", 1
526     assert_select "diffResult>way", 1
527     assert_select "diffResult>relation", 2
528
529     # check that everything was deleted
530     assert_equal false, Node.find(used_node.id).visible
531     assert_equal false, Way.find(used_way.id).visible
532     assert_equal false, Relation.find(super_relation.id).visible
533     assert_equal false, Relation.find(used_relation.id).visible
534   end
535
536   ##
537   # test uploading a delete with no lat/lon, as they are optional in
538   # the osmChange spec.
539   def test_upload_nolatlon_delete
540     node = create(:node)
541     changeset = create(:changeset)
542
543     basic_authorization changeset.user.display_name, "test"
544     diff = "<osmChange><delete><node id='#{node.id}' version='#{node.version}' changeset='#{changeset.id}'/></delete></osmChange>"
545
546     # upload it
547     content diff
548     post :upload, :params => { :id => changeset.id }
549     assert_response :success,
550                     "can't upload a deletion diff to changeset: #{@response.body}"
551
552     # check the response is well-formed
553     assert_select "diffResult>node", 1
554
555     # check that everything was deleted
556     assert_equal false, Node.find(node.id).visible
557   end
558
559   def test_repeated_changeset_create
560     3.times do
561       basic_authorization create(:user).email, "test"
562
563       # create a temporary changeset
564       content "<osm><changeset>" \
565               "<tag k='created_by' v='osm test suite checking changesets'/>" \
566               "</changeset></osm>"
567       assert_difference "Changeset.count", 1 do
568         put :create
569       end
570       assert_response :success
571     end
572   end
573
574   def test_upload_large_changeset
575     basic_authorization create(:user).email, "test"
576
577     # create a changeset
578     content "<osm><changeset/></osm>"
579     put :create
580     assert_response :success, "Should be able to create a changeset: #{@response.body}"
581     changeset_id = @response.body.to_i
582
583     # upload some widely-spaced nodes, spiralling positive and negative
584     diff = <<CHANGESET.strip_heredoc
585       <osmChange>
586        <create>
587         <node id='-1' lon='-20' lat='-10' changeset='#{changeset_id}'/>
588         <node id='-10' lon='20'  lat='10' changeset='#{changeset_id}'/>
589         <node id='-2' lon='-40' lat='-20' changeset='#{changeset_id}'/>
590         <node id='-11' lon='40'  lat='20' changeset='#{changeset_id}'/>
591         <node id='-3' lon='-60' lat='-30' changeset='#{changeset_id}'/>
592         <node id='-12' lon='60'  lat='30' changeset='#{changeset_id}'/>
593         <node id='-4' lon='-80' lat='-40' changeset='#{changeset_id}'/>
594         <node id='-13' lon='80'  lat='40' changeset='#{changeset_id}'/>
595         <node id='-5' lon='-100' lat='-50' changeset='#{changeset_id}'/>
596         <node id='-14' lon='100'  lat='50' changeset='#{changeset_id}'/>
597         <node id='-6' lon='-120' lat='-60' changeset='#{changeset_id}'/>
598         <node id='-15' lon='120'  lat='60' changeset='#{changeset_id}'/>
599         <node id='-7' lon='-140' lat='-70' changeset='#{changeset_id}'/>
600         <node id='-16' lon='140'  lat='70' changeset='#{changeset_id}'/>
601         <node id='-8' lon='-160' lat='-80' changeset='#{changeset_id}'/>
602         <node id='-17' lon='160'  lat='80' changeset='#{changeset_id}'/>
603         <node id='-9' lon='-179.9' lat='-89.9' changeset='#{changeset_id}'/>
604         <node id='-18' lon='179.9'  lat='89.9' changeset='#{changeset_id}'/>
605        </create>
606       </osmChange>
607 CHANGESET
608
609     # upload it, which used to cause an error like "PGError: ERROR:
610     # integer out of range" (bug #2152). but shouldn't any more.
611     content diff
612     post :upload, :params => { :id => changeset_id }
613     assert_response :success,
614                     "can't upload a spatially-large diff to changeset: #{@response.body}"
615
616     # check that the changeset bbox is within bounds
617     cs = Changeset.find(changeset_id)
618     assert cs.min_lon >= -180 * GeoRecord::SCALE, "Minimum longitude (#{cs.min_lon / GeoRecord::SCALE}) should be >= -180 to be valid."
619     assert cs.max_lon <= 180 * GeoRecord::SCALE, "Maximum longitude (#{cs.max_lon / GeoRecord::SCALE}) should be <= 180 to be valid."
620     assert cs.min_lat >= -90 * GeoRecord::SCALE, "Minimum latitude (#{cs.min_lat / GeoRecord::SCALE}) should be >= -90 to be valid."
621     assert cs.max_lat <= 90 * GeoRecord::SCALE, "Maximum latitude (#{cs.max_lat / GeoRecord::SCALE}) should be <= 90 to be valid."
622   end
623
624   ##
625   # test that deleting stuff in a transaction doesn't bypass the checks
626   # to ensure that used elements are not deleted.
627   def test_upload_delete_invalid
628     changeset = create(:changeset)
629     relation = create(:relation)
630     other_relation = create(:relation)
631     used_way = create(:way)
632     used_node = create(:node)
633     create(:relation_member, :relation => relation, :member => used_way)
634     create(:relation_member, :relation => relation, :member => used_node)
635
636     basic_authorization changeset.user.email, "test"
637
638     diff = XML::Document.new
639     diff.root = XML::Node.new "osmChange"
640     delete = XML::Node.new "delete"
641     diff.root << delete
642     delete << other_relation.to_xml_node
643     delete << used_way.to_xml_node
644     delete << used_node.to_xml_node
645
646     # update the changeset to one that this user owns
647     %w[node way relation].each do |type|
648       delete.find("//osmChange/delete/#{type}").each do |n|
649         n["changeset"] = changeset.id.to_s
650       end
651     end
652
653     # upload it
654     content diff
655     post :upload, :params => { :id => changeset.id }
656     assert_response :precondition_failed,
657                     "shouldn't be able to upload a invalid deletion diff: #{@response.body}"
658     assert_equal "Precondition failed: Way #{used_way.id} is still used by relations #{relation.id}.", @response.body
659
660     # check that nothing was, in fact, deleted
661     assert_equal true, Node.find(used_node.id).visible
662     assert_equal true, Way.find(used_way.id).visible
663     assert_equal true, Relation.find(relation.id).visible
664     assert_equal true, Relation.find(other_relation.id).visible
665   end
666
667   ##
668   # test that a conditional delete of an in use object works.
669   def test_upload_delete_if_unused
670     changeset = create(:changeset)
671     super_relation = create(:relation)
672     used_relation = create(:relation)
673     used_way = create(:way)
674     used_node = create(:node)
675     create(:relation_member, :relation => super_relation, :member => used_relation)
676     create(:relation_member, :relation => super_relation, :member => used_way)
677     create(:relation_member, :relation => super_relation, :member => used_node)
678
679     basic_authorization changeset.user.email, "test"
680
681     diff = XML::Document.new
682     diff.root = XML::Node.new "osmChange"
683     delete = XML::Node.new "delete"
684     diff.root << delete
685     delete["if-unused"] = ""
686     delete << used_relation.to_xml_node
687     delete << used_way.to_xml_node
688     delete << used_node.to_xml_node
689
690     # update the changeset to one that this user owns
691     %w[node way relation].each do |type|
692       delete.find("//osmChange/delete/#{type}").each do |n|
693         n["changeset"] = changeset.id.to_s
694       end
695     end
696
697     # upload it
698     content diff
699     post :upload, :params => { :id => changeset.id }
700     assert_response :success,
701                     "can't do a conditional delete of in use objects: #{@response.body}"
702
703     # check the returned payload
704     assert_select "diffResult[version='#{API_VERSION}'][generator='OpenStreetMap server']", 1
705     assert_select "diffResult>node", 1
706     assert_select "diffResult>way", 1
707     assert_select "diffResult>relation", 1
708
709     # parse the response
710     doc = XML::Parser.string(@response.body).parse
711
712     # check the old IDs are all present and what we expect
713     assert_equal used_node.id, doc.find("//diffResult/node").first["old_id"].to_i
714     assert_equal used_way.id, doc.find("//diffResult/way").first["old_id"].to_i
715     assert_equal used_relation.id, doc.find("//diffResult/relation").first["old_id"].to_i
716
717     # check the new IDs are all present and unchanged
718     assert_equal used_node.id, doc.find("//diffResult/node").first["new_id"].to_i
719     assert_equal used_way.id, doc.find("//diffResult/way").first["new_id"].to_i
720     assert_equal used_relation.id, doc.find("//diffResult/relation").first["new_id"].to_i
721
722     # check the new versions are all present and unchanged
723     assert_equal used_node.version, doc.find("//diffResult/node").first["new_version"].to_i
724     assert_equal used_way.version, doc.find("//diffResult/way").first["new_version"].to_i
725     assert_equal used_relation.version, doc.find("//diffResult/relation").first["new_version"].to_i
726
727     # check that nothing was, in fact, deleted
728     assert_equal true, Node.find(used_node.id).visible
729     assert_equal true, Way.find(used_way.id).visible
730     assert_equal true, Relation.find(used_relation.id).visible
731   end
732
733   ##
734   # upload an element with a really long tag value
735   def test_upload_invalid_too_long_tag
736     changeset = create(:changeset)
737
738     basic_authorization changeset.user.email, "test"
739
740     # simple diff to create a node way and relation using placeholders
741     diff = <<CHANGESET.strip_heredoc
742       <osmChange>
743        <create>
744         <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
745          <tag k='foo' v='#{'x' * 256}'/>
746         </node>
747        </create>
748       </osmChange>
749 CHANGESET
750
751     # upload it
752     content diff
753     post :upload, :params => { :id => changeset.id }
754     assert_response :bad_request,
755                     "shoudln't be able to upload too long a tag to changeset: #{@response.body}"
756   end
757
758   ##
759   # upload something which creates new objects and inserts them into
760   # existing containers using placeholders.
761   def test_upload_complex
762     way = create(:way)
763     node = create(:node)
764     relation = create(:relation)
765     create(:way_node, :way => way, :node => node)
766
767     changeset = create(:changeset)
768
769     basic_authorization changeset.user.email, "test"
770
771     # simple diff to create a node way and relation using placeholders
772     diff = <<CHANGESET.strip_heredoc
773       <osmChange>
774        <create>
775         <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
776          <tag k='foo' v='bar'/>
777          <tag k='baz' v='bat'/>
778         </node>
779        </create>
780        <modify>
781         <way id='#{way.id}' changeset='#{changeset.id}' version='1'>
782          <nd ref='-1'/>
783          <nd ref='#{node.id}'/>
784         </way>
785         <relation id='#{relation.id}' changeset='#{changeset.id}' version='1'>
786          <member type='way' role='some' ref='#{way.id}'/>
787          <member type='node' role='some' ref='-1'/>
788          <member type='relation' role='some' ref='#{relation.id}'/>
789         </relation>
790        </modify>
791       </osmChange>
792 CHANGESET
793
794     # upload it
795     content diff
796     post :upload, :params => { :id => changeset.id }
797     assert_response :success,
798                     "can't upload a complex diff to changeset: #{@response.body}"
799
800     # check the returned payload
801     assert_select "diffResult[version='#{API_VERSION}'][generator='#{GENERATOR}']", 1
802     assert_select "diffResult>node", 1
803     assert_select "diffResult>way", 1
804     assert_select "diffResult>relation", 1
805
806     # inspect the response to find out what the new element IDs are
807     doc = XML::Parser.string(@response.body).parse
808     new_node_id = doc.find("//diffResult/node").first["new_id"].to_i
809
810     # check that the changes made it into the database
811     assert_equal 2, Node.find(new_node_id).tags.size, "new node should have two tags"
812     assert_equal [new_node_id, node.id], Way.find(way.id).nds, "way nodes should match"
813     Relation.find(relation.id).members.each do |type, id, _role|
814       assert_equal new_node_id, id, "relation should contain new node" if type == "node"
815     end
816   end
817
818   ##
819   # create a diff which references several changesets, which should cause
820   # a rollback and none of the diff gets committed
821   def test_upload_invalid_changesets
822     changeset = create(:changeset)
823     other_changeset = create(:changeset, :user => changeset.user)
824     node = create(:node)
825     way = create(:way)
826     relation = create(:relation)
827     other_relation = create(:relation)
828
829     basic_authorization changeset.user.email, "test"
830
831     # simple diff to create a node way and relation using placeholders
832     diff = <<CHANGESET.strip_heredoc
833       <osmChange>
834        <modify>
835         <node id='#{node.id}' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
836         <way id='#{way.id}' changeset='#{changeset.id}' version='1'>
837          <nd ref='#{node.id}'/>
838         </way>
839        </modify>
840        <modify>
841         <relation id='#{relation.id}' changeset='#{changeset.id}' version='1'>
842          <member type='way' role='some' ref='#{way.id}'/>
843          <member type='node' role='some' ref='#{node.id}'/>
844          <member type='relation' role='some' ref='#{other_relation.id}'/>
845         </relation>
846        </modify>
847        <create>
848         <node id='-1' lon='0' lat='0' changeset='#{other_changeset.id}'>
849          <tag k='foo' v='bar'/>
850          <tag k='baz' v='bat'/>
851         </node>
852        </create>
853       </osmChange>
854 CHANGESET
855
856     # upload it
857     content diff
858     post :upload, :params => { :id => changeset.id }
859     assert_response :conflict,
860                     "uploading a diff with multiple changesets should have failed"
861
862     # check that objects are unmodified
863     assert_nodes_are_equal(node, Node.find(node.id))
864     assert_ways_are_equal(way, Way.find(way.id))
865     assert_relations_are_equal(relation, Relation.find(relation.id))
866   end
867
868   ##
869   # upload multiple versions of the same element in the same diff.
870   def test_upload_multiple_valid
871     node = create(:node)
872     changeset = create(:changeset)
873     basic_authorization changeset.user.email, "test"
874
875     # change the location of a node multiple times, each time referencing
876     # the last version. doesn't this depend on version numbers being
877     # sequential?
878     diff = <<CHANGESET.strip_heredoc
879       <osmChange>
880        <modify>
881         <node id='#{node.id}' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
882         <node id='#{node.id}' lon='1' lat='0' changeset='#{changeset.id}' version='2'/>
883         <node id='#{node.id}' lon='1' lat='1' changeset='#{changeset.id}' version='3'/>
884         <node id='#{node.id}' lon='1' lat='2' changeset='#{changeset.id}' version='4'/>
885         <node id='#{node.id}' lon='2' lat='2' changeset='#{changeset.id}' version='5'/>
886         <node id='#{node.id}' lon='3' lat='2' changeset='#{changeset.id}' version='6'/>
887         <node id='#{node.id}' lon='3' lat='3' changeset='#{changeset.id}' version='7'/>
888         <node id='#{node.id}' lon='9' lat='9' changeset='#{changeset.id}' version='8'/>
889        </modify>
890       </osmChange>
891 CHANGESET
892
893     # upload it
894     content diff
895     post :upload, :params => { :id => changeset.id }
896     assert_response :success,
897                     "can't upload multiple versions of an element in a diff: #{@response.body}"
898
899     # check the response is well-formed. its counter-intuitive, but the
900     # API will return multiple elements with the same ID and different
901     # version numbers for each change we made.
902     assert_select "diffResult>node", 8
903   end
904
905   ##
906   # upload multiple versions of the same element in the same diff, but
907   # keep the version numbers the same.
908   def test_upload_multiple_duplicate
909     node = create(:node)
910     changeset = create(:changeset)
911
912     basic_authorization changeset.user.email, "test"
913
914     diff = <<CHANGESET.strip_heredoc
915       <osmChange>
916        <modify>
917         <node id='#{node.id}' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
918         <node id='#{node.id}' lon='1' lat='1' changeset='#{changeset.id}' version='1'/>
919        </modify>
920       </osmChange>
921 CHANGESET
922
923     # upload it
924     content diff
925     post :upload, :params => { :id => changeset.id }
926     assert_response :conflict,
927                     "shouldn't be able to upload the same element twice in a diff: #{@response.body}"
928   end
929
930   ##
931   # try to upload some elements without specifying the version
932   def test_upload_missing_version
933     changeset = create(:changeset)
934
935     basic_authorization changeset.user.email, "test"
936
937     diff = <<CHANGESET.strip_heredoc
938       <osmChange>
939        <modify>
940        <node id='1' lon='1' lat='1' changeset='#{changeset.id}'/>
941        </modify>
942       </osmChange>
943 CHANGESET
944
945     # upload it
946     content diff
947     post :upload, :params => { :id => changeset.id }
948     assert_response :bad_request,
949                     "shouldn't be able to upload an element without version: #{@response.body}"
950   end
951
952   ##
953   # try to upload with commands other than create, modify, or delete
954   def test_action_upload_invalid
955     changeset = create(:changeset)
956
957     basic_authorization changeset.user.email, "test"
958
959     diff = <<CHANGESET.strip_heredoc
960       <osmChange>
961         <ping>
962          <node id='1' lon='1' lat='1' changeset='#{changeset.id}' />
963         </ping>
964       </osmChange>
965 CHANGESET
966     content diff
967     post :upload, :params => { :id => changeset.id }
968     assert_response :bad_request, "Shouldn't be able to upload a diff with the action ping"
969     assert_equal @response.body, "Unknown action ping, choices are create, modify, delete"
970   end
971
972   ##
973   # upload a valid changeset which has a mixture of whitespace
974   # to check a bug reported by ivansanchez (#1565).
975   def test_upload_whitespace_valid
976     changeset = create(:changeset)
977     node = create(:node)
978     way = create(:way_with_nodes, :nodes_count => 2)
979     relation = create(:relation)
980     other_relation = create(:relation)
981     create(:relation_tag, :relation => relation)
982
983     basic_authorization changeset.user.email, "test"
984
985     diff = <<CHANGESET.strip_heredoc
986       <osmChange>
987        <modify><node id='#{node.id}' lon='0' lat='0' changeset='#{changeset.id}'
988         version='1'></node>
989         <node id='#{node.id}' lon='1' lat='1' changeset='#{changeset.id}' version='2'><tag k='k' v='v'/></node></modify>
990        <modify>
991        <relation id='#{relation.id}' changeset='#{changeset.id}' version='1'><member
992          type='way' role='some' ref='#{way.id}'/><member
993           type='node' role='some' ref='#{node.id}'/>
994          <member type='relation' role='some' ref='#{other_relation.id}'/>
995         </relation>
996        </modify></osmChange>
997 CHANGESET
998
999     # upload it
1000     content diff
1001     post :upload, :params => { :id => changeset.id }
1002     assert_response :success,
1003                     "can't upload a valid diff with whitespace variations to changeset: #{@response.body}"
1004
1005     # check the response is well-formed
1006     assert_select "diffResult>node", 2
1007     assert_select "diffResult>relation", 1
1008
1009     # check that the changes made it into the database
1010     assert_equal 1, Node.find(node.id).tags.size, "node #{node.id} should now have one tag"
1011     assert_equal 0, Relation.find(relation.id).tags.size, "relation #{relation.id} should now have no tags"
1012   end
1013
1014   ##
1015   # test that a placeholder can be reused within the same upload.
1016   def test_upload_reuse_placeholder_valid
1017     changeset = create(:changeset)
1018
1019     basic_authorization changeset.user.email, "test"
1020
1021     diff = <<CHANGESET.strip_heredoc
1022       <osmChange>
1023        <create>
1024         <node id='-1' lon='0' lat='0' changeset='#{changeset.id}'>
1025          <tag k="foo" v="bar"/>
1026         </node>
1027        </create>
1028        <modify>
1029         <node id='-1' lon='1' lat='1' changeset='#{changeset.id}' version='1'/>
1030        </modify>
1031        <delete>
1032         <node id='-1' lon='2' lat='2' changeset='#{changeset.id}' version='2'/>
1033        </delete>
1034       </osmChange>
1035 CHANGESET
1036
1037     # upload it
1038     content diff
1039     post :upload, :params => { :id => changeset.id }
1040     assert_response :success,
1041                     "can't upload a valid diff with re-used placeholders to changeset: #{@response.body}"
1042
1043     # check the response is well-formed
1044     assert_select "diffResult>node", 3
1045     assert_select "diffResult>node[old_id='-1']", 3
1046   end
1047
1048   ##
1049   # test what happens if a diff upload re-uses placeholder IDs in an
1050   # illegal way.
1051   def test_upload_placeholder_invalid
1052     changeset = create(:changeset)
1053
1054     basic_authorization changeset.user.email, "test"
1055
1056     diff = <<CHANGESET.strip_heredoc
1057       <osmChange>
1058        <create>
1059         <node id='-1' lon='0' lat='0' changeset='#{changeset.id}' version='1'/>
1060         <node id='-1' lon='1' lat='1' changeset='#{changeset.id}' version='1'/>
1061         <node id='-1' lon='2' lat='2' changeset='#{changeset.id}' version='2'/>
1062        </create>
1063       </osmChange>
1064 CHANGESET
1065
1066     # upload it
1067     content diff
1068     post :upload, :params => { :id => changeset.id }
1069     assert_response :bad_request,
1070                     "shouldn't be able to re-use placeholder IDs"
1071   end
1072
1073   ##
1074   # test that uploading a way referencing invalid placeholders gives a
1075   # proper error, not a 500.
1076   def test_upload_placeholder_invalid_way
1077     changeset = create(:changeset)
1078     way = create(:way)
1079
1080     basic_authorization changeset.user.email, "test"
1081
1082     diff = <<CHANGESET.strip_heredoc
1083       <osmChange>
1084        <create>
1085         <node id="-1" lon="0" lat="0" changeset="#{changeset.id}" version="1"/>
1086         <node id="-2" lon="1" lat="1" changeset="#{changeset.id}" version="1"/>
1087         <node id="-3" lon="2" lat="2" changeset="#{changeset.id}" version="1"/>
1088         <way id="-1" changeset="#{changeset.id}" version="1">
1089          <nd ref="-1"/>
1090          <nd ref="-2"/>
1091          <nd ref="-3"/>
1092          <nd ref="-4"/>
1093         </way>
1094        </create>
1095       </osmChange>
1096 CHANGESET
1097
1098     # upload it
1099     content diff
1100     post :upload, :params => { :id => changeset.id }
1101     assert_response :bad_request,
1102                     "shouldn't be able to use invalid placeholder IDs"
1103     assert_equal "Placeholder node not found for reference -4 in way -1", @response.body
1104
1105     # the same again, but this time use an existing way
1106     diff = <<CHANGESET.strip_heredoc
1107       <osmChange>
1108        <create>
1109         <node id="-1" lon="0" lat="0" changeset="#{changeset.id}" version="1"/>
1110         <node id="-2" lon="1" lat="1" changeset="#{changeset.id}" version="1"/>
1111         <node id="-3" lon="2" lat="2" changeset="#{changeset.id}" version="1"/>
1112         <way id="#{way.id}" changeset="#{changeset.id}" version="1">
1113          <nd ref="-1"/>
1114          <nd ref="-2"/>
1115          <nd ref="-3"/>
1116          <nd ref="-4"/>
1117         </way>
1118        </create>
1119       </osmChange>
1120 CHANGESET
1121
1122     # upload it
1123     content diff
1124     post :upload, :params => { :id => changeset.id }
1125     assert_response :bad_request,
1126                     "shouldn't be able to use invalid placeholder IDs"
1127     assert_equal "Placeholder node not found for reference -4 in way #{way.id}", @response.body
1128   end
1129
1130   ##
1131   # test that uploading a relation referencing invalid placeholders gives a
1132   # proper error, not a 500.
1133   def test_upload_placeholder_invalid_relation
1134     changeset = create(:changeset)
1135     relation = create(:relation)
1136
1137     basic_authorization changeset.user.email, "test"
1138
1139     diff = <<CHANGESET.strip_heredoc
1140       <osmChange>
1141        <create>
1142         <node id="-1" lon="0" lat="0" changeset="#{changeset.id}" version="1"/>
1143         <node id="-2" lon="1" lat="1" changeset="#{changeset.id}" version="1"/>
1144         <node id="-3" lon="2" lat="2" changeset="#{changeset.id}" version="1"/>
1145         <relation id="-1" changeset="#{changeset.id}" version="1">
1146          <member type="node" role="foo" ref="-1"/>
1147          <member type="node" role="foo" ref="-2"/>
1148          <member type="node" role="foo" ref="-3"/>
1149          <member type="node" role="foo" ref="-4"/>
1150         </relation>
1151        </create>
1152       </osmChange>
1153 CHANGESET
1154
1155     # upload it
1156     content diff
1157     post :upload, :params => { :id => changeset.id }
1158     assert_response :bad_request,
1159                     "shouldn't be able to use invalid placeholder IDs"
1160     assert_equal "Placeholder Node not found for reference -4 in relation -1.", @response.body
1161
1162     # the same again, but this time use an existing relation
1163     diff = <<CHANGESET.strip_heredoc
1164       <osmChange>
1165        <create>
1166         <node id="-1" lon="0" lat="0" changeset="#{changeset.id}" version="1"/>
1167         <node id="-2" lon="1" lat="1" changeset="#{changeset.id}" version="1"/>
1168         <node id="-3" lon="2" lat="2" changeset="#{changeset.id}" version="1"/>
1169         <relation id="#{relation.id}" changeset="#{changeset.id}" version="1">
1170          <member type="node" role="foo" ref="-1"/>
1171          <member type="node" role="foo" ref="-2"/>
1172          <member type="node" role="foo" ref="-3"/>
1173          <member type="way" role="bar" ref="-1"/>
1174         </relation>
1175        </create>
1176       </osmChange>
1177 CHANGESET
1178
1179     # upload it
1180     content diff
1181     post :upload, :params => { :id => changeset.id }
1182     assert_response :bad_request,
1183                     "shouldn't be able to use invalid placeholder IDs"
1184     assert_equal "Placeholder Way not found for reference -1 in relation #{relation.id}.", @response.body
1185   end
1186
1187   ##
1188   # test what happens if a diff is uploaded containing only a node
1189   # move.
1190   def test_upload_node_move
1191     basic_authorization create(:user).email, "test"
1192
1193     content "<osm><changeset>" \
1194             "<tag k='created_by' v='osm test suite checking changesets'/>" \
1195             "</changeset></osm>"
1196     put :create
1197     assert_response :success
1198     changeset_id = @response.body.to_i
1199
1200     old_node = create(:node, :lat => 1, :lon => 1)
1201
1202     diff = XML::Document.new
1203     diff.root = XML::Node.new "osmChange"
1204     modify = XML::Node.new "modify"
1205     xml_old_node = old_node.to_xml_node
1206     xml_old_node["lat"] = 2.0.to_s
1207     xml_old_node["lon"] = 2.0.to_s
1208     xml_old_node["changeset"] = changeset_id.to_s
1209     modify << xml_old_node
1210     diff.root << modify
1211
1212     # upload it
1213     content diff
1214     post :upload, :params => { :id => changeset_id }
1215     assert_response :success,
1216                     "diff should have uploaded OK"
1217
1218     # check the bbox
1219     changeset = Changeset.find(changeset_id)
1220     assert_equal 1 * GeoRecord::SCALE, changeset.min_lon, "min_lon should be 1 degree"
1221     assert_equal 2 * GeoRecord::SCALE, changeset.max_lon, "max_lon should be 2 degrees"
1222     assert_equal 1 * GeoRecord::SCALE, changeset.min_lat, "min_lat should be 1 degree"
1223     assert_equal 2 * GeoRecord::SCALE, changeset.max_lat, "max_lat should be 2 degrees"
1224   end
1225
1226   ##
1227   # test what happens if a diff is uploaded adding a node to a way.
1228   def test_upload_way_extend
1229     basic_authorization create(:user).email, "test"
1230
1231     content "<osm><changeset>" \
1232             "<tag k='created_by' v='osm test suite checking changesets'/>" \
1233             "</changeset></osm>"
1234     put :create
1235     assert_response :success
1236     changeset_id = @response.body.to_i
1237
1238     old_way = create(:way)
1239     create(:way_node, :way => old_way, :node => create(:node, :lat => 1, :lon => 1))
1240
1241     diff = XML::Document.new
1242     diff.root = XML::Node.new "osmChange"
1243     modify = XML::Node.new "modify"
1244     xml_old_way = old_way.to_xml_node
1245     nd_ref = XML::Node.new "nd"
1246     nd_ref["ref"] = create(:node, :lat => 3, :lon => 3).id.to_s
1247     xml_old_way << nd_ref
1248     xml_old_way["changeset"] = changeset_id.to_s
1249     modify << xml_old_way
1250     diff.root << modify
1251
1252     # upload it
1253     content diff
1254     post :upload, :params => { :id => changeset_id }
1255     assert_response :success,
1256                     "diff should have uploaded OK"
1257
1258     # check the bbox
1259     changeset = Changeset.find(changeset_id)
1260     assert_equal 1 * GeoRecord::SCALE, changeset.min_lon, "min_lon should be 1 degree"
1261     assert_equal 3 * GeoRecord::SCALE, changeset.max_lon, "max_lon should be 3 degrees"
1262     assert_equal 1 * GeoRecord::SCALE, changeset.min_lat, "min_lat should be 1 degree"
1263     assert_equal 3 * GeoRecord::SCALE, changeset.max_lat, "max_lat should be 3 degrees"
1264   end
1265
1266   ##
1267   # test for more issues in #1568
1268   def test_upload_empty_invalid
1269     changeset = create(:changeset)
1270
1271     basic_authorization changeset.user.email, "test"
1272
1273     ["<osmChange/>",
1274      "<osmChange></osmChange>",
1275      "<osmChange><modify/></osmChange>",
1276      "<osmChange><modify></modify></osmChange>"].each do |diff|
1277       # upload it
1278       content diff
1279       post :upload, :params => { :id => changeset.id }
1280       assert_response(:success, "should be able to upload " \
1281                       "empty changeset: " + diff)
1282     end
1283   end
1284
1285   ##
1286   # test that the X-Error-Format header works to request XML errors
1287   def test_upload_xml_errors
1288     changeset = create(:changeset)
1289     node = create(:node)
1290     create(:relation_member, :member => node)
1291
1292     basic_authorization changeset.user.email, "test"
1293
1294     # try and delete a node that is in use
1295     diff = XML::Document.new
1296     diff.root = XML::Node.new "osmChange"
1297     delete = XML::Node.new "delete"
1298     diff.root << delete
1299     delete << node.to_xml_node
1300
1301     # upload it
1302     content diff
1303     error_format "xml"
1304     post :upload, :params => { :id => changeset.id }
1305     assert_response :success,
1306                     "failed to return error in XML format"
1307
1308     # check the returned payload
1309     assert_select "osmError[version='#{API_VERSION}'][generator='OpenStreetMap server']", 1
1310     assert_select "osmError>status", 1
1311     assert_select "osmError>message", 1
1312   end
1313
1314   ##
1315   # when we make some simple changes we get the same changes back from the
1316   # diff download.
1317   def test_diff_download_simple
1318     node = create(:node)
1319
1320     ## First try with a non-public user, which should get a forbidden
1321     basic_authorization create(:user, :data_public => false).email, "test"
1322
1323     # create a temporary changeset
1324     content "<osm><changeset>" \
1325             "<tag k='created_by' v='osm test suite checking changesets'/>" \
1326             "</changeset></osm>"
1327     put :create
1328     assert_response :forbidden
1329
1330     ## Now try with a normal user
1331     basic_authorization create(:user).email, "test"
1332
1333     # create a temporary changeset
1334     content "<osm><changeset>" \
1335             "<tag k='created_by' v='osm test suite checking changesets'/>" \
1336             "</changeset></osm>"
1337     put :create
1338     assert_response :success
1339     changeset_id = @response.body.to_i
1340
1341     # add a diff to it
1342     diff = <<CHANGESET.strip_heredoc
1343       <osmChange>
1344        <modify>
1345         <node id='#{node.id}' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
1346         <node id='#{node.id}' lon='1' lat='0' changeset='#{changeset_id}' version='2'/>
1347         <node id='#{node.id}' lon='1' lat='1' changeset='#{changeset_id}' version='3'/>
1348         <node id='#{node.id}' lon='1' lat='2' changeset='#{changeset_id}' version='4'/>
1349         <node id='#{node.id}' lon='2' lat='2' changeset='#{changeset_id}' version='5'/>
1350         <node id='#{node.id}' lon='3' lat='2' changeset='#{changeset_id}' version='6'/>
1351         <node id='#{node.id}' lon='3' lat='3' changeset='#{changeset_id}' version='7'/>
1352         <node id='#{node.id}' lon='9' lat='9' changeset='#{changeset_id}' version='8'/>
1353        </modify>
1354       </osmChange>
1355 CHANGESET
1356
1357     # upload it
1358     content diff
1359     post :upload, :params => { :id => changeset_id }
1360     assert_response :success,
1361                     "can't upload multiple versions of an element in a diff: #{@response.body}"
1362
1363     get :download, :params => { :id => changeset_id }
1364     assert_response :success
1365
1366     assert_select "osmChange", 1
1367     assert_select "osmChange>modify", 8
1368     assert_select "osmChange>modify>node", 8
1369   end
1370
1371   ##
1372   # culled this from josm to ensure that nothing in the way that josm
1373   # is formatting the request is causing it to fail.
1374   #
1375   # NOTE: the error turned out to be something else completely!
1376   def test_josm_upload
1377     basic_authorization create(:user).email, "test"
1378
1379     # create a temporary changeset
1380     content "<osm><changeset>" \
1381             "<tag k='created_by' v='osm test suite checking changesets'/>" \
1382             "</changeset></osm>"
1383     put :create
1384     assert_response :success
1385     changeset_id = @response.body.to_i
1386
1387     diff = <<OSMFILE.strip_heredoc
1388       <osmChange version="0.6" generator="JOSM">
1389       <create version="0.6" generator="JOSM">
1390         <node id='-1' visible='true' changeset='#{changeset_id}' lat='51.49619982187321' lon='-0.18722061869438314' />
1391         <node id='-2' visible='true' changeset='#{changeset_id}' lat='51.496359883909605' lon='-0.18653093576241928' />
1392         <node id='-3' visible='true' changeset='#{changeset_id}' lat='51.49598132358285' lon='-0.18719613290981638' />
1393         <node id='-4' visible='true' changeset='#{changeset_id}' lat='51.4961591711078' lon='-0.18629015888084607' />
1394         <node id='-5' visible='true' changeset='#{changeset_id}' lat='51.49582126021711' lon='-0.18708186591517145' />
1395         <node id='-6' visible='true' changeset='#{changeset_id}' lat='51.49591018437858' lon='-0.1861432441734455' />
1396         <node id='-7' visible='true' changeset='#{changeset_id}' lat='51.49560784152179' lon='-0.18694719410005425' />
1397         <node id='-8' visible='true' changeset='#{changeset_id}' lat='51.49567389979617' lon='-0.1860289771788006' />
1398         <node id='-9' visible='true' changeset='#{changeset_id}' lat='51.49543761398892' lon='-0.186820684213126' />
1399         <way id='-10' action='modiy' visible='true' changeset='#{changeset_id}'>
1400           <nd ref='-1' />
1401           <nd ref='-2' />
1402           <nd ref='-3' />
1403           <nd ref='-4' />
1404           <nd ref='-5' />
1405           <nd ref='-6' />
1406           <nd ref='-7' />
1407           <nd ref='-8' />
1408           <nd ref='-9' />
1409           <tag k='highway' v='residential' />
1410           <tag k='name' v='Foobar Street' />
1411         </way>
1412       </create>
1413       </osmChange>
1414 OSMFILE
1415
1416     # upload it
1417     content diff
1418     post :upload, :params => { :id => changeset_id }
1419     assert_response :success,
1420                     "can't upload a diff from JOSM: #{@response.body}"
1421
1422     get :download, :params => { :id => changeset_id }
1423     assert_response :success
1424
1425     assert_select "osmChange", 1
1426     assert_select "osmChange>create>node", 9
1427     assert_select "osmChange>create>way", 1
1428     assert_select "osmChange>create>way>nd", 9
1429     assert_select "osmChange>create>way>tag", 2
1430   end
1431
1432   ##
1433   # when we make some complex changes we get the same changes back from the
1434   # diff download.
1435   def test_diff_download_complex
1436     node = create(:node)
1437     node2 = create(:node)
1438     way = create(:way)
1439     basic_authorization create(:user).email, "test"
1440
1441     # create a temporary changeset
1442     content "<osm><changeset>" \
1443             "<tag k='created_by' v='osm test suite checking changesets'/>" \
1444             "</changeset></osm>"
1445     put :create
1446     assert_response :success
1447     changeset_id = @response.body.to_i
1448
1449     # add a diff to it
1450     diff = <<CHANGESET.strip_heredoc
1451       <osmChange>
1452        <delete>
1453         <node id='#{node.id}' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
1454        </delete>
1455        <create>
1456         <node id='-1' lon='9' lat='9' changeset='#{changeset_id}' version='0'/>
1457         <node id='-2' lon='8' lat='9' changeset='#{changeset_id}' version='0'/>
1458         <node id='-3' lon='7' lat='9' changeset='#{changeset_id}' version='0'/>
1459        </create>
1460        <modify>
1461         <node id='#{node2.id}' lon='20' lat='15' changeset='#{changeset_id}' version='1'/>
1462         <way id='#{way.id}' changeset='#{changeset_id}' version='1'>
1463          <nd ref='#{node2.id}'/>
1464          <nd ref='-1'/>
1465          <nd ref='-2'/>
1466          <nd ref='-3'/>
1467         </way>
1468        </modify>
1469       </osmChange>
1470 CHANGESET
1471
1472     # upload it
1473     content diff
1474     post :upload, :params => { :id => changeset_id }
1475     assert_response :success,
1476                     "can't upload multiple versions of an element in a diff: #{@response.body}"
1477
1478     get :download, :params => { :id => changeset_id }
1479     assert_response :success
1480
1481     assert_select "osmChange", 1
1482     assert_select "osmChange>create", 3
1483     assert_select "osmChange>delete", 1
1484     assert_select "osmChange>modify", 2
1485     assert_select "osmChange>create>node", 3
1486     assert_select "osmChange>delete>node", 1
1487     assert_select "osmChange>modify>node", 1
1488     assert_select "osmChange>modify>way", 1
1489   end
1490
1491   def test_changeset_download
1492     changeset = create(:changeset)
1493     node = create(:node, :with_history, :version => 1, :changeset => changeset)
1494     tag = create(:old_node_tag, :old_node => node.old_nodes.find_by(:version => 1))
1495     node2 = create(:node, :with_history, :version => 1, :changeset => changeset)
1496     _node3 = create(:node, :with_history, :deleted, :version => 1, :changeset => changeset)
1497     _relation = create(:relation, :with_history, :version => 1, :changeset => changeset)
1498     _relation2 = create(:relation, :with_history, :deleted, :version => 1, :changeset => changeset)
1499
1500     get :download, :params => { :id => changeset.id }
1501
1502     assert_response :success
1503     assert_template nil
1504     # print @response.body
1505     # FIXME: needs more assert_select tests
1506     assert_select "osmChange[version='#{API_VERSION}'][generator='#{GENERATOR}']" do
1507       assert_select "create", :count => 5
1508       assert_select "create>node[id='#{node.id}'][visible='#{node.visible?}'][version='#{node.version}']" do
1509         assert_select "tag[k='#{tag.k}'][v='#{tag.v}']"
1510       end
1511       assert_select "create>node[id='#{node2.id}']"
1512     end
1513   end
1514
1515   ##
1516   # check that the bounding box of a changeset gets updated correctly
1517   # FIXME: This should really be moded to a integration test due to the with_controller
1518   def test_changeset_bbox
1519     way = create(:way)
1520     create(:way_node, :way => way, :node => create(:node, :lat => 3, :lon => 3))
1521
1522     basic_authorization create(:user).email, "test"
1523
1524     # create a new changeset
1525     content "<osm><changeset/></osm>"
1526     put :create
1527     assert_response :success, "Creating of changeset failed."
1528     changeset_id = @response.body.to_i
1529
1530     # add a single node to it
1531     with_controller(NodeController.new) do
1532       content "<osm><node lon='1' lat='2' changeset='#{changeset_id}'/></osm>"
1533       put :create
1534       assert_response :success, "Couldn't create node."
1535     end
1536
1537     # get the bounding box back from the changeset
1538     get :read, :params => { :id => changeset_id }
1539     assert_response :success, "Couldn't read back changeset."
1540     assert_select "osm>changeset[min_lon='1.0000000']", 1
1541     assert_select "osm>changeset[max_lon='1.0000000']", 1
1542     assert_select "osm>changeset[min_lat='2.0000000']", 1
1543     assert_select "osm>changeset[max_lat='2.0000000']", 1
1544
1545     # add another node to it
1546     with_controller(NodeController.new) do
1547       content "<osm><node lon='2' lat='1' changeset='#{changeset_id}'/></osm>"
1548       put :create
1549       assert_response :success, "Couldn't create second node."
1550     end
1551
1552     # get the bounding box back from the changeset
1553     get :read, :params => { :id => changeset_id }
1554     assert_response :success, "Couldn't read back changeset for the second time."
1555     assert_select "osm>changeset[min_lon='1.0000000']", 1
1556     assert_select "osm>changeset[max_lon='2.0000000']", 1
1557     assert_select "osm>changeset[min_lat='1.0000000']", 1
1558     assert_select "osm>changeset[max_lat='2.0000000']", 1
1559
1560     # add (delete) a way to it, which contains a point at (3,3)
1561     with_controller(WayController.new) do
1562       content update_changeset(way.to_xml, changeset_id)
1563       put :delete, :params => { :id => way.id }
1564       assert_response :success, "Couldn't delete a way."
1565     end
1566
1567     # get the bounding box back from the changeset
1568     get :read, :params => { :id => changeset_id }
1569     assert_response :success, "Couldn't read back changeset for the third time."
1570     assert_select "osm>changeset[min_lon='1.0000000']", 1
1571     assert_select "osm>changeset[max_lon='3.0000000']", 1
1572     assert_select "osm>changeset[min_lat='1.0000000']", 1
1573     assert_select "osm>changeset[max_lat='3.0000000']", 1
1574   end
1575
1576   ##
1577   # test that the changeset :include method works as it should
1578   def test_changeset_include
1579     basic_authorization create(:user).display_name, "test"
1580
1581     # create a new changeset
1582     content "<osm><changeset/></osm>"
1583     put :create
1584     assert_response :success, "Creating of changeset failed."
1585     changeset_id = @response.body.to_i
1586
1587     # NOTE: the include method doesn't over-expand, like inserting
1588     # a real method does. this is because we expect the client to
1589     # know what it is doing!
1590     check_after_include(changeset_id, 1, 1, [1, 1, 1, 1])
1591     check_after_include(changeset_id, 3, 3, [1, 1, 3, 3])
1592     check_after_include(changeset_id, 4, 2, [1, 1, 4, 3])
1593     check_after_include(changeset_id, 2, 2, [1, 1, 4, 3])
1594     check_after_include(changeset_id, -1, -1, [-1, -1, 4, 3])
1595     check_after_include(changeset_id, -2, 5, [-2, -1, 4, 5])
1596   end
1597
1598   ##
1599   # test that a not found, wrong method with the expand bbox works as expected
1600   def test_changeset_expand_bbox_error
1601     basic_authorization create(:user).display_name, "test"
1602
1603     # create a new changeset
1604     content "<osm><changeset/></osm>"
1605     put :create
1606     assert_response :success, "Creating of changeset failed."
1607     changeset_id = @response.body.to_i
1608
1609     lon = 58.2
1610     lat = -0.45
1611
1612     # Try and put
1613     content "<osm><node lon='#{lon}' lat='#{lat}'/></osm>"
1614     put :expand_bbox, :params => { :id => changeset_id }
1615     assert_response :method_not_allowed, "shouldn't be able to put a bbox expand"
1616
1617     # Try to get the update
1618     content "<osm><node lon='#{lon}' lat='#{lat}'/></osm>"
1619     get :expand_bbox, :params => { :id => changeset_id }
1620     assert_response :method_not_allowed, "shouldn't be able to get a bbox expand"
1621
1622     # Try to use a hopefully missing changeset
1623     content "<osm><node lon='#{lon}' lat='#{lat}'/></osm>"
1624     post :expand_bbox, :params => { :id => changeset_id + 13245 }
1625     assert_response :not_found, "shouldn't be able to do a bbox expand on a nonexistant changeset"
1626   end
1627
1628   ##
1629   # test the query functionality of changesets
1630   def test_query
1631     private_user = create(:user, :data_public => false)
1632     private_user_changeset = create(:changeset, :user => private_user)
1633     private_user_closed_changeset = create(:changeset, :closed, :user => private_user)
1634     user = create(:user)
1635     changeset = create(:changeset, :user => user)
1636     closed_changeset = create(:changeset, :closed, :user => user, :created_at => Time.utc(2008, 1, 1, 0, 0, 0), :closed_at => Time.utc(2008, 1, 2, 0, 0, 0))
1637     changeset2 = create(:changeset, :min_lat => 5 * GeoRecord::SCALE, :min_lon => 5 * GeoRecord::SCALE, :max_lat => 15 * GeoRecord::SCALE, :max_lon => 15 * GeoRecord::SCALE)
1638     changeset3 = create(:changeset, :min_lat => 4.5 * GeoRecord::SCALE, :min_lon => 4.5 * GeoRecord::SCALE, :max_lat => 5 * GeoRecord::SCALE, :max_lon => 5 * GeoRecord::SCALE)
1639
1640     get :query, :params => { :bbox => "-10,-10, 10, 10" }
1641     assert_response :success, "can't get changesets in bbox"
1642     assert_changesets [changeset2, changeset3]
1643
1644     get :query, :params => { :bbox => "4.5,4.5,4.6,4.6" }
1645     assert_response :success, "can't get changesets in bbox"
1646     assert_changesets [changeset3]
1647
1648     # not found when looking for changesets of non-existing users
1649     get :query, :params => { :user => User.maximum(:id) + 1 }
1650     assert_response :not_found
1651     get :query, :params => { :display_name => " " }
1652     assert_response :not_found
1653
1654     # can't get changesets of user 1 without authenticating
1655     get :query, :params => { :user => private_user.id }
1656     assert_response :not_found, "shouldn't be able to get changesets by non-public user (ID)"
1657     get :query, :params => { :display_name => private_user.display_name }
1658     assert_response :not_found, "shouldn't be able to get changesets by non-public user (name)"
1659
1660     # but this should work
1661     basic_authorization private_user.email, "test"
1662     get :query, :params => { :user => private_user.id }
1663     assert_response :success, "can't get changesets by user ID"
1664     assert_changesets [private_user_changeset, private_user_closed_changeset]
1665
1666     get :query, :params => { :display_name => private_user.display_name }
1667     assert_response :success, "can't get changesets by user name"
1668     assert_changesets [private_user_changeset, private_user_closed_changeset]
1669
1670     # check that the correct error is given when we provide both UID and name
1671     get :query, :params => { :user => private_user.id,
1672                              :display_name => private_user.display_name }
1673     assert_response :bad_request, "should be a bad request to have both ID and name specified"
1674
1675     get :query, :params => { :user => private_user.id, :open => true }
1676     assert_response :success, "can't get changesets by user and open"
1677     assert_changesets [private_user_changeset]
1678
1679     get :query, :params => { :time => "2007-12-31" }
1680     assert_response :success, "can't get changesets by time-since"
1681     assert_changesets [private_user_changeset, private_user_closed_changeset, changeset, closed_changeset, changeset2, changeset3]
1682
1683     get :query, :params => { :time => "2008-01-01T12:34Z" }
1684     assert_response :success, "can't get changesets by time-since with hour"
1685     assert_changesets [private_user_changeset, private_user_closed_changeset, changeset, closed_changeset, changeset2, changeset3]
1686
1687     get :query, :params => { :time => "2007-12-31T23:59Z,2008-01-02T00:01Z" }
1688     assert_response :success, "can't get changesets by time-range"
1689     assert_changesets [closed_changeset]
1690
1691     get :query, :params => { :open => "true" }
1692     assert_response :success, "can't get changesets by open-ness"
1693     assert_changesets [private_user_changeset, changeset, changeset2, changeset3]
1694
1695     get :query, :params => { :closed => "true" }
1696     assert_response :success, "can't get changesets by closed-ness"
1697     assert_changesets [private_user_closed_changeset, closed_changeset]
1698
1699     get :query, :params => { :closed => "true", :user => private_user.id }
1700     assert_response :success, "can't get changesets by closed-ness and user"
1701     assert_changesets [private_user_closed_changeset]
1702
1703     get :query, :params => { :closed => "true", :user => user.id }
1704     assert_response :success, "can't get changesets by closed-ness and user"
1705     assert_changesets [closed_changeset]
1706
1707     get :query, :params => { :changesets => "#{private_user_changeset.id},#{changeset.id},#{closed_changeset.id}" }
1708     assert_response :success, "can't get changesets by id (as comma-separated string)"
1709     assert_changesets [private_user_changeset, changeset, closed_changeset]
1710
1711     get :query, :params => { :changesets => "" }
1712     assert_response :bad_request, "should be a bad request since changesets is empty"
1713   end
1714
1715   ##
1716   # check that errors are returned if garbage is inserted
1717   # into query strings
1718   def test_query_invalid
1719     ["abracadabra!",
1720      "1,2,3,F",
1721      ";drop table users;"].each do |bbox|
1722       get :query, :params => { :bbox => bbox }
1723       assert_response :bad_request, "'#{bbox}' isn't a bbox"
1724     end
1725
1726     ["now()",
1727      "00-00-00",
1728      ";drop table users;",
1729      ",",
1730      "-,-"].each do |time|
1731       get :query, :params => { :time => time }
1732       assert_response :bad_request, "'#{time}' isn't a valid time range"
1733     end
1734
1735     ["me",
1736      "foobar",
1737      "-1",
1738      "0"].each do |uid|
1739       get :query, :params => { :user => uid }
1740       assert_response :bad_request, "'#{uid}' isn't a valid user ID"
1741     end
1742   end
1743
1744   ##
1745   # check updating tags on a changeset
1746   def test_changeset_update
1747     private_user = create(:user, :data_public => false)
1748     private_changeset = create(:changeset, :user => private_user)
1749     user = create(:user)
1750     changeset = create(:changeset, :user => user)
1751
1752     ## First try with a non-public user
1753     new_changeset = private_changeset.to_xml
1754     new_tag = XML::Node.new "tag"
1755     new_tag["k"] = "tagtesting"
1756     new_tag["v"] = "valuetesting"
1757     new_changeset.find("//osm/changeset").first << new_tag
1758     content new_changeset
1759
1760     # try without any authorization
1761     put :update, :params => { :id => private_changeset.id }
1762     assert_response :unauthorized
1763
1764     # try with the wrong authorization
1765     basic_authorization create(:user).email, "test"
1766     put :update, :params => { :id => private_changeset.id }
1767     assert_response :conflict
1768
1769     # now this should get an unauthorized
1770     basic_authorization private_user.email, "test"
1771     put :update, :params => { :id => private_changeset.id }
1772     assert_require_public_data "user with their data non-public, shouldn't be able to edit their changeset"
1773
1774     ## Now try with the public user
1775     create(:changeset_tag, :changeset => changeset)
1776     new_changeset = changeset.to_xml
1777     new_tag = XML::Node.new "tag"
1778     new_tag["k"] = "tagtesting"
1779     new_tag["v"] = "valuetesting"
1780     new_changeset.find("//osm/changeset").first << new_tag
1781     content new_changeset
1782
1783     # try without any authorization
1784     @request.env["HTTP_AUTHORIZATION"] = nil
1785     put :update, :params => { :id => changeset.id }
1786     assert_response :unauthorized
1787
1788     # try with the wrong authorization
1789     basic_authorization create(:user).email, "test"
1790     put :update, :params => { :id => changeset.id }
1791     assert_response :conflict
1792
1793     # now this should work...
1794     basic_authorization user.email, "test"
1795     put :update, :params => { :id => changeset.id }
1796     assert_response :success
1797
1798     assert_select "osm>changeset[id='#{changeset.id}']", 1
1799     assert_select "osm>changeset>tag", 2
1800     assert_select "osm>changeset>tag[k='tagtesting'][v='valuetesting']", 1
1801   end
1802
1803   ##
1804   # check that a user different from the one who opened the changeset
1805   # can't modify it.
1806   def test_changeset_update_invalid
1807     basic_authorization create(:user).email, "test"
1808
1809     changeset = create(:changeset)
1810     new_changeset = changeset.to_xml
1811     new_tag = XML::Node.new "tag"
1812     new_tag["k"] = "testing"
1813     new_tag["v"] = "testing"
1814     new_changeset.find("//osm/changeset").first << new_tag
1815
1816     content new_changeset
1817     put :update, :params => { :id => changeset.id }
1818     assert_response :conflict
1819   end
1820
1821   ##
1822   # check that a changeset can contain a certain max number of changes.
1823   ## FIXME should be changed to an integration test due to the with_controller
1824   def test_changeset_limits
1825     basic_authorization create(:user).email, "test"
1826
1827     # open a new changeset
1828     content "<osm><changeset/></osm>"
1829     put :create
1830     assert_response :success, "can't create a new changeset"
1831     cs_id = @response.body.to_i
1832
1833     # start the counter just short of where the changeset should finish.
1834     offset = 10
1835     # alter the database to set the counter on the changeset directly,
1836     # otherwise it takes about 6 minutes to fill all of them.
1837     changeset = Changeset.find(cs_id)
1838     changeset.num_changes = Changeset::MAX_ELEMENTS - offset
1839     changeset.save!
1840
1841     with_controller(NodeController.new) do
1842       # create a new node
1843       content "<osm><node changeset='#{cs_id}' lat='0.0' lon='0.0'/></osm>"
1844       put :create
1845       assert_response :success, "can't create a new node"
1846       node_id = @response.body.to_i
1847
1848       get :read, :params => { :id => node_id }
1849       assert_response :success, "can't read back new node"
1850       node_doc = XML::Parser.string(@response.body).parse
1851       node_xml = node_doc.find("//osm/node").first
1852
1853       # loop until we fill the changeset with nodes
1854       offset.times do |i|
1855         node_xml["lat"] = rand.to_s
1856         node_xml["lon"] = rand.to_s
1857         node_xml["version"] = (i + 1).to_s
1858
1859         content node_doc
1860         put :update, :params => { :id => node_id }
1861         assert_response :success, "attempt #{i} should have succeeded"
1862       end
1863
1864       # trying again should fail
1865       node_xml["lat"] = rand.to_s
1866       node_xml["lon"] = rand.to_s
1867       node_xml["version"] = offset.to_s
1868
1869       content node_doc
1870       put :update, :params => { :id => node_id }
1871       assert_response :conflict, "final attempt should have failed"
1872     end
1873
1874     changeset = Changeset.find(cs_id)
1875     assert_equal Changeset::MAX_ELEMENTS + 1, changeset.num_changes
1876
1877     # check that the changeset is now closed as well
1878     assert(!changeset.is_open?,
1879            "changeset should have been auto-closed by exceeding " \
1880            "element limit.")
1881   end
1882
1883   ##
1884   # This should display the last 20 changesets closed
1885   def test_list
1886     get :list, :params => { :format => "html" }
1887     assert_response :success
1888     assert_template "history"
1889     assert_template :layout => "map"
1890     assert_select "h2", :text => "Changesets", :count => 1
1891
1892     get :list, :params => { :format => "html", :list => "1" }, :xhr => true
1893     assert_response :success
1894     assert_template "list"
1895
1896     check_list_result(Changeset.all)
1897   end
1898
1899   ##
1900   # This should display the last 20 changesets closed
1901   def test_list_xhr
1902     get :list, :params => { :format => "html" }, :xhr => true
1903     assert_response :success
1904     assert_template "history"
1905     assert_template :layout => "xhr"
1906     assert_select "h2", :text => "Changesets", :count => 1
1907
1908     get :list, :params => { :format => "html", :list => "1" }, :xhr => true
1909     assert_response :success
1910     assert_template "list"
1911
1912     check_list_result(Changeset.all)
1913   end
1914
1915   ##
1916   # This should display the last 20 changesets closed in a specific area
1917   def test_list_bbox
1918     get :list, :params => { :format => "html", :bbox => "4.5,4.5,5.5,5.5" }
1919     assert_response :success
1920     assert_template "history"
1921     assert_template :layout => "map"
1922     assert_select "h2", :text => "Changesets", :count => 1
1923
1924     get :list, :params => { :format => "html", :bbox => "4.5,4.5,5.5,5.5", :list => "1" }, :xhr => true
1925     assert_response :success
1926     assert_template "list"
1927
1928     check_list_result(Changeset.where("min_lon < 55000000 and max_lon > 45000000 and min_lat < 55000000 and max_lat > 45000000"))
1929   end
1930
1931   ##
1932   # Checks the display of the user changesets listing
1933   def test_list_user
1934     user = create(:user)
1935     create(:changeset, :user => user)
1936     create(:changeset, :closed, :user => user)
1937
1938     get :list, :params => { :format => "html", :display_name => user.display_name }
1939     assert_response :success
1940     assert_template "history"
1941
1942     get :list, :params => { :format => "html", :display_name => user.display_name, :list => "1" }, :xhr => true
1943     assert_response :success
1944     assert_template "list"
1945
1946     check_list_result(user.changesets)
1947   end
1948
1949   ##
1950   # Checks the display of the user changesets listing for a private user
1951   def test_list_private_user
1952     private_user = create(:user, :data_public => false)
1953     create(:changeset, :user => private_user)
1954     create(:changeset, :closed, :user => private_user)
1955
1956     get :list, :params => { :format => "html", :display_name => private_user.display_name }
1957     assert_response :success
1958     assert_template "history"
1959
1960     get :list, :params => { :format => "html", :display_name => private_user.display_name, :list => "1" }, :xhr => true
1961     assert_response :success
1962     assert_template "list"
1963
1964     check_list_result(Changeset.none)
1965   end
1966
1967   ##
1968   # Check the not found of the list user changesets
1969   def test_list_user_not_found
1970     get :list, :params => { :format => "html", :display_name => "Some random user" }
1971     assert_response :not_found
1972     assert_template "user/no_such_user"
1973
1974     get :list, :params => { :format => "html", :display_name => "Some random user", :list => "1" }, :xhr => true
1975     assert_response :not_found
1976     assert_template "user/no_such_user"
1977   end
1978
1979   ##
1980   # Checks the display of the friends changesets listing
1981   def test_list_friends
1982     private_user = create(:user, :data_public => true)
1983     friend = create(:friend, :befriender => private_user)
1984     create(:changeset, :user => friend.befriendee)
1985
1986     get :list, :params => { :friends => true }
1987     assert_response :redirect
1988     assert_redirected_to :controller => :user, :action => :login, :referer => friend_changesets_path
1989
1990     session[:user] = private_user.id
1991
1992     get :list, :params => { :friends => true }
1993     assert_response :success
1994     assert_template "history"
1995
1996     get :list, :params => { :friends => true, :list => "1" }, :xhr => true
1997     assert_response :success
1998     assert_template "list"
1999
2000     check_list_result(Changeset.where(:user => private_user.friend_users.identifiable))
2001   end
2002
2003   ##
2004   # Checks the display of the nearby user changesets listing
2005   def test_list_nearby
2006     private_user = create(:user, :data_public => false, :home_lat => 51.1, :home_lon => 1.0)
2007     user = create(:user, :home_lat => 51.0, :home_lon => 1.0)
2008     create(:changeset, :user => user)
2009
2010     get :list, :params => { :nearby => true }
2011     assert_response :redirect
2012     assert_redirected_to :controller => :user, :action => :login, :referer => nearby_changesets_path
2013
2014     session[:user] = private_user.id
2015
2016     get :list, :params => { :nearby => true }
2017     assert_response :success
2018     assert_template "history"
2019
2020     get :list, :params => { :nearby => true, :list => "1" }, :xhr => true
2021     assert_response :success
2022     assert_template "list"
2023
2024     check_list_result(Changeset.where(:user => user.nearby))
2025   end
2026
2027   ##
2028   # Check that we can't request later pages of the changesets list
2029   def test_list_max_id
2030     get :list, :params => { :format => "html", :max_id => 4 }, :xhr => true
2031     assert_response :success
2032     assert_template "history"
2033     assert_template :layout => "xhr"
2034     assert_select "h2", :text => "Changesets", :count => 1
2035
2036     get :list, :params => { :format => "html", :list => "1", :max_id => 4 }, :xhr => true
2037     assert_response :success
2038     assert_template "list"
2039
2040     check_list_result(Changeset.where("id <= 4"))
2041   end
2042
2043   ##
2044   # Check that a list with a next page link works
2045   def test_list_more
2046     create_list(:changeset, 50)
2047
2048     get :list, :params => { :format => "html" }
2049     assert_response :success
2050
2051     get :list, :params => { :format => "html" }, :xhr => true
2052     assert_response :success
2053   end
2054
2055   ##
2056   # This should display the last 20 non-empty changesets
2057   def test_feed
2058     changeset = create(:changeset, :num_changes => 1)
2059     create(:changeset_tag, :changeset => changeset)
2060     create(:changeset_tag, :changeset => changeset, :k => "website", :v => "http://example.com/")
2061     closed_changeset = create(:changeset, :closed, :num_changes => 1)
2062     _empty_changeset = create(:changeset, :num_changes => 0)
2063
2064     get :feed, :params => { :format => :atom }
2065     assert_response :success
2066     assert_template "list"
2067     assert_equal "application/atom+xml", response.content_type
2068
2069     check_feed_result([changeset, closed_changeset])
2070   end
2071
2072   ##
2073   # This should display the last 20 changesets closed in a specific area
2074   def test_feed_bbox
2075     changeset = create(:changeset, :num_changes => 1, :min_lat => 5 * GeoRecord::SCALE, :min_lon => 5 * GeoRecord::SCALE, :max_lat => 5 * GeoRecord::SCALE, :max_lon => 5 * GeoRecord::SCALE)
2076     create(:changeset_tag, :changeset => changeset)
2077     create(:changeset_tag, :changeset => changeset, :k => "website", :v => "http://example.com/")
2078     closed_changeset = create(:changeset, :closed, :num_changes => 1, :min_lat => 5 * GeoRecord::SCALE, :min_lon => 5 * GeoRecord::SCALE, :max_lat => 5 * GeoRecord::SCALE, :max_lon => 5 * GeoRecord::SCALE)
2079     _elsewhere_changeset = create(:changeset, :num_changes => 1, :min_lat => -5 * GeoRecord::SCALE, :min_lon => -5 * GeoRecord::SCALE, :max_lat => -5 * GeoRecord::SCALE, :max_lon => -5 * GeoRecord::SCALE)
2080     _empty_changeset = create(:changeset, :num_changes => 0, :min_lat => -5 * GeoRecord::SCALE, :min_lon => -5 * GeoRecord::SCALE, :max_lat => -5 * GeoRecord::SCALE, :max_lon => -5 * GeoRecord::SCALE)
2081
2082     get :feed, :params => { :format => :atom, :bbox => "4.5,4.5,5.5,5.5" }
2083     assert_response :success
2084     assert_template "list"
2085     assert_equal "application/atom+xml", response.content_type
2086
2087     check_feed_result([changeset, closed_changeset])
2088   end
2089
2090   ##
2091   # Checks the display of the user changesets feed
2092   def test_feed_user
2093     user = create(:user)
2094     changesets = create_list(:changeset, 3, :user => user, :num_changes => 4)
2095     create(:changeset_tag, :changeset => changesets[1])
2096     create(:changeset_tag, :changeset => changesets[1], :k => "website", :v => "http://example.com/")
2097     _other_changeset = create(:changeset)
2098
2099     get :feed, :params => { :format => :atom, :display_name => user.display_name }
2100
2101     assert_response :success
2102     assert_template "list"
2103     assert_equal "application/atom+xml", response.content_type
2104
2105     check_feed_result(changesets)
2106   end
2107
2108   ##
2109   # Check the not found of the user changesets feed
2110   def test_feed_user_not_found
2111     get :feed, :params => { :format => "atom", :display_name => "Some random user" }
2112     assert_response :not_found
2113   end
2114
2115   ##
2116   # Check that we can't request later pages of the changesets feed
2117   def test_feed_max_id
2118     get :feed, :params => { :format => "atom", :max_id => 100 }
2119     assert_response :redirect
2120     assert_redirected_to :action => :feed
2121   end
2122
2123   ##
2124   # check that the changeset download for a changeset with a redacted
2125   # element in it doesn't contain that element.
2126   def test_diff_download_redacted
2127     changeset = create(:changeset)
2128     node = create(:node, :with_history, :version => 2, :changeset => changeset)
2129     node_v1 = node.old_nodes.find_by(:version => 1)
2130     node_v1.redact!(create(:redaction))
2131
2132     get :download, :params => { :id => changeset.id }
2133     assert_response :success
2134
2135     assert_select "osmChange", 1
2136     # this changeset contains the node in versions 1 & 2, but 1 should
2137     # be hidden.
2138     assert_select "osmChange node[id='#{node.id}']", 1
2139     assert_select "osmChange node[id='#{node.id}'][version='1']", 0
2140   end
2141
2142   ##
2143   # create comment success
2144   def test_create_comment_success
2145     user = create(:user)
2146     user2 = create(:user)
2147     private_user = create(:user, :data_public => false)
2148     suspended_user = create(:user, :suspended)
2149     deleted_user = create(:user, :deleted)
2150     private_user_closed_changeset = create(:changeset, :closed, :user => private_user)
2151
2152     basic_authorization user.email, "test"
2153
2154     assert_difference "ChangesetComment.count", 1 do
2155       assert_no_difference "ActionMailer::Base.deliveries.size" do
2156         post :comment, :params => { :id => private_user_closed_changeset.id, :text => "This is a comment" }
2157       end
2158     end
2159     assert_response :success
2160
2161     changeset = create(:changeset, :closed, :user => private_user)
2162     changeset.subscribers.push(private_user)
2163     changeset.subscribers.push(user)
2164     changeset.subscribers.push(suspended_user)
2165     changeset.subscribers.push(deleted_user)
2166
2167     assert_difference "ChangesetComment.count", 1 do
2168       assert_difference "ActionMailer::Base.deliveries.size", 1 do
2169         post :comment, :params => { :id => changeset.id, :text => "This is a comment" }
2170       end
2171     end
2172     assert_response :success
2173
2174     email = ActionMailer::Base.deliveries.first
2175     assert_equal 1, email.to.length
2176     assert_equal "[OpenStreetMap] #{user.display_name} has commented on one of your changesets", email.subject
2177     assert_equal private_user.email, email.to.first
2178
2179     ActionMailer::Base.deliveries.clear
2180
2181     basic_authorization user2.email, "test"
2182
2183     assert_difference "ChangesetComment.count", 1 do
2184       assert_difference "ActionMailer::Base.deliveries.size", 2 do
2185         post :comment, :params => { :id => changeset.id, :text => "This is a comment" }
2186       end
2187     end
2188     assert_response :success
2189
2190     email = ActionMailer::Base.deliveries.find { |e| e.to.first == private_user.email }
2191     assert_not_nil email
2192     assert_equal 1, email.to.length
2193     assert_equal "[OpenStreetMap] #{user2.display_name} has commented on one of your changesets", email.subject
2194
2195     email = ActionMailer::Base.deliveries.find { |e| e.to.first == user.email }
2196     assert_not_nil email
2197     assert_equal 1, email.to.length
2198     assert_equal "[OpenStreetMap] #{user2.display_name} has commented on a changeset you are interested in", email.subject
2199
2200     ActionMailer::Base.deliveries.clear
2201   end
2202
2203   ##
2204   # create comment fail
2205   def test_create_comment_fail
2206     # unauthorized
2207     post :comment, :params => { :id => create(:changeset, :closed).id, :text => "This is a comment" }
2208     assert_response :unauthorized
2209
2210     basic_authorization create(:user).email, "test"
2211
2212     # bad changeset id
2213     assert_no_difference "ChangesetComment.count" do
2214       post :comment, :params => { :id => 999111, :text => "This is a comment" }
2215     end
2216     assert_response :not_found
2217
2218     # not closed changeset
2219     assert_no_difference "ChangesetComment.count" do
2220       post :comment, :params => { :id => create(:changeset).id, :text => "This is a comment" }
2221     end
2222     assert_response :conflict
2223
2224     # no text
2225     assert_no_difference "ChangesetComment.count" do
2226       post :comment, :params => { :id => create(:changeset, :closed).id }
2227     end
2228     assert_response :bad_request
2229
2230     # empty text
2231     assert_no_difference "ChangesetComment.count" do
2232       post :comment, :params => { :id => create(:changeset, :closed).id, :text => "" }
2233     end
2234     assert_response :bad_request
2235   end
2236
2237   ##
2238   # test subscribe success
2239   def test_subscribe_success
2240     basic_authorization create(:user).email, "test"
2241     changeset = create(:changeset, :closed)
2242
2243     assert_difference "changeset.subscribers.count", 1 do
2244       post :subscribe, :params => { :id => changeset.id }
2245     end
2246     assert_response :success
2247
2248     # not closed changeset
2249     changeset = create(:changeset)
2250     assert_difference "changeset.subscribers.count", 1 do
2251       post :subscribe, :params => { :id => changeset.id }
2252     end
2253     assert_response :success
2254   end
2255
2256   ##
2257   # test subscribe fail
2258   def test_subscribe_fail
2259     user = create(:user)
2260
2261     # unauthorized
2262     changeset = create(:changeset, :closed)
2263     assert_no_difference "changeset.subscribers.count" do
2264       post :subscribe, :params => { :id => changeset.id }
2265     end
2266     assert_response :unauthorized
2267
2268     basic_authorization user.email, "test"
2269
2270     # bad changeset id
2271     assert_no_difference "changeset.subscribers.count" do
2272       post :subscribe, :params => { :id => 999111 }
2273     end
2274     assert_response :not_found
2275
2276     # trying to subscribe when already subscribed
2277     changeset = create(:changeset, :closed)
2278     changeset.subscribers.push(user)
2279     assert_no_difference "changeset.subscribers.count" do
2280       post :subscribe, :params => { :id => changeset.id }
2281     end
2282     assert_response :conflict
2283   end
2284
2285   ##
2286   # test unsubscribe success
2287   def test_unsubscribe_success
2288     user = create(:user)
2289     basic_authorization user.email, "test"
2290     changeset = create(:changeset, :closed)
2291     changeset.subscribers.push(user)
2292
2293     assert_difference "changeset.subscribers.count", -1 do
2294       post :unsubscribe, :params => { :id => changeset.id }
2295     end
2296     assert_response :success
2297
2298     # not closed changeset
2299     changeset = create(:changeset)
2300     changeset.subscribers.push(user)
2301
2302     assert_difference "changeset.subscribers.count", -1 do
2303       post :unsubscribe, :params => { :id => changeset.id }
2304     end
2305     assert_response :success
2306   end
2307
2308   ##
2309   # test unsubscribe fail
2310   def test_unsubscribe_fail
2311     # unauthorized
2312     changeset = create(:changeset, :closed)
2313     assert_no_difference "changeset.subscribers.count" do
2314       post :unsubscribe, :params => { :id => changeset.id }
2315     end
2316     assert_response :unauthorized
2317
2318     basic_authorization create(:user).email, "test"
2319
2320     # bad changeset id
2321     assert_no_difference "changeset.subscribers.count" do
2322       post :unsubscribe, :params => { :id => 999111 }
2323     end
2324     assert_response :not_found
2325
2326     # trying to unsubscribe when not subscribed
2327     changeset = create(:changeset, :closed)
2328     assert_no_difference "changeset.subscribers.count" do
2329       post :unsubscribe, :params => { :id => changeset.id }
2330     end
2331     assert_response :not_found
2332   end
2333
2334   ##
2335   # test hide comment fail
2336   def test_hide_comment_fail
2337     # unauthorized
2338     comment = create(:changeset_comment)
2339     assert_equal true, comment.visible
2340
2341     post :hide_comment, :params => { :id => comment.id }
2342     assert_response :unauthorized
2343     assert_equal true, comment.reload.visible
2344
2345     basic_authorization create(:user).email, "test"
2346
2347     # not a moderator
2348     post :hide_comment, :params => { :id => comment.id }
2349     assert_response :forbidden
2350     assert_equal true, comment.reload.visible
2351
2352     basic_authorization create(:moderator_user).email, "test"
2353
2354     # bad comment id
2355     post :hide_comment, :params => { :id => 999111 }
2356     assert_response :not_found
2357     assert_equal true, comment.reload.visible
2358   end
2359
2360   ##
2361   # test hide comment succes
2362   def test_hide_comment_success
2363     comment = create(:changeset_comment)
2364     assert_equal true, comment.visible
2365
2366     basic_authorization create(:moderator_user).email, "test"
2367
2368     post :hide_comment, :params => { :id => comment.id }
2369     assert_response :success
2370     assert_equal false, comment.reload.visible
2371   end
2372
2373   ##
2374   # test unhide comment fail
2375   def test_unhide_comment_fail
2376     # unauthorized
2377     comment = create(:changeset_comment, :visible => false)
2378     assert_equal false, comment.visible
2379
2380     post :unhide_comment, :params => { :id => comment.id }
2381     assert_response :unauthorized
2382     assert_equal false, comment.reload.visible
2383
2384     basic_authorization create(:user).email, "test"
2385
2386     # not a moderator
2387     post :unhide_comment, :params => { :id => comment.id }
2388     assert_response :forbidden
2389     assert_equal false, comment.reload.visible
2390
2391     basic_authorization create(:moderator_user).email, "test"
2392
2393     # bad comment id
2394     post :unhide_comment, :params => { :id => 999111 }
2395     assert_response :not_found
2396     assert_equal false, comment.reload.visible
2397   end
2398
2399   ##
2400   # test unhide comment succes
2401   def test_unhide_comment_success
2402     comment = create(:changeset_comment, :visible => false)
2403     assert_equal false, comment.visible
2404
2405     basic_authorization create(:moderator_user).email, "test"
2406
2407     post :unhide_comment, :params => { :id => comment.id }
2408     assert_response :success
2409     assert_equal true, comment.reload.visible
2410   end
2411
2412   ##
2413   # test comments feed
2414   def test_comments_feed
2415     changeset = create(:changeset, :closed)
2416     create_list(:changeset_comment, 3, :changeset => changeset)
2417
2418     get :comments_feed, :params => { :format => "rss" }
2419     assert_response :success
2420     assert_equal "application/rss+xml", @response.content_type
2421     assert_select "rss", :count => 1 do
2422       assert_select "channel", :count => 1 do
2423         assert_select "item", :count => 3
2424       end
2425     end
2426
2427     get :comments_feed, :params => { :format => "rss", :limit => 2 }
2428     assert_response :success
2429     assert_equal "application/rss+xml", @response.content_type
2430     assert_select "rss", :count => 1 do
2431       assert_select "channel", :count => 1 do
2432         assert_select "item", :count => 2
2433       end
2434     end
2435
2436     get :comments_feed, :params => { :id => changeset.id, :format => "rss" }
2437     assert_response :success
2438     assert_equal "application/rss+xml", @response.content_type
2439     assert_select "rss", :count => 1 do
2440       assert_select "channel", :count => 1 do
2441         assert_select "item", :count => 3
2442       end
2443     end
2444   end
2445
2446   ##
2447   # test comments feed
2448   def test_comments_feed_bad_limit
2449     get :comments_feed, :params => { :format => "rss", :limit => 0 }
2450     assert_response :bad_request
2451
2452     get :comments_feed, :params => { :format => "rss", :limit => 100001 }
2453     assert_response :bad_request
2454   end
2455
2456   private
2457
2458   ##
2459   # boilerplate for checking that certain changesets exist in the
2460   # output.
2461   def assert_changesets(changesets)
2462     assert_select "osm>changeset", changesets.size
2463     changesets.each do |changeset|
2464       assert_select "osm>changeset[id='#{changeset.id}']", 1
2465     end
2466   end
2467
2468   ##
2469   # call the include method and assert properties of the bbox
2470   def check_after_include(changeset_id, lon, lat, bbox)
2471     content "<osm><node lon='#{lon}' lat='#{lat}'/></osm>"
2472     post :expand_bbox, :params => { :id => changeset_id }
2473     assert_response :success, "Setting include of changeset failed: #{@response.body}"
2474
2475     # check exactly one changeset
2476     assert_select "osm>changeset", 1
2477     assert_select "osm>changeset[id='#{changeset_id}']", 1
2478
2479     # check the bbox
2480     doc = XML::Parser.string(@response.body).parse
2481     changeset = doc.find("//osm/changeset").first
2482     assert_equal bbox[0], changeset["min_lon"].to_f, "min lon"
2483     assert_equal bbox[1], changeset["min_lat"].to_f, "min lat"
2484     assert_equal bbox[2], changeset["max_lon"].to_f, "max lon"
2485     assert_equal bbox[3], changeset["max_lat"].to_f, "max lat"
2486   end
2487
2488   ##
2489   # update the changeset_id of a way element
2490   def update_changeset(xml, changeset_id)
2491     xml_attr_rewrite(xml, "changeset", changeset_id)
2492   end
2493
2494   ##
2495   # update an attribute in a way element
2496   def xml_attr_rewrite(xml, name, value)
2497     xml.find("//osm/way").first[name] = value.to_s
2498     xml
2499   end
2500
2501   ##
2502   # check the result of a list
2503   def check_list_result(changesets)
2504     changesets = changesets.where("num_changes > 0")
2505                            .order(:created_at => :desc)
2506                            .limit(20)
2507     assert changesets.size <= 20
2508
2509     assert_select "ol.changesets", :count => [changesets.size, 1].min do
2510       assert_select "li", :count => changesets.size
2511
2512       changesets.each do |changeset|
2513         assert_select "li#changeset_#{changeset.id}", :count => 1
2514       end
2515     end
2516   end
2517
2518   ##
2519   # check the result of a feed
2520   def check_feed_result(changesets)
2521     assert changesets.size <= 20
2522
2523     assert_select "feed", :count => [changesets.size, 1].min do
2524       assert_select "> title", :count => 1, :text => /^Changesets/
2525       assert_select "> entry", :count => changesets.size
2526
2527       changesets.each do |changeset|
2528         assert_select "> entry > id", changeset_url(:id => changeset.id)
2529       end
2530     end
2531   end
2532 end