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