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