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