Verify that the key and value isn't getting mixed up by having them different. verify...
[rails.git] / test / functional / changeset_controller_test.rb
1 require File.dirname(__FILE__) + '/../test_helper'
2 require 'changeset_controller'
3
4 class ChangesetControllerTest < ActionController::TestCase
5   api_fixtures
6
7   def basic_authorization(user, pass)
8     @request.env["HTTP_AUTHORIZATION"] = "Basic %s" % Base64.encode64("#{user}:#{pass}")
9   end
10
11   def content(c)
12     @request.env["RAW_POST_DATA"] = c.to_s
13   end
14   
15   # -----------------------
16   # Test simple changeset creation
17   # -----------------------
18   
19   def test_create
20     basic_authorization "test@openstreetmap.org", "test"
21     
22     # Create the first user's changeset
23     content "<osm><changeset>" +
24       "<tag k='created_by' v='osm test suite checking changesets'/>" + 
25       "</changeset></osm>"
26     put :create
27     
28     assert_response :success, "Creation of changeset did not return sucess status"
29     newid = @response.body
30   end
31   
32   def test_create_invalid
33     basic_authorization "test@openstreetmap.org", "test"
34     content "<osm><changeset></osm>"
35     put :create
36     assert_response :bad_request, "creating a invalid changeset should fail"
37   end
38
39   ##
40   # check that the changeset can be read and returns the correct
41   # document structure.
42   def test_read
43     changeset_id = changesets(:normal_user_first_change).id
44     get :read, :id => changeset_id
45     assert_response :success, "cannot get first changeset"
46     
47     assert_select "osm[version=#{API_VERSION}][generator=\"OpenStreetMap server\"]", 1
48     assert_select "osm>changeset[id=#{changeset_id}]", 1
49   end
50   
51   ##
52   # test that the user who opened a change can close it
53   def test_close
54     basic_authorization "test@openstreetmap.org", "test"
55
56     put :close, :id => changesets(:normal_user_first_change).id
57     assert_response :success
58   end
59
60   ##
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"
64
65     put :close, :id => changesets(:normal_user_first_change).id
66     assert_response :conflict
67     assert_equal "The user doesn't own that changeset", @response.body
68   end
69
70   ##
71   # upload something simple, but valid and check that it can 
72   # be read back ok.
73   def test_upload_simple_valid
74     basic_authorization "test@openstreetmap.org", "test"
75
76     # simple diff to change a node, way and relation by removing 
77     # their tags
78     diff = <<EOF
79 <osmChange>
80  <modify>
81   <node id='1' lon='0' lat='0' changeset='1' version='1'/>
82   <way id='1' changeset='1' version='1'>
83    <nd ref='3'/>
84   </way>
85  </modify>
86  <modify>
87   <relation id='1' changeset='1' version='1'>
88    <member type='way' role='some' ref='3'/>
89    <member type='node' role='some' ref='5'/>
90    <member type='relation' role='some' ref='3'/>
91   </relation>
92  </modify>
93 </osmChange>
94 EOF
95
96     # upload it
97     content diff
98     post :upload, :id => 1
99     assert_response :success, 
100       "can't upload a simple valid diff to changeset: #{@response.body}"
101
102     # check that the changes made it into the database
103     assert_equal 0, Node.find(1).tags.size, "node 1 should now have no tags"
104     assert_equal 0, Way.find(1).tags.size, "way 1 should now have no tags"
105     assert_equal 0, Relation.find(1).tags.size, "relation 1 should now have no tags"
106   end
107     
108   ##
109   # upload something which creates new objects using placeholders
110   def test_upload_create_valid
111     basic_authorization "test@openstreetmap.org", "test"
112
113     # simple diff to create a node way and relation using placeholders
114     diff = <<EOF
115 <osmChange>
116  <create>
117   <node id='-1' lon='0' lat='0' changeset='1'>
118    <tag k='foo' v='bar'/>
119    <tag k='baz' v='bat'/>
120   </node>
121   <way id='-1' changeset='1'>
122    <nd ref='3'/>
123   </way>
124  </create>
125  <create>
126   <relation id='-1' changeset='1'>
127    <member type='way' role='some' ref='3'/>
128    <member type='node' role='some' ref='5'/>
129    <member type='relation' role='some' ref='3'/>
130   </relation>
131  </create>
132 </osmChange>
133 EOF
134
135     # upload it
136     content diff
137     post :upload, :id => 1
138     assert_response :success, 
139       "can't upload a simple valid creation to changeset: #{@response.body}"
140
141     # check the returned payload
142     assert_select "diffResult[version=#{API_VERSION}][generator=\"OpenStreetMap server\"]", 1
143     assert_select "diffResult>node", 1
144     assert_select "diffresult>way", 1
145     assert_select "diffResult>relation", 1
146
147     # inspect the response to find out what the new element IDs are
148     doc = XML::Parser.string(@response.body).parse
149     new_node_id = doc.find("//diffResult/node").first["new_id"].to_i
150     new_way_id = doc.find("//diffResult/way").first["new_id"].to_i
151     new_rel_id = doc.find("//diffResult/relation").first["new_id"].to_i
152
153     # check the old IDs are all present and negative one
154     assert_equal -1, doc.find("//diffResult/node").first["old_id"].to_i
155     assert_equal -1, doc.find("//diffResult/way").first["old_id"].to_i
156     assert_equal -1, doc.find("//diffResult/relation").first["old_id"].to_i
157
158     # check the versions are present and equal one
159     assert_equal 1, doc.find("//diffResult/node").first["new_version"].to_i
160     assert_equal 1, doc.find("//diffResult/way").first["new_version"].to_i
161     assert_equal 1, doc.find("//diffResult/relation").first["new_version"].to_i
162
163     # check that the changes made it into the database
164     assert_equal 2, Node.find(new_node_id).tags.size, "new node should have two tags"
165     assert_equal 0, Way.find(new_way_id).tags.size, "new way should have no tags"
166     assert_equal 0, Relation.find(new_rel_id).tags.size, "new relation should have no tags"
167   end
168     
169   ##
170   # test a complex delete where we delete elements which rely on eachother
171   # in the same transaction.
172   def test_upload_delete
173     basic_authorization "test@openstreetmap.org", "test"
174
175     diff = XML::Document.new
176     diff.root = XML::Node.new "osmChange"
177     delete = XML::Node.new "delete"
178     diff.root << delete
179     delete << current_relations(:visible_relation).to_xml_node
180     delete << current_relations(:used_relation).to_xml_node
181     delete << current_ways(:used_way).to_xml_node
182     delete << current_nodes(:node_used_by_relationship).to_xml_node
183
184     # upload it
185     content diff
186     post :upload, :id => 1
187     assert_response :success, 
188       "can't upload a deletion diff to changeset: #{@response.body}"
189
190     # check that everything was deleted
191     assert_equal false, Node.find(current_nodes(:node_used_by_relationship).id).visible
192     assert_equal false, Way.find(current_ways(:used_way).id).visible
193     assert_equal false, Relation.find(current_relations(:visible_relation).id).visible
194     assert_equal false, Relation.find(current_relations(:used_relation).id).visible
195   end
196
197   ##
198   # test that deleting stuff in a transaction doesn't bypass the checks
199   # to ensure that used elements are not deleted.
200   def test_upload_delete_invalid
201     basic_authorization "test@openstreetmap.org", "test"
202
203     diff = XML::Document.new
204     diff.root = XML::Node.new "osmChange"
205     delete = XML::Node.new "delete"
206     diff.root << delete
207     delete << current_relations(:visible_relation).to_xml_node
208     delete << current_ways(:used_way).to_xml_node
209     delete << current_nodes(:node_used_by_relationship).to_xml_node
210
211     # upload it
212     content diff
213     post :upload, :id => 1
214     assert_response :precondition_failed, 
215       "shouldn't be able to upload a invalid deletion diff: #{@response.body}"
216
217     # check that nothing was, in fact, deleted
218     assert_equal true, Node.find(current_nodes(:node_used_by_relationship).id).visible
219     assert_equal true, Way.find(current_ways(:used_way).id).visible
220     assert_equal true, Relation.find(current_relations(:visible_relation).id).visible
221   end
222
223   ##
224   # upload something which creates new objects and inserts them into
225   # existing containers using placeholders.
226   def test_upload_complex
227     basic_authorization "test@openstreetmap.org", "test"
228
229     # simple diff to create a node way and relation using placeholders
230     diff = <<EOF
231 <osmChange>
232  <create>
233   <node id='-1' lon='0' lat='0' changeset='1'>
234    <tag k='foo' v='bar'/>
235    <tag k='baz' v='bat'/>
236   </node>
237  </create>
238  <modify>
239   <way id='1' changeset='1' version='1'>
240    <nd ref='-1'/>
241    <nd ref='3'/>
242   </way>
243   <relation id='1' changeset='1' version='1'>
244    <member type='way' role='some' ref='3'/>
245    <member type='node' role='some' ref='-1'/>
246    <member type='relation' role='some' ref='3'/>
247   </relation>
248  </modify>
249 </osmChange>
250 EOF
251
252     # upload it
253     content diff
254     post :upload, :id => 1
255     assert_response :success, 
256       "can't upload a complex diff to changeset: #{@response.body}"
257
258     # check the returned payload
259     assert_select "diffResult[version=#{API_VERSION}][generator=\"#{GENERATOR}\"]", 1
260     assert_select "diffResult>node", 1
261     assert_select "diffResult>way", 1
262     assert_select "diffResult>relation", 1
263
264     # inspect the response to find out what the new element IDs are
265     doc = XML::Parser.string(@response.body).parse
266     new_node_id = doc.find("//diffResult/node").first["new_id"].to_i
267
268     # check that the changes made it into the database
269     assert_equal 2, Node.find(new_node_id).tags.size, "new node should have two tags"
270     assert_equal [new_node_id, 3], Way.find(1).nds, "way nodes should match"
271     Relation.find(1).members.each do |type,id,role|
272       if type == 'node'
273         assert_equal new_node_id, id, "relation should contain new node"
274       end
275     end
276   end
277     
278   ##
279   # create a diff which references several changesets, which should cause
280   # a rollback and none of the diff gets committed
281   def test_upload_invalid_changesets
282     basic_authorization "test@openstreetmap.org", "test"
283
284     # simple diff to create a node way and relation using placeholders
285     diff = <<EOF
286 <osmChange>
287  <modify>
288   <node id='1' lon='0' lat='0' changeset='1' version='1'/>
289   <way id='1' changeset='1' version='1'>
290    <nd ref='3'/>
291   </way>
292  </modify>
293  <modify>
294   <relation id='1' changeset='1' version='1'>
295    <member type='way' role='some' ref='3'/>
296    <member type='node' role='some' ref='5'/>
297    <member type='relation' role='some' ref='3'/>
298   </relation>
299  </modify>
300  <create>
301   <node id='-1' changeset='4'>
302    <tag k='foo' v='bar'/>
303    <tag k='baz' v='bat'/>
304   </node>
305  </create>
306 </osmChange>
307 EOF
308     # cache the objects before uploading them
309     node = current_nodes(:visible_node)
310     way = current_ways(:visible_way)
311     rel = current_relations(:visible_relation)
312
313     # upload it
314     content diff
315     post :upload, :id => 1
316     assert_response :conflict, 
317       "uploading a diff with multiple changsets should have failed"
318
319     # check that objects are unmodified
320     assert_nodes_are_equal(node, Node.find(1))
321     assert_ways_are_equal(way, Way.find(1))
322   end
323     
324   ##
325   # upload multiple versions of the same element in the same diff.
326   def test_upload_multiple_valid
327     basic_authorization "test@openstreetmap.org", "test"
328
329     # change the location of a node multiple times, each time referencing
330     # the last version. doesn't this depend on version numbers being
331     # sequential?
332     diff = <<EOF
333 <osmChange>
334  <modify>
335   <node id='1' lon='0' lat='0' changeset='1' version='1'/>
336   <node id='1' lon='1' lat='0' changeset='1' version='2'/>
337   <node id='1' lon='1' lat='1' changeset='1' version='3'/>
338   <node id='1' lon='1' lat='2' changeset='1' version='4'/>
339   <node id='1' lon='2' lat='2' changeset='1' version='5'/>
340   <node id='1' lon='3' lat='2' changeset='1' version='6'/>
341   <node id='1' lon='3' lat='3' changeset='1' version='7'/>
342   <node id='1' lon='9' lat='9' changeset='1' version='8'/>
343  </modify>
344 </osmChange>
345 EOF
346
347     # upload it
348     content diff
349     post :upload, :id => 1
350     assert_response :success, 
351       "can't upload multiple versions of an element in a diff: #{@response.body}"
352   end
353
354   ##
355   # upload multiple versions of the same element in the same diff, but
356   # keep the version numbers the same.
357   def test_upload_multiple_duplicate
358     basic_authorization "test@openstreetmap.org", "test"
359
360     diff = <<EOF
361 <osmChange>
362  <modify>
363   <node id='1' lon='0' lat='0' changeset='1' version='1'/>
364   <node id='1' lon='1' lat='1' changeset='1' version='1'/>
365  </modify>
366 </osmChange>
367 EOF
368
369     # upload it
370     content diff
371     post :upload, :id => 1
372     assert_response :conflict, 
373       "shouldn't be able to upload the same element twice in a diff: #{@response.body}"
374   end
375
376   ##
377   # try to upload some elements without specifying the version
378   def test_upload_missing_version
379     basic_authorization "test@openstreetmap.org", "test"
380
381     diff = <<EOF
382 <osmChange>
383  <modify>
384   <node id='1' lon='1' lat='1' changeset='1'/>
385  </modify>
386 </osmChange>
387 EOF
388
389     # upload it
390     content diff
391     post :upload, :id => 1
392     assert_response :bad_request, 
393       "shouldn't be able to upload an element without version: #{@response.body}"
394   end
395   
396   ##
397   # try to upload with commands other than create, modify, or delete
398   def test_action_upload_invalid
399     basic_authorization "test@openstreetmap.org", "test"
400     
401     diff = <<EOF
402 <osmChange>
403   <ping>
404     <node id='1' lon='1' lat='1' changeset='1' />
405   </ping>
406 </osmChange>
407 EOF
408   content diff
409   post :upload, :id => 1
410   assert_response :bad_request, "Shouldn't be able to upload a diff with the action ping"
411   assert_equal @response.body, "Unknown action ping, choices are create, modify, delete."
412   end
413
414   ##
415   # when we make some simple changes we get the same changes back from the 
416   # diff download.
417   def test_diff_download_simple
418     basic_authorization(users(:normal_user).email, "test")
419
420     # create a temporary changeset
421     content "<osm><changeset>" +
422       "<tag k='created_by' v='osm test suite checking changesets'/>" + 
423       "</changeset></osm>"
424     put :create
425     assert_response :success
426     changeset_id = @response.body.to_i
427
428     # add a diff to it
429     diff = <<EOF
430 <osmChange>
431  <modify>
432   <node id='1' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
433   <node id='1' lon='1' lat='0' changeset='#{changeset_id}' version='2'/>
434   <node id='1' lon='1' lat='1' changeset='#{changeset_id}' version='3'/>
435   <node id='1' lon='1' lat='2' changeset='#{changeset_id}' version='4'/>
436   <node id='1' lon='2' lat='2' changeset='#{changeset_id}' version='5'/>
437   <node id='1' lon='3' lat='2' changeset='#{changeset_id}' version='6'/>
438   <node id='1' lon='3' lat='3' changeset='#{changeset_id}' version='7'/>
439   <node id='1' lon='9' lat='9' changeset='#{changeset_id}' version='8'/>
440  </modify>
441 </osmChange>
442 EOF
443
444     # upload it
445     content diff
446     post :upload, :id => changeset_id
447     assert_response :success, 
448       "can't upload multiple versions of an element in a diff: #{@response.body}"
449     
450     get :download, :id => changeset_id
451     assert_response :success
452
453     assert_select "osmChange", 1
454     assert_select "osmChange>modify", 8
455     assert_select "osmChange>modify>node", 8
456   end
457   
458   ##
459   # when we make some complex changes we get the same changes back from the 
460   # diff download.
461   def test_diff_download_complex
462     basic_authorization(users(:normal_user).email, "test")
463
464     # create a temporary changeset
465     content "<osm><changeset>" +
466       "<tag k='created_by' v='osm test suite checking changesets'/>" + 
467       "</changeset></osm>"
468     put :create
469     assert_response :success
470     changeset_id = @response.body.to_i
471
472     # add a diff to it
473     diff = <<EOF
474 <osmChange>
475  <delete>
476   <node id='1' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
477  </delete>
478  <create>
479   <node id='-1' lon='9' lat='9' changeset='#{changeset_id}' version='0'/>
480   <node id='-2' lon='8' lat='9' changeset='#{changeset_id}' version='0'/>
481   <node id='-3' lon='7' lat='9' changeset='#{changeset_id}' version='0'/>
482  </create>
483  <modify>
484   <node id='3' lon='20' lat='15' changeset='#{changeset_id}' version='1'/>
485   <way id='1' changeset='#{changeset_id}' version='1'>
486    <nd ref='3'/>
487    <nd ref='-1'/>
488    <nd ref='-2'/>
489    <nd ref='-3'/>
490   </way>
491  </modify>
492 </osmChange>
493 EOF
494
495     # upload it
496     content diff
497     post :upload, :id => changeset_id
498     assert_response :success, 
499       "can't upload multiple versions of an element in a diff: #{@response.body}"
500     
501     get :download, :id => changeset_id
502     assert_response :success
503
504     assert_select "osmChange", 1
505     assert_select "osmChange>create", 3
506     assert_select "osmChange>delete", 1
507     assert_select "osmChange>modify", 2
508     assert_select "osmChange>create>node", 3
509     assert_select "osmChange>delete>node", 1 
510     assert_select "osmChange>modify>node", 1
511     assert_select "osmChange>modify>way", 1
512   end
513
514   ##
515   # check that the bounding box of a changeset gets updated correctly
516   def test_changeset_bbox
517     basic_authorization "test@openstreetmap.org", "test"
518
519     # create a new changeset
520     content "<osm><changeset/></osm>"
521     put :create
522     assert_response :success, "Creating of changeset failed."
523     changeset_id = @response.body.to_i
524     
525     # add a single node to it
526     with_controller(NodeController.new) do
527       content "<osm><node lon='1' lat='2' changeset='#{changeset_id}'/></osm>"
528       put :create
529       assert_response :success, "Couldn't create node."
530     end
531
532     # get the bounding box back from the changeset
533     get :read, :id => changeset_id
534     assert_response :success, "Couldn't read back changeset."
535     assert_select "osm>changeset[min_lon=1.0]", 1
536     assert_select "osm>changeset[max_lon=1.0]", 1
537     assert_select "osm>changeset[min_lat=2.0]", 1
538     assert_select "osm>changeset[max_lat=2.0]", 1
539
540     # add another node to it
541     with_controller(NodeController.new) do
542       content "<osm><node lon='2' lat='1' changeset='#{changeset_id}'/></osm>"
543       put :create
544       assert_response :success, "Couldn't create second node."
545     end
546
547     # get the bounding box back from the changeset
548     get :read, :id => changeset_id
549     assert_response :success, "Couldn't read back changeset for the second time."
550     assert_select "osm>changeset[min_lon=1.0]", 1
551     assert_select "osm>changeset[max_lon=2.0]", 1
552     assert_select "osm>changeset[min_lat=1.0]", 1
553     assert_select "osm>changeset[max_lat=2.0]", 1
554
555     # add (delete) a way to it
556     with_controller(WayController.new) do
557       content update_changeset(current_ways(:visible_way).to_xml,
558                                changeset_id)
559       put :delete, :id => current_ways(:visible_way).id
560       assert_response :success, "Couldn't delete a way."
561     end
562
563     # get the bounding box back from the changeset
564     get :read, :id => changeset_id
565     assert_response :success, "Couldn't read back changeset for the third time."
566     assert_select "osm>changeset[min_lon=1.0]", 1
567     assert_select "osm>changeset[max_lon=3.1]", 1
568     assert_select "osm>changeset[min_lat=1.0]", 1
569     assert_select "osm>changeset[max_lat=3.1]", 1    
570   end
571
572   ##
573   # test that the changeset :include method works as it should
574   def test_changeset_include
575     basic_authorization "test@openstreetmap.org", "test"
576
577     # create a new changeset
578     content "<osm><changeset/></osm>"
579     put :create
580     assert_response :success, "Creating of changeset failed."
581     changeset_id = @response.body.to_i
582
583     # NOTE: the include method doesn't over-expand, like inserting
584     # a real method does. this is because we expect the client to 
585     # know what it is doing!
586     check_after_include(changeset_id,  1,  1, [ 1,  1,  1,  1])
587     check_after_include(changeset_id,  3,  3, [ 1,  1,  3,  3])
588     check_after_include(changeset_id,  4,  2, [ 1,  1,  4,  3])
589     check_after_include(changeset_id,  2,  2, [ 1,  1,  4,  3])
590     check_after_include(changeset_id, -1, -1, [-1, -1,  4,  3])
591     check_after_include(changeset_id, -2,  5, [-2, -1,  4,  5])
592   end
593
594   ##
595   # check searching for changesets by bbox
596   def test_changeset_by_bbox
597     get :query, :bbox => "-10,-10, 10, 10"
598     assert_response :success, "can't get changesets in bbox"
599     assert_changesets [1,4]
600
601     get :query, :bbox => "4.5,4.5,4.6,4.6"
602     assert_response :success, "can't get changesets in bbox"
603     assert_changesets [1]
604
605     # can't get changesets of user 1 without authenticating
606     get :query, :user => users(:normal_user).id
607     assert_response :not_found, "shouldn't be able to get changesets by non-public user"
608
609     # but this should work
610     basic_authorization "test@openstreetmap.org", "test"
611     get :query, :user => users(:normal_user).id
612     assert_response :success, "can't get changesets by user"
613     assert_changesets [1,3,4]
614
615     get :query, :user => users(:normal_user).id, :open => true
616     assert_response :success, "can't get changesets by user and open"
617     assert_changesets [1,4]
618
619     get :query, :time => '2007-12-31'
620     assert_response :success, "can't get changesets by time-since"
621     assert_changesets [2,4,5]
622
623     get :query, :time => '2008-01-01T12:34Z'
624     assert_response :success, "can't get changesets by time-since with hour"
625     assert_changesets [2]
626
627     get :query, :time => '2007-12-31T23:59Z,2008-01-01T00:01Z'
628     assert_response :success, "can't get changesets by time-range"
629     assert_changesets [4,5]
630   end
631
632   ##
633   # check updating tags on a changeset
634   def test_changeset_update
635     basic_authorization "test@openstreetmap.org", "test"
636
637     changeset = changesets(:normal_user_first_change)
638     new_changeset = changeset.to_xml
639     new_tag = XML::Node.new "tag"
640     new_tag['k'] = "tagtesting"
641     new_tag['v'] = "valuetesting"
642     new_changeset.find("//osm/changeset").first << new_tag
643
644     content new_changeset
645     put :update, :id => changeset.id
646     assert_response :success
647
648     assert_select "osm>changeset[id=#{changeset.id}]", 1
649     assert_select "osm>changeset>tag", 2
650     assert_select "osm>changeset>tag[k=tagtesting][v=valuetesting]", 1
651   end
652   
653   ##
654   # check that a user different from the one who opened the changeset
655   # can't modify it.
656   def test_changeset_update_invalid
657     basic_authorization "test@example.com", "test"
658
659     changeset = changesets(:normal_user_first_change)
660     new_changeset = changeset.to_xml
661     new_tag = XML::Node.new "tag"
662     new_tag['k'] = "testing"
663     new_tag['v'] = "testing"
664     new_changeset.find("//osm/changeset").first << new_tag
665
666     content new_changeset
667     put :update, :id => changeset.id
668     assert_response :conflict
669   end
670
671   #------------------------------------------------------------
672   # utility functions
673   #------------------------------------------------------------
674
675   ##
676   # boilerplate for checking that certain changesets exist in the
677   # output.
678   def assert_changesets(ids)
679     assert_select "osm>changeset", ids.size
680     ids.each do |id|
681       assert_select "osm>changeset[id=#{id}]", 1
682     end
683   end
684
685   ##
686   # call the include method and assert properties of the bbox
687   def check_after_include(changeset_id, lon, lat, bbox)
688     content "<osm><node lon='#{lon}' lat='#{lat}'/></osm>"
689     post :include, :id => changeset_id
690     assert_response :success, "Setting include of changeset failed: #{@response.body}"
691
692     # check exactly one changeset
693     assert_select "osm>changeset", 1
694     assert_select "osm>changeset[id=#{changeset_id}]", 1
695
696     # check the bbox
697     doc = XML::Parser.string(@response.body).parse
698     changeset = doc.find("//osm/changeset").first
699     assert_equal bbox[0], changeset['min_lon'].to_f, "min lon"
700     assert_equal bbox[1], changeset['min_lat'].to_f, "min lat"
701     assert_equal bbox[2], changeset['max_lon'].to_f, "max lon"
702     assert_equal bbox[3], changeset['max_lat'].to_f, "max lat"
703   end
704
705   ##
706   # update the changeset_id of a way element
707   def update_changeset(xml, changeset_id)
708     xml_attr_rewrite(xml, 'changeset', changeset_id)
709   end
710
711   ##
712   # update an attribute in a way element
713   def xml_attr_rewrite(xml, name, value)
714     xml.find("//osm/way").first[name] = value.to_s
715     return xml
716   end
717
718 end