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
52 # test that the user who opened a change can close it
54 basic_authorization "test@openstreetmap.org", "test"
56 put :close, :id => changesets(:normal_user_first_change).id
57 assert_response :success
61 # test that a different user can't close another user's changeset
62 def test_close_invalid
63 basic_authorization "test@example.com", "test"
65 put :close, :id => changesets(:normal_user_first_change).id
66 assert_response :conflict
70 # upload something simple, but valid and check that it can
72 def test_upload_simple_valid
73 basic_authorization "test@openstreetmap.org", "test"
75 # simple diff to change a node, way and relation by removing
80 <node id='1' lon='0' lat='0' changeset='1' version='1'/>
81 <way id='1' changeset='1' version='1'>
86 <relation id='1' changeset='1' version='1'>
87 <member type='way' role='some' ref='3'/>
88 <member type='node' role='some' ref='5'/>
89 <member type='relation' role='some' ref='3'/>
97 post :upload, :id => 1
98 assert_response :success,
99 "can't upload a simple valid diff to changeset: #{@response.body}"
101 # check that the changes made it into the database
102 assert_equal 0, Node.find(1).tags.size, "node 1 should now have no tags"
103 assert_equal 0, Way.find(1).tags.size, "way 1 should now have no tags"
104 assert_equal 0, Relation.find(1).tags.size, "relation 1 should now have no tags"
108 # upload something which creates new objects using placeholders
109 def test_upload_create_valid
110 basic_authorization "test@openstreetmap.org", "test"
112 # simple diff to create a node way and relation using placeholders
116 <node id='-1' lon='0' lat='0' changeset='1'>
117 <tag k='foo' v='bar'/>
118 <tag k='baz' v='bat'/>
120 <way id='-1' changeset='1'>
125 <relation id='-1' changeset='1'>
126 <member type='way' role='some' ref='3'/>
127 <member type='node' role='some' ref='5'/>
128 <member type='relation' role='some' ref='3'/>
136 post :upload, :id => 1
137 assert_response :success,
138 "can't upload a simple valid creation to changeset: #{@response.body}"
140 # check the returned payload
141 assert_select "diffResult[version=#{API_VERSION}][generator=\"OpenStreetMap server\"]", 1
142 assert_select "diffResult>node", 1
143 assert_select "diffresult>way", 1
144 assert_select "diffResult>relation", 1
146 # inspect the response to find out what the new element IDs are
147 doc = XML::Parser.string(@response.body).parse
148 new_node_id = doc.find("//diffResult/node").first["new_id"].to_i
149 new_way_id = doc.find("//diffResult/way").first["new_id"].to_i
150 new_rel_id = doc.find("//diffResult/relation").first["new_id"].to_i
152 # check the old IDs are all present and negative one
153 assert_equal -1, doc.find("//diffResult/node").first["old_id"].to_i
154 assert_equal -1, doc.find("//diffResult/way").first["old_id"].to_i
155 assert_equal -1, doc.find("//diffResult/relation").first["old_id"].to_i
157 # check the versions are present and equal one
158 assert_equal 1, doc.find("//diffResult/node").first["new_version"].to_i
159 assert_equal 1, doc.find("//diffResult/way").first["new_version"].to_i
160 assert_equal 1, doc.find("//diffResult/relation").first["new_version"].to_i
162 # check that the changes made it into the database
163 assert_equal 2, Node.find(new_node_id).tags.size, "new node should have two tags"
164 assert_equal 0, Way.find(new_way_id).tags.size, "new way should have no tags"
165 assert_equal 0, Relation.find(new_rel_id).tags.size, "new relation should have no tags"
169 # test a complex delete where we delete elements which rely on eachother
170 # in the same transaction.
171 def test_upload_delete
172 basic_authorization "test@openstreetmap.org", "test"
174 diff = XML::Document.new
175 diff.root = XML::Node.new "osmChange"
176 delete = XML::Node.new "delete"
178 delete << current_relations(:visible_relation).to_xml_node
179 delete << current_relations(:used_relation).to_xml_node
180 delete << current_ways(:used_way).to_xml_node
181 delete << current_nodes(:node_used_by_relationship).to_xml_node
185 post :upload, :id => 1
186 assert_response :success,
187 "can't upload a deletion diff to changeset: #{@response.body}"
189 # check that everything was deleted
190 assert_equal false, Node.find(current_nodes(:node_used_by_relationship).id).visible
191 assert_equal false, Way.find(current_ways(:used_way).id).visible
192 assert_equal false, Relation.find(current_relations(:visible_relation).id).visible
193 assert_equal false, Relation.find(current_relations(:used_relation).id).visible
197 # test that deleting stuff in a transaction doesn't bypass the checks
198 # to ensure that used elements are not deleted.
199 def test_upload_delete_invalid
200 basic_authorization "test@openstreetmap.org", "test"
202 diff = XML::Document.new
203 diff.root = XML::Node.new "osmChange"
204 delete = XML::Node.new "delete"
206 delete << current_relations(:visible_relation).to_xml_node
207 delete << current_ways(:used_way).to_xml_node
208 delete << current_nodes(:node_used_by_relationship).to_xml_node
212 post :upload, :id => 1
213 assert_response :precondition_failed,
214 "shouldn't be able to upload a invalid deletion diff: #{@response.body}"
216 # check that nothing was, in fact, deleted
217 assert_equal true, Node.find(current_nodes(:node_used_by_relationship).id).visible
218 assert_equal true, Way.find(current_ways(:used_way).id).visible
219 assert_equal true, Relation.find(current_relations(:visible_relation).id).visible
223 # upload something which creates new objects and inserts them into
224 # existing containers using placeholders.
225 def test_upload_complex
226 basic_authorization "test@openstreetmap.org", "test"
228 # simple diff to create a node way and relation using placeholders
232 <node id='-1' lon='0' lat='0' changeset='1'>
233 <tag k='foo' v='bar'/>
234 <tag k='baz' v='bat'/>
238 <way id='1' changeset='1' version='1'>
242 <relation id='1' changeset='1' version='1'>
243 <member type='way' role='some' ref='3'/>
244 <member type='node' role='some' ref='-1'/>
245 <member type='relation' role='some' ref='3'/>
253 post :upload, :id => 1
254 assert_response :success,
255 "can't upload a complex diff to changeset: #{@response.body}"
257 # check the returned payload
258 assert_select "diffResult[version=#{API_VERSION}][generator=\"#{GENERATOR}\"]", 1
259 assert_select "diffResult>node", 1
260 assert_select "diffResult>way", 1
261 assert_select "diffResult>relation", 1
263 # inspect the response to find out what the new element IDs are
264 doc = XML::Parser.string(@response.body).parse
265 new_node_id = doc.find("//diffResult/node").first["new_id"].to_i
267 # check that the changes made it into the database
268 assert_equal 2, Node.find(new_node_id).tags.size, "new node should have two tags"
269 assert_equal [new_node_id, 3], Way.find(1).nds, "way nodes should match"
270 Relation.find(1).members.each do |type,id,role|
272 assert_equal new_node_id, id, "relation should contain new node"
278 # create a diff which references several changesets, which should cause
279 # a rollback and none of the diff gets committed
280 def test_upload_invalid_changesets
281 basic_authorization "test@openstreetmap.org", "test"
283 # simple diff to create a node way and relation using placeholders
287 <node id='1' lon='0' lat='0' changeset='1' version='1'/>
288 <way id='1' changeset='1' version='1'>
293 <relation id='1' changeset='1' version='1'>
294 <member type='way' role='some' ref='3'/>
295 <member type='node' role='some' ref='5'/>
296 <member type='relation' role='some' ref='3'/>
300 <node id='-1' changeset='4'>
301 <tag k='foo' v='bar'/>
302 <tag k='baz' v='bat'/>
307 # cache the objects before uploading them
308 node = current_nodes(:visible_node)
309 way = current_ways(:visible_way)
310 rel = current_relations(:visible_relation)
314 post :upload, :id => 1
315 assert_response :conflict,
316 "uploading a diff with multiple changsets should have failed"
318 # check that objects are unmodified
319 assert_nodes_are_equal(node, Node.find(1))
320 assert_ways_are_equal(way, Way.find(1))
324 # upload multiple versions of the same element in the same diff.
325 def test_upload_multiple_valid
326 basic_authorization "test@openstreetmap.org", "test"
328 # change the location of a node multiple times, each time referencing
329 # the last version. doesn't this depend on version numbers being
334 <node id='1' lon='0' lat='0' changeset='1' version='1'/>
335 <node id='1' lon='1' lat='0' changeset='1' version='2'/>
336 <node id='1' lon='1' lat='1' changeset='1' version='3'/>
337 <node id='1' lon='1' lat='2' changeset='1' version='4'/>
338 <node id='1' lon='2' lat='2' changeset='1' version='5'/>
339 <node id='1' lon='3' lat='2' changeset='1' version='6'/>
340 <node id='1' lon='3' lat='3' changeset='1' version='7'/>
341 <node id='1' lon='9' lat='9' changeset='1' version='8'/>
348 post :upload, :id => 1
349 assert_response :success,
350 "can't upload multiple versions of an element in a diff: #{@response.body}"
354 # upload multiple versions of the same element in the same diff, but
355 # keep the version numbers the same.
356 def test_upload_multiple_duplicate
357 basic_authorization "test@openstreetmap.org", "test"
362 <node id='1' lon='0' lat='0' changeset='1' version='1'/>
363 <node id='1' lon='1' lat='1' changeset='1' version='1'/>
370 post :upload, :id => 1
371 assert_response :conflict,
372 "shouldn't be able to upload the same element twice in a diff: #{@response.body}"
376 # try to upload some elements without specifying the version
377 def test_upload_missing_version
378 basic_authorization "test@openstreetmap.org", "test"
383 <node id='1' lon='1' lat='1' changeset='1'/>
390 post :upload, :id => 1
391 assert_response :bad_request,
392 "shouldn't be able to upload an element without version: #{@response.body}"
396 # try to upload with commands other than create, modify, or delete
397 def test_action_upload_invalid
398 basic_authorization "test@openstreetmap.org", "test"
403 <node id='1' lon='1' lat='1' changeset='1' />
408 post :upload, :id => 1
409 assert_response :bad_request, "Shouldn't be able to upload a diff with the action ping"
410 assert_equal @response.body, "Unknown action ping, choices are create, modify, delete."
414 # when we make some simple changes we get the same changes back from the
416 def test_diff_download_simple
417 basic_authorization(users(:normal_user).email, "test")
419 # create a temporary changeset
420 content "<osm><changeset>" +
421 "<tag k='created_by' v='osm test suite checking changesets'/>" +
424 assert_response :success
425 changeset_id = @response.body.to_i
431 <node id='1' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
432 <node id='1' lon='1' lat='0' changeset='#{changeset_id}' version='2'/>
433 <node id='1' lon='1' lat='1' changeset='#{changeset_id}' version='3'/>
434 <node id='1' lon='1' lat='2' changeset='#{changeset_id}' version='4'/>
435 <node id='1' lon='2' lat='2' changeset='#{changeset_id}' version='5'/>
436 <node id='1' lon='3' lat='2' changeset='#{changeset_id}' version='6'/>
437 <node id='1' lon='3' lat='3' changeset='#{changeset_id}' version='7'/>
438 <node id='1' lon='9' lat='9' changeset='#{changeset_id}' version='8'/>
445 post :upload, :id => changeset_id
446 assert_response :success,
447 "can't upload multiple versions of an element in a diff: #{@response.body}"
449 get :download, :id => changeset_id
450 assert_response :success
452 assert_select "osmChange", 1
453 assert_select "osmChange>modify", 8
454 assert_select "osmChange>modify>node", 8
458 # when we make some complex changes we get the same changes back from the
460 def test_diff_download_complex
461 basic_authorization(users(:normal_user).email, "test")
463 # create a temporary changeset
464 content "<osm><changeset>" +
465 "<tag k='created_by' v='osm test suite checking changesets'/>" +
468 assert_response :success
469 changeset_id = @response.body.to_i
475 <node id='1' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
478 <node id='-1' lon='9' lat='9' changeset='#{changeset_id}' version='0'/>
479 <node id='-2' lon='8' lat='9' changeset='#{changeset_id}' version='0'/>
480 <node id='-3' lon='7' lat='9' changeset='#{changeset_id}' version='0'/>
483 <node id='3' lon='20' lat='15' changeset='#{changeset_id}' version='1'/>
484 <way id='1' changeset='#{changeset_id}' version='1'>
496 post :upload, :id => changeset_id
497 assert_response :success,
498 "can't upload multiple versions of an element in a diff: #{@response.body}"
500 get :download, :id => changeset_id
501 assert_response :success
503 assert_select "osmChange", 1
504 assert_select "osmChange>create", 3
505 assert_select "osmChange>delete", 1
506 assert_select "osmChange>modify", 2
507 assert_select "osmChange>create>node", 3
508 assert_select "osmChange>delete>node", 1
509 assert_select "osmChange>modify>node", 1
510 assert_select "osmChange>modify>way", 1
514 # check that the bounding box of a changeset gets updated correctly
515 def test_changeset_bbox
516 basic_authorization "test@openstreetmap.org", "test"
518 # create a new changeset
519 content "<osm><changeset/></osm>"
521 assert_response :success, "Creating of changeset failed."
522 changeset_id = @response.body.to_i
524 # add a single node to it
525 with_controller(NodeController.new) do
526 content "<osm><node lon='1' lat='2' changeset='#{changeset_id}'/></osm>"
528 assert_response :success, "Couldn't create node."
531 # get the bounding box back from the changeset
532 get :read, :id => changeset_id
533 assert_response :success, "Couldn't read back changeset."
534 assert_select "osm>changeset[min_lon=1.0]", 1
535 assert_select "osm>changeset[max_lon=1.0]", 1
536 assert_select "osm>changeset[min_lat=2.0]", 1
537 assert_select "osm>changeset[max_lat=2.0]", 1
539 # add another node to it
540 with_controller(NodeController.new) do
541 content "<osm><node lon='2' lat='1' changeset='#{changeset_id}'/></osm>"
543 assert_response :success, "Couldn't create second node."
546 # get the bounding box back from the changeset
547 get :read, :id => changeset_id
548 assert_response :success, "Couldn't read back changeset for the second time."
549 assert_select "osm>changeset[min_lon=1.0]", 1
550 assert_select "osm>changeset[max_lon=2.0]", 1
551 assert_select "osm>changeset[min_lat=1.0]", 1
552 assert_select "osm>changeset[max_lat=2.0]", 1
554 # add (delete) a way to it
555 with_controller(WayController.new) do
556 content update_changeset(current_ways(:visible_way).to_xml,
558 put :delete, :id => current_ways(:visible_way).id
559 assert_response :success, "Couldn't delete a way."
562 # get the bounding box back from the changeset
563 get :read, :id => changeset_id
564 assert_response :success, "Couldn't read back changeset for the third time."
565 assert_select "osm>changeset[min_lon=1.0]", 1
566 assert_select "osm>changeset[max_lon=3.1]", 1
567 assert_select "osm>changeset[min_lat=1.0]", 1
568 assert_select "osm>changeset[max_lat=3.1]", 1
572 # test that the changeset :include method works as it should
573 def test_changeset_include
574 basic_authorization "test@openstreetmap.org", "test"
576 # create a new changeset
577 content "<osm><changeset/></osm>"
579 assert_response :success, "Creating of changeset failed."
580 changeset_id = @response.body.to_i
582 # NOTE: the include method doesn't over-expand, like inserting
583 # a real method does. this is because we expect the client to
584 # know what it is doing!
585 check_after_include(changeset_id, 1, 1, [ 1, 1, 1, 1])
586 check_after_include(changeset_id, 3, 3, [ 1, 1, 3, 3])
587 check_after_include(changeset_id, 4, 2, [ 1, 1, 4, 3])
588 check_after_include(changeset_id, 2, 2, [ 1, 1, 4, 3])
589 check_after_include(changeset_id, -1, -1, [-1, -1, 4, 3])
590 check_after_include(changeset_id, -2, 5, [-2, -1, 4, 5])
594 # check searching for changesets by bbox
595 def test_changeset_by_bbox
596 get :query, :bbox => "-10,-10, 10, 10"
597 assert_response :success, "can't get changesets in bbox"
598 # FIXME: write the actual test bit after fixing the fixtures!
602 # check updating tags on a changeset
603 def test_changeset_update
604 basic_authorization "test@openstreetmap.org", "test"
606 changeset = changesets(:normal_user_first_change)
607 new_changeset = changeset.to_xml
608 new_tag = XML::Node.new "tag"
609 new_tag['k'] = "testing"
610 new_tag['v'] = "testing"
611 new_changeset.find("//osm/changeset").first << new_tag
613 content new_changeset
614 put :update, :id => changeset.id
615 assert_response :success
617 assert_select "osm>changeset[id=#{changeset.id}]", 1
618 assert_select "osm>changeset>tag", 2
619 assert_select "osm>changeset>tag[k=testing][v=testing]", 1
623 # check that a user different from the one who opened the changeset
625 def test_changeset_update_invalid
626 basic_authorization "test@example.com", "test"
628 changeset = changesets(:normal_user_first_change)
629 new_changeset = changeset.to_xml
630 new_tag = XML::Node.new "tag"
631 new_tag['k'] = "testing"
632 new_tag['v'] = "testing"
633 new_changeset.find("//osm/changeset").first << new_tag
635 content new_changeset
636 put :update, :id => changeset.id
637 assert_response :conflict
640 #------------------------------------------------------------
642 #------------------------------------------------------------
645 # call the include method and assert properties of the bbox
646 def check_after_include(changeset_id, lon, lat, bbox)
647 content "<osm><node lon='#{lon}' lat='#{lat}'/></osm>"
648 post :include, :id => changeset_id
649 assert_response :success, "Setting include of changeset failed: #{@response.body}"
651 # check exactly one changeset
652 assert_select "osm>changeset", 1
653 assert_select "osm>changeset[id=#{changeset_id}]", 1
656 doc = XML::Parser.string(@response.body).parse
657 changeset = doc.find("//osm/changeset").first
658 assert_equal bbox[0], changeset['min_lon'].to_f, "min lon"
659 assert_equal bbox[1], changeset['min_lat'].to_f, "min lat"
660 assert_equal bbox[2], changeset['max_lon'].to_f, "max lon"
661 assert_equal bbox[3], changeset['max_lat'].to_f, "max lat"
665 # update the changeset_id of a way element
666 def update_changeset(xml, changeset_id)
667 xml_attr_rewrite(xml, 'changeset', changeset_id)
671 # update an attribute in a way element
672 def xml_attr_rewrite(xml, name, value)
673 xml.find("//osm/way").first[name] = value.to_s