1 require File.dirname(__FILE__) + '/../test_helper'
2 require 'changeset_controller'
4 class ChangesetControllerTest < ActionController::TestCase
7 def basic_authorization(user, pass)
8 @request.env["HTTP_AUTHORIZATION"] = "Basic %s" % Base64.encode64("#{user}:#{pass}")
12 @request.env["RAW_POST_DATA"] = c.to_s
15 # -----------------------
16 # Test simple changeset creation
17 # -----------------------
20 basic_authorization "test@openstreetmap.org", "test"
22 # Create the first user's changeset
23 content "<osm><changeset>" +
24 "<tag k='created_by' v='osm test suite checking changesets'/>" +
28 assert_response :success, "Creation of changeset did not return sucess status"
29 newid = @response.body
32 def test_create_invalid
33 basic_authorization "test@openstreetmap.org", "test"
34 content "<osm><changeset></osm>"
36 assert_response :bad_request, "creating a invalid changeset should fail"
40 # check that the changeset can be read and returns the correct
43 changeset_id = changesets(:normal_user_first_change).id
44 get :read, :id => changeset_id
45 assert_response :success, "cannot get first changeset"
47 assert_select "osm[version=#{API_VERSION}][generator=\"OpenStreetMap server\"]", 1
48 assert_select "osm>changeset[id=#{changeset_id}]", 1
56 # upload something simple, but valid and check that it can
58 def test_upload_simple_valid
59 basic_authorization "test@openstreetmap.org", "test"
61 # simple diff to change a node, way and relation by removing
66 <node id='1' lon='0' lat='0' changeset='1' version='1'/>
67 <way id='1' changeset='1' version='1'>
72 <relation id='1' changeset='1' version='1'>
73 <member type='way' role='some' ref='3'/>
74 <member type='node' role='some' ref='5'/>
75 <member type='relation' role='some' ref='3'/>
83 post :upload, :id => 1
84 assert_response :success,
85 "can't upload a simple valid diff to changeset: #{@response.body}"
87 # check that the changes made it into the database
88 assert_equal 0, Node.find(1).tags.size, "node 1 should now have no tags"
89 assert_equal 0, Way.find(1).tags.size, "way 1 should now have no tags"
90 assert_equal 0, Relation.find(1).tags.size, "relation 1 should now have no tags"
94 # upload something which creates new objects using placeholders
95 def test_upload_create_valid
96 basic_authorization "test@openstreetmap.org", "test"
98 # simple diff to create a node way and relation using placeholders
102 <node id='-1' lon='0' lat='0' changeset='1'>
103 <tag k='foo' v='bar'/>
104 <tag k='baz' v='bat'/>
106 <way id='-1' changeset='1'>
111 <relation id='-1' changeset='1'>
112 <member type='way' role='some' ref='3'/>
113 <member type='node' role='some' ref='5'/>
114 <member type='relation' role='some' ref='3'/>
122 post :upload, :id => 1
123 assert_response :success,
124 "can't upload a simple valid creation to changeset: #{@response.body}"
126 # check the returned payload
127 assert_select "osm[version=#{API_VERSION}][generator=\"OpenStreetMap server\"]", 1
128 assert_select "osm>node", 1
129 assert_select "osm>way", 1
130 assert_select "osm>relation", 1
132 # inspect the response to find out what the new element IDs are
133 doc = XML::Parser.string(@response.body).parse
134 new_node_id = doc.find("//osm/node").first["new_id"].to_i
135 new_way_id = doc.find("//osm/way").first["new_id"].to_i
136 new_rel_id = doc.find("//osm/relation").first["new_id"].to_i
138 # check the old IDs are all present and negative one
139 assert_equal -1, doc.find("//osm/node").first["old_id"].to_i
140 assert_equal -1, doc.find("//osm/way").first["old_id"].to_i
141 assert_equal -1, doc.find("//osm/relation").first["old_id"].to_i
143 # check the versions are present and equal one
144 assert_equal 1, doc.find("//osm/node").first["new_version"].to_i
145 assert_equal 1, doc.find("//osm/way").first["new_version"].to_i
146 assert_equal 1, doc.find("//osm/relation").first["new_version"].to_i
148 # check that the changes made it into the database
149 assert_equal 2, Node.find(new_node_id).tags.size, "new node should have two tags"
150 assert_equal 0, Way.find(new_way_id).tags.size, "new way should have no tags"
151 assert_equal 0, Relation.find(new_rel_id).tags.size, "new relation should have no tags"
155 # test a complex delete where we delete elements which rely on eachother
156 # in the same transaction.
157 def test_upload_delete
158 basic_authorization "test@openstreetmap.org", "test"
160 diff = XML::Document.new
161 diff.root = XML::Node.new "osmChange"
162 delete = XML::Node.new "delete"
164 delete << current_relations(:visible_relation).to_xml_node
165 delete << current_relations(:used_relation).to_xml_node
166 delete << current_ways(:used_way).to_xml_node
167 delete << current_nodes(:node_used_by_relationship).to_xml_node
171 post :upload, :id => 1
172 assert_response :success,
173 "can't upload a deletion diff to changeset: #{@response.body}"
175 # check that everything was deleted
176 assert_equal false, Node.find(current_nodes(:node_used_by_relationship).id).visible
177 assert_equal false, Way.find(current_ways(:used_way).id).visible
178 assert_equal false, Relation.find(current_relations(:visible_relation).id).visible
179 assert_equal false, Relation.find(current_relations(:used_relation).id).visible
183 # test that deleting stuff in a transaction doesn't bypass the checks
184 # to ensure that used elements are not deleted.
185 def test_upload_delete_invalid
186 basic_authorization "test@openstreetmap.org", "test"
188 diff = XML::Document.new
189 diff.root = XML::Node.new "osmChange"
190 delete = XML::Node.new "delete"
192 delete << current_relations(:visible_relation).to_xml_node
193 delete << current_ways(:used_way).to_xml_node
194 delete << current_nodes(:node_used_by_relationship).to_xml_node
198 post :upload, :id => 1
199 assert_response :precondition_failed,
200 "shouldn't be able to upload a invalid deletion diff: #{@response.body}"
202 # check that nothing was, in fact, deleted
203 assert_equal true, Node.find(current_nodes(:node_used_by_relationship).id).visible
204 assert_equal true, Way.find(current_ways(:used_way).id).visible
205 assert_equal true, Relation.find(current_relations(:visible_relation).id).visible
209 # upload something which creates new objects and inserts them into
210 # existing containers using placeholders.
211 def test_upload_complex
212 basic_authorization "test@openstreetmap.org", "test"
214 # simple diff to create a node way and relation using placeholders
218 <node id='-1' lon='0' lat='0' changeset='1'>
219 <tag k='foo' v='bar'/>
220 <tag k='baz' v='bat'/>
224 <way id='1' changeset='1' version='1'>
228 <relation id='1' changeset='1' version='1'>
229 <member type='way' role='some' ref='3'/>
230 <member type='node' role='some' ref='-1'/>
231 <member type='relation' role='some' ref='3'/>
239 post :upload, :id => 1
240 assert_response :success,
241 "can't upload a complex diff to changeset: #{@response.body}"
243 # check the returned payload
244 assert_select "osm[version=#{API_VERSION}][generator=\"#{GENERATOR}\"]", 1
245 assert_select "osm>node", 1
246 assert_select "osm>way", 1
247 assert_select "osm>relation", 1
249 # inspect the response to find out what the new element IDs are
250 doc = XML::Parser.string(@response.body).parse
251 new_node_id = doc.find("//osm/node").first["new_id"].to_i
253 # check that the changes made it into the database
254 assert_equal 2, Node.find(new_node_id).tags.size, "new node should have two tags"
255 assert_equal [new_node_id, 3], Way.find(1).nds, "way nodes should match"
256 Relation.find(1).members.each do |type,id,role|
258 assert_equal new_node_id, id, "relation should contain new node"
264 # create a diff which references several changesets, which should cause
265 # a rollback and none of the diff gets committed
266 def test_upload_invalid_changesets
267 basic_authorization "test@openstreetmap.org", "test"
269 # simple diff to create a node way and relation using placeholders
273 <node id='1' lon='0' lat='0' changeset='1' version='1'/>
274 <way id='1' changeset='1' version='1'>
279 <relation id='1' changeset='1' version='1'>
280 <member type='way' role='some' ref='3'/>
281 <member type='node' role='some' ref='5'/>
282 <member type='relation' role='some' ref='3'/>
286 <node id='-1' changeset='4'>
287 <tag k='foo' v='bar'/>
288 <tag k='baz' v='bat'/>
293 # cache the objects before uploading them
294 node = current_nodes(:visible_node)
295 way = current_ways(:visible_way)
296 rel = current_relations(:visible_relation)
300 post :upload, :id => 1
301 assert_response :conflict,
302 "uploading a diff with multiple changsets should have failed"
304 # check that objects are unmodified
305 assert_nodes_are_equal(node, Node.find(1))
306 assert_ways_are_equal(way, Way.find(1))
310 # upload multiple versions of the same element in the same diff.
311 def test_upload_multiple_valid
312 basic_authorization "test@openstreetmap.org", "test"
314 # change the location of a node multiple times, each time referencing
315 # the last version. doesn't this depend on version numbers being
320 <node id='1' lon='0' lat='0' changeset='1' version='1'/>
321 <node id='1' lon='1' lat='0' changeset='1' version='2'/>
322 <node id='1' lon='1' lat='1' changeset='1' version='3'/>
323 <node id='1' lon='1' lat='2' changeset='1' version='4'/>
324 <node id='1' lon='2' lat='2' changeset='1' version='5'/>
325 <node id='1' lon='3' lat='2' changeset='1' version='6'/>
326 <node id='1' lon='3' lat='3' changeset='1' version='7'/>
327 <node id='1' lon='9' lat='9' changeset='1' version='8'/>
334 post :upload, :id => 1
335 assert_response :success,
336 "can't upload multiple versions of an element in a diff: #{@response.body}"
340 # upload multiple versions of the same element in the same diff, but
341 # keep the version numbers the same.
342 def test_upload_multiple_duplicate
343 basic_authorization "test@openstreetmap.org", "test"
348 <node id='1' lon='0' lat='0' changeset='1' version='1'/>
349 <node id='1' lon='1' lat='1' changeset='1' version='1'/>
356 post :upload, :id => 1
357 assert_response :conflict,
358 "shouldn't be able to upload the same element twice in a diff: #{@response.body}"
362 # try to upload some elements without specifying the version
363 def test_upload_missing_version
364 basic_authorization "test@openstreetmap.org", "test"
369 <node id='1' lon='1' lat='1' changeset='1'/>
376 post :upload, :id => 1
377 assert_response :bad_request,
378 "shouldn't be able to upload an element without version: #{@response.body}"
382 # try to upload with commands other than create, modify, or delete
383 def test_action_upload_invalid
384 basic_authorization "test@openstreetmap.org", "test"
389 <node id='1' lon='1' lat='1' changeset='1' />
394 post :upload, :id => 1
395 assert_response :bad_request, "Shouldn't be able to upload a diff with the action ping"
396 assert_equal @response.body, "Unknown action ping, choices are create, modify, delete."
400 # when we make some simple changes we get the same changes back from the
402 def test_diff_download_simple
403 basic_authorization(users(:normal_user).email, "test")
405 # create a temporary changeset
406 content "<osm><changeset>" +
407 "<tag k='created_by' v='osm test suite checking changesets'/>" +
410 assert_response :success
411 changeset_id = @response.body.to_i
417 <node id='1' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
418 <node id='1' lon='1' lat='0' changeset='#{changeset_id}' version='2'/>
419 <node id='1' lon='1' lat='1' changeset='#{changeset_id}' version='3'/>
420 <node id='1' lon='1' lat='2' changeset='#{changeset_id}' version='4'/>
421 <node id='1' lon='2' lat='2' changeset='#{changeset_id}' version='5'/>
422 <node id='1' lon='3' lat='2' changeset='#{changeset_id}' version='6'/>
423 <node id='1' lon='3' lat='3' changeset='#{changeset_id}' version='7'/>
424 <node id='1' lon='9' lat='9' changeset='#{changeset_id}' version='8'/>
431 post :upload, :id => changeset_id
432 assert_response :success,
433 "can't upload multiple versions of an element in a diff: #{@response.body}"
435 get :download, :id => changeset_id
436 assert_response :success
438 assert_select "osmChange", 1
439 assert_select "osmChange>modify", 8
440 assert_select "osmChange>modify>node", 8
444 # when we make some complex changes we get the same changes back from the
446 def test_diff_download_complex
447 basic_authorization(users(:normal_user).email, "test")
449 # create a temporary changeset
450 content "<osm><changeset>" +
451 "<tag k='created_by' v='osm test suite checking changesets'/>" +
454 assert_response :success
455 changeset_id = @response.body.to_i
461 <node id='1' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
464 <node id='-1' lon='9' lat='9' changeset='#{changeset_id}' version='0'/>
465 <node id='-2' lon='8' lat='9' changeset='#{changeset_id}' version='0'/>
466 <node id='-3' lon='7' lat='9' changeset='#{changeset_id}' version='0'/>
469 <node id='3' lon='20' lat='15' changeset='#{changeset_id}' version='1'/>
470 <way id='1' changeset='#{changeset_id}' version='1'>
482 post :upload, :id => changeset_id
483 assert_response :success,
484 "can't upload multiple versions of an element in a diff: #{@response.body}"
486 get :download, :id => changeset_id
487 assert_response :success
489 assert_select "osmChange", 1
490 assert_select "osmChange>create", 3
491 assert_select "osmChange>delete", 1
492 assert_select "osmChange>modify", 2
493 assert_select "osmChange>create>node", 3
494 assert_select "osmChange>delete>node", 1
495 assert_select "osmChange>modify>node", 1
496 assert_select "osmChange>modify>way", 1
500 # check that the bounding box of a changeset gets updated correctly
501 def test_changeset_bbox
502 basic_authorization "test@openstreetmap.org", "test"
504 # create a new changeset
505 content "<osm><changeset/></osm>"
507 assert_response :success, "Creating of changeset failed."
508 changeset_id = @response.body.to_i
510 # add a single node to it
511 with_controller(NodeController.new) do
512 content "<osm><node lon='1' lat='2' changeset='#{changeset_id}'/></osm>"
514 assert_response :success, "Couldn't create node."
517 # get the bounding box back from the changeset
518 get :read, :id => changeset_id
519 assert_response :success, "Couldn't read back changeset."
520 assert_select "osm>changeset[min_lon=1]", 1
521 assert_select "osm>changeset[max_lon=1]", 1
522 assert_select "osm>changeset[min_lat=2]", 1
523 assert_select "osm>changeset[max_lat=2]", 1
525 # add another node to it
526 with_controller(NodeController.new) do
527 content "<osm><node lon='2' lat='1' changeset='#{changeset_id}'/></osm>"
529 assert_response :success, "Couldn't create second node."
532 # get the bounding box back from the changeset
533 get :read, :id => changeset_id
534 assert_response :success, "Couldn't read back changeset for the second time."
535 assert_select "osm>changeset[min_lon=1]", 1
536 assert_select "osm>changeset[max_lon=2]", 1
537 assert_select "osm>changeset[min_lat=1]", 1
538 assert_select "osm>changeset[max_lat=2]", 1
540 # add (delete) a way to it
541 with_controller(WayController.new) do
542 content update_changeset(current_ways(:visible_way).to_xml,
544 put :delete, :id => current_ways(:visible_way).id
545 assert_response :success, "Couldn't delete a way."
548 # get the bounding box back from the changeset
549 get :read, :id => changeset_id
550 assert_response :success, "Couldn't read back changeset for the third time."
551 assert_select "osm>changeset[min_lon=1]", 1
552 assert_select "osm>changeset[max_lon=3]", 1
553 assert_select "osm>changeset[min_lat=1]", 1
554 assert_select "osm>changeset[max_lat=3]", 1
558 # test that the changeset :include method works as it should
559 def test_changeset_include
560 basic_authorization "test@openstreetmap.org", "test"
562 # create a new changeset
563 content "<osm><changeset/></osm>"
565 assert_response :success, "Creating of changeset failed."
566 changeset_id = @response.body.to_i
568 # NOTE: the include method doesn't over-expand, like inserting
569 # a real method does. this is because we expect the client to
570 # know what it is doing!
571 check_after_include(changeset_id, 1, 1, [ 1, 1, 1, 1])
572 check_after_include(changeset_id, 3, 3, [ 1, 1, 3, 3])
573 check_after_include(changeset_id, 4, 2, [ 1, 1, 4, 3])
574 check_after_include(changeset_id, 2, 2, [ 1, 1, 4, 3])
575 check_after_include(changeset_id, -1, -1, [-1, -1, 4, 3])
576 check_after_include(changeset_id, -2, 5, [-2, -1, 4, 5])
579 #------------------------------------------------------------
581 #------------------------------------------------------------
584 # call the include method and assert properties of the bbox
585 def check_after_include(changeset_id, lon, lat, bbox)
586 content "<osm><node lon='#{lon}' lat='#{lat}'/></osm>"
587 post :include, :id => changeset_id
588 assert_response :success, "Setting include of changeset failed: #{@response.body}"
590 # check exactly one changeset
591 assert_select "osm>changeset", 1
592 assert_select "osm>changeset[id=#{changeset_id}]", 1
595 doc = XML::Parser.string(@response.body).parse
596 changeset = doc.find("//osm/changeset").first
597 assert_equal bbox[0], changeset['min_lon'].to_f, "min lon"
598 assert_equal bbox[1], changeset['min_lat'].to_f, "min lat"
599 assert_equal bbox[2], changeset['max_lon'].to_f, "max lon"
600 assert_equal bbox[3], changeset['max_lat'].to_f, "max lat"
604 # update the changeset_id of a way element
605 def update_changeset(xml, changeset_id)
606 xml_attr_rewrite(xml, 'changeset', changeset_id)
610 # update an attribute in a way element
611 def xml_attr_rewrite(xml, name, value)
612 xml.find("//osm/way").first[name] = value.to_s