Starting the gpx trace testing with additional fixtures
[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   def test_read
40     
41   end
42   
43   def test_close
44     
45   end
46
47   ##
48   # upload something simple, but valid and check that it can 
49   # be read back ok.
50   def test_upload_simple_valid
51     basic_authorization "test@openstreetmap.org", "test"
52
53     # simple diff to change a node, way and relation by removing 
54     # their tags
55     diff = <<EOF
56 <osmChange>
57  <modify>
58   <node id='1' lon='0' lat='0' changeset='1' version='1'/>
59   <way id='1' changeset='1' version='1'>
60    <nd ref='3'/>
61   </way>
62  </modify>
63  <modify>
64   <relation id='1' changeset='1' version='1'>
65    <member type='way' role='some' ref='3'/>
66    <member type='node' role='some' ref='5'/>
67    <member type='relation' role='some' ref='3'/>
68   </relation>
69  </modify>
70 </osmChange>
71 EOF
72
73     # upload it
74     content diff
75     post :upload, :id => 1
76     assert_response :success, 
77       "can't upload a simple valid diff to changeset: #{@response.body}"
78
79     # check that the changes made it into the database
80     assert_equal 0, Node.find(1).tags.size, "node 1 should now have no tags"
81     assert_equal 0, Way.find(1).tags.size, "way 1 should now have no tags"
82     assert_equal 0, Relation.find(1).tags.size, "relation 1 should now have no tags"
83   end
84     
85   ##
86   # upload something which creates new objects using placeholders
87   def test_upload_create_valid
88     basic_authorization "test@openstreetmap.org", "test"
89
90     # simple diff to create a node way and relation using placeholders
91     diff = <<EOF
92 <osmChange>
93  <create>
94   <node id='-1' lon='0' lat='0' changeset='1'>
95    <tag k='foo' v='bar'/>
96    <tag k='baz' v='bat'/>
97   </node>
98   <way id='-1' changeset='1'>
99    <nd ref='3'/>
100   </way>
101  </create>
102  <create>
103   <relation id='-1' changeset='1'>
104    <member type='way' role='some' ref='3'/>
105    <member type='node' role='some' ref='5'/>
106    <member type='relation' role='some' ref='3'/>
107   </relation>
108  </create>
109 </osmChange>
110 EOF
111
112     # upload it
113     content diff
114     post :upload, :id => 1
115     assert_response :success, 
116       "can't upload a simple valid creation to changeset: #{@response.body}"
117
118     # check the returned payload
119     assert_select "osm[version=#{API_VERSION}][generator=\"OpenStreetMap server\"]", 1
120     assert_select "osm>node", 1
121     assert_select "osm>way", 1
122     assert_select "osm>relation", 1
123
124     # inspect the response to find out what the new element IDs are
125     doc = XML::Parser.string(@response.body).parse
126     new_node_id = doc.find("//osm/node").first["new_id"].to_i
127     new_way_id = doc.find("//osm/way").first["new_id"].to_i
128     new_rel_id = doc.find("//osm/relation").first["new_id"].to_i
129
130     # check the old IDs are all present and negative one
131     assert_equal -1, doc.find("//osm/node").first["old_id"].to_i
132     assert_equal -1, doc.find("//osm/way").first["old_id"].to_i
133     assert_equal -1, doc.find("//osm/relation").first["old_id"].to_i
134
135     # check the versions are present and equal one
136     assert_equal 1, doc.find("//osm/node").first["new_version"].to_i
137     assert_equal 1, doc.find("//osm/way").first["new_version"].to_i
138     assert_equal 1, doc.find("//osm/relation").first["new_version"].to_i
139
140     # check that the changes made it into the database
141     assert_equal 2, Node.find(new_node_id).tags.size, "new node should have two tags"
142     assert_equal 0, Way.find(new_way_id).tags.size, "new way should have no tags"
143     assert_equal 0, Relation.find(new_rel_id).tags.size, "new relation should have no tags"
144   end
145     
146   ##
147   # test a complex delete where we delete elements which rely on eachother
148   # in the same transaction.
149   def test_upload_delete
150     basic_authorization "test@openstreetmap.org", "test"
151
152     diff = XML::Document.new
153     diff.root = XML::Node.new "osmChange"
154     delete = XML::Node.new "delete"
155     diff.root << delete
156     delete << current_relations(:visible_relation).to_xml_node
157     delete << current_relations(:used_relation).to_xml_node
158     delete << current_ways(:used_way).to_xml_node
159     delete << current_nodes(:node_used_by_relationship).to_xml_node
160
161     # upload it
162     content diff
163     post :upload, :id => 1
164     assert_response :success, 
165       "can't upload a deletion diff to changeset: #{@response.body}"
166
167     # check that everything was deleted
168     assert_equal false, Node.find(current_nodes(:node_used_by_relationship).id).visible
169     assert_equal false, Way.find(current_ways(:used_way).id).visible
170     assert_equal false, Relation.find(current_relations(:visible_relation).id).visible
171     assert_equal false, Relation.find(current_relations(:used_relation).id).visible
172   end
173
174   ##
175   # test that deleting stuff in a transaction doesn't bypass the checks
176   # to ensure that used elements are not deleted.
177   def test_upload_delete_invalid
178     basic_authorization "test@openstreetmap.org", "test"
179
180     diff = XML::Document.new
181     diff.root = XML::Node.new "osmChange"
182     delete = XML::Node.new "delete"
183     diff.root << delete
184     delete << current_relations(:visible_relation).to_xml_node
185     delete << current_ways(:used_way).to_xml_node
186     delete << current_nodes(:node_used_by_relationship).to_xml_node
187
188     # upload it
189     content diff
190     post :upload, :id => 1
191     assert_response :precondition_failed, 
192       "shouldn't be able to upload a invalid deletion diff: #{@response.body}"
193
194     # check that nothing was, in fact, deleted
195     assert_equal true, Node.find(current_nodes(:node_used_by_relationship).id).visible
196     assert_equal true, Way.find(current_ways(:used_way).id).visible
197     assert_equal true, Relation.find(current_relations(:visible_relation).id).visible
198   end
199
200   ##
201   # upload something which creates new objects and inserts them into
202   # existing containers using placeholders.
203   def test_upload_complex
204     basic_authorization "test@openstreetmap.org", "test"
205
206     # simple diff to create a node way and relation using placeholders
207     diff = <<EOF
208 <osmChange>
209  <create>
210   <node id='-1' lon='0' lat='0' changeset='1'>
211    <tag k='foo' v='bar'/>
212    <tag k='baz' v='bat'/>
213   </node>
214  </create>
215  <modify>
216   <way id='1' changeset='1' version='1'>
217    <nd ref='-1'/>
218    <nd ref='3'/>
219   </way>
220   <relation id='1' changeset='1' version='1'>
221    <member type='way' role='some' ref='3'/>
222    <member type='node' role='some' ref='-1'/>
223    <member type='relation' role='some' ref='3'/>
224   </relation>
225  </modify>
226 </osmChange>
227 EOF
228
229     # upload it
230     content diff
231     post :upload, :id => 1
232     assert_response :success, 
233       "can't upload a complex diff to changeset: #{@response.body}"
234
235     # check the returned payload
236     assert_select "osm[version=#{API_VERSION}][generator=\"OpenStreetMap server\"]", 1
237     assert_select "osm>node", 1
238     assert_select "osm>way", 1
239     assert_select "osm>relation", 1
240
241     # inspect the response to find out what the new element IDs are
242     doc = XML::Parser.string(@response.body).parse
243     new_node_id = doc.find("//osm/node").first["new_id"].to_i
244
245     # check that the changes made it into the database
246     assert_equal 2, Node.find(new_node_id).tags.size, "new node should have two tags"
247     assert_equal [new_node_id, 3], Way.find(1).nds, "way nodes should match"
248     Relation.find(1).members.each do |type,id,role|
249       if type == 'node'
250         assert_equal new_node_id, id, "relation should contain new node"
251       end
252     end
253   end
254     
255   ##
256   # create a diff which references several changesets, which should cause
257   # a rollback and none of the diff gets committed
258   def test_upload_invalid_changesets
259     basic_authorization "test@openstreetmap.org", "test"
260
261     # simple diff to create a node way and relation using placeholders
262     diff = <<EOF
263 <osmChange>
264  <modify>
265   <node id='1' lon='0' lat='0' changeset='1' version='1'/>
266   <way id='1' changeset='1' version='1'>
267    <nd ref='3'/>
268   </way>
269  </modify>
270  <modify>
271   <relation id='1' changeset='1' version='1'>
272    <member type='way' role='some' ref='3'/>
273    <member type='node' role='some' ref='5'/>
274    <member type='relation' role='some' ref='3'/>
275   </relation>
276  </modify>
277  <create>
278   <node id='-1' changeset='4'>
279    <tag k='foo' v='bar'/>
280    <tag k='baz' v='bat'/>
281   </node>
282  </create>
283 </osmChange>
284 EOF
285     # cache the objects before uploading them
286     node = current_nodes(:visible_node)
287     way = current_ways(:visible_way)
288     rel = current_relations(:visible_relation)
289
290     # upload it
291     content diff
292     post :upload, :id => 1
293     assert_response :conflict, 
294       "uploading a diff with multiple changsets should have failed"
295
296     # check that objects are unmodified
297     assert_nodes_are_equal(node, Node.find(1))
298     assert_ways_are_equal(way, Way.find(1))
299   end
300     
301   ##
302   # upload multiple versions of the same element in the same diff.
303   def test_upload_multiple_valid
304     basic_authorization "test@openstreetmap.org", "test"
305
306     # change the location of a node multiple times, each time referencing
307     # the last version. doesn't this depend on version numbers being
308     # sequential?
309     diff = <<EOF
310 <osmChange>
311  <modify>
312   <node id='1' lon='0' lat='0' changeset='1' version='1'/>
313   <node id='1' lon='1' lat='0' changeset='1' version='2'/>
314   <node id='1' lon='1' lat='1' changeset='1' version='3'/>
315   <node id='1' lon='1' lat='2' changeset='1' version='4'/>
316   <node id='1' lon='2' lat='2' changeset='1' version='5'/>
317   <node id='1' lon='3' lat='2' changeset='1' version='6'/>
318   <node id='1' lon='3' lat='3' changeset='1' version='7'/>
319   <node id='1' lon='9' lat='9' changeset='1' version='8'/>
320  </modify>
321 </osmChange>
322 EOF
323
324     # upload it
325     content diff
326     post :upload, :id => 1
327     assert_response :success, 
328       "can't upload multiple versions of an element in a diff: #{@response.body}"
329   end
330
331   ##
332   # upload multiple versions of the same element in the same diff, but
333   # keep the version numbers the same.
334   def test_upload_multiple_duplicate
335     basic_authorization "test@openstreetmap.org", "test"
336
337     diff = <<EOF
338 <osmChange>
339  <modify>
340   <node id='1' lon='0' lat='0' changeset='1' version='1'/>
341   <node id='1' lon='1' lat='1' changeset='1' version='1'/>
342  </modify>
343 </osmChange>
344 EOF
345
346     # upload it
347     content diff
348     post :upload, :id => 1
349     assert_response :conflict, 
350       "shouldn't be able to upload the same element twice in a diff: #{@response.body}"
351   end
352
353   ##
354   # try to upload some elements without specifying the version
355   def test_upload_missing_version
356     basic_authorization "test@openstreetmap.org", "test"
357
358     diff = <<EOF
359 <osmChange>
360  <modify>
361   <node id='1' lon='1' lat='1' changeset='1'/>
362  </modify>
363 </osmChange>
364 EOF
365
366     # upload it
367     content diff
368     post :upload, :id => 1
369     assert_response :bad_request, 
370       "shouldn't be able to upload an element without version: #{@response.body}"
371   end
372
373   ##
374   # when we make some simple changes we get the same changes back from the 
375   # diff download.
376   def test_diff_download_simple
377     basic_authorization(users(:normal_user).email, "test")
378
379     # create a temporary changeset
380     content "<osm><changeset>" +
381       "<tag k='created_by' v='osm test suite checking changesets'/>" + 
382       "</changeset></osm>"
383     put :create
384     assert_response :success
385     changeset_id = @response.body.to_i
386
387     # add a diff to it
388     diff = <<EOF
389 <osmChange>
390  <modify>
391   <node id='1' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
392   <node id='1' lon='1' lat='0' changeset='#{changeset_id}' version='2'/>
393   <node id='1' lon='1' lat='1' changeset='#{changeset_id}' version='3'/>
394   <node id='1' lon='1' lat='2' changeset='#{changeset_id}' version='4'/>
395   <node id='1' lon='2' lat='2' changeset='#{changeset_id}' version='5'/>
396   <node id='1' lon='3' lat='2' changeset='#{changeset_id}' version='6'/>
397   <node id='1' lon='3' lat='3' changeset='#{changeset_id}' version='7'/>
398   <node id='1' lon='9' lat='9' changeset='#{changeset_id}' version='8'/>
399  </modify>
400 </osmChange>
401 EOF
402
403     # upload it
404     content diff
405     post :upload, :id => changeset_id
406     assert_response :success, 
407       "can't upload multiple versions of an element in a diff: #{@response.body}"
408     
409     get :download, :id => changeset_id
410     assert_response :success
411
412     assert_select "osmChange", 1
413     assert_select "osmChange>modify", 8
414     assert_select "osmChange>modify>node", 8
415   end
416   
417   ##
418   # when we make some complex changes we get the same changes back from the 
419   # diff download.
420   def test_diff_download_complex
421     basic_authorization(users(:normal_user).email, "test")
422
423     # create a temporary changeset
424     content "<osm><changeset>" +
425       "<tag k='created_by' v='osm test suite checking changesets'/>" + 
426       "</changeset></osm>"
427     put :create
428     assert_response :success
429     changeset_id = @response.body.to_i
430
431     # add a diff to it
432     diff = <<EOF
433 <osmChange>
434  <delete>
435   <node id='1' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
436  </delete>
437  <create>
438   <node id='-1' lon='9' lat='9' changeset='#{changeset_id}' version='0'/>
439   <node id='-2' lon='8' lat='9' changeset='#{changeset_id}' version='0'/>
440   <node id='-3' lon='7' lat='9' changeset='#{changeset_id}' version='0'/>
441  </create>
442  <modify>
443   <node id='3' lon='20' lat='15' changeset='#{changeset_id}' version='1'/>
444   <way id='1' changeset='#{changeset_id}' version='1'>
445    <nd ref='3'/>
446    <nd ref='-1'/>
447    <nd ref='-2'/>
448    <nd ref='-3'/>
449   </way>
450  </modify>
451 </osmChange>
452 EOF
453
454     # upload it
455     content diff
456     post :upload, :id => changeset_id
457     assert_response :success, 
458       "can't upload multiple versions of an element in a diff: #{@response.body}"
459     
460     get :download, :id => changeset_id
461     assert_response :success
462
463     assert_select "osmChange", 1
464     assert_select "osmChange>create", 3
465     assert_select "osmChange>delete", 1
466     assert_select "osmChange>modify", 2
467     assert_select "osmChange>create>node", 3
468     assert_select "osmChange>delete>node", 1 
469     assert_select "osmChange>modify>node", 1
470     assert_select "osmChange>modify>way", 1
471   end
472   
473 end