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