Adding an extra test to make sure that the correct response is returned when an inval...
[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=\"#{GENERATOR}\"]", 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   # try to upload with commands other than create, modify, or delete
375   def test_action_upload_invalid
376     basic_authorization "test@openstreetmap.org", "test"
377     
378     diff = <<EOF
379 <osmChange>
380   <ping>
381     <node id='1' lon='1' lat='1' changeset='1' />
382   </ping>
383 </osmChange>
384 EOF
385   content diff
386   post :upload, :id => 1
387   assert_response :bad_request, "Shouldn't be able to upload a diff with the action ping"
388   assert_equal @response.body, "Unknown action ping, choices are create, modify, delete."
389   end
390
391   ##
392   # when we make some simple changes we get the same changes back from the 
393   # diff download.
394   def test_diff_download_simple
395     basic_authorization(users(:normal_user).email, "test")
396
397     # create a temporary changeset
398     content "<osm><changeset>" +
399       "<tag k='created_by' v='osm test suite checking changesets'/>" + 
400       "</changeset></osm>"
401     put :create
402     assert_response :success
403     changeset_id = @response.body.to_i
404
405     # add a diff to it
406     diff = <<EOF
407 <osmChange>
408  <modify>
409   <node id='1' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
410   <node id='1' lon='1' lat='0' changeset='#{changeset_id}' version='2'/>
411   <node id='1' lon='1' lat='1' changeset='#{changeset_id}' version='3'/>
412   <node id='1' lon='1' lat='2' changeset='#{changeset_id}' version='4'/>
413   <node id='1' lon='2' lat='2' changeset='#{changeset_id}' version='5'/>
414   <node id='1' lon='3' lat='2' changeset='#{changeset_id}' version='6'/>
415   <node id='1' lon='3' lat='3' changeset='#{changeset_id}' version='7'/>
416   <node id='1' lon='9' lat='9' changeset='#{changeset_id}' version='8'/>
417  </modify>
418 </osmChange>
419 EOF
420
421     # upload it
422     content diff
423     post :upload, :id => changeset_id
424     assert_response :success, 
425       "can't upload multiple versions of an element in a diff: #{@response.body}"
426     
427     get :download, :id => changeset_id
428     assert_response :success
429
430     assert_select "osmChange", 1
431     assert_select "osmChange>modify", 8
432     assert_select "osmChange>modify>node", 8
433   end
434   
435   ##
436   # when we make some complex changes we get the same changes back from the 
437   # diff download.
438   def test_diff_download_complex
439     basic_authorization(users(:normal_user).email, "test")
440
441     # create a temporary changeset
442     content "<osm><changeset>" +
443       "<tag k='created_by' v='osm test suite checking changesets'/>" + 
444       "</changeset></osm>"
445     put :create
446     assert_response :success
447     changeset_id = @response.body.to_i
448
449     # add a diff to it
450     diff = <<EOF
451 <osmChange>
452  <delete>
453   <node id='1' lon='0' lat='0' changeset='#{changeset_id}' version='1'/>
454  </delete>
455  <create>
456   <node id='-1' lon='9' lat='9' changeset='#{changeset_id}' version='0'/>
457   <node id='-2' lon='8' lat='9' changeset='#{changeset_id}' version='0'/>
458   <node id='-3' lon='7' lat='9' changeset='#{changeset_id}' version='0'/>
459  </create>
460  <modify>
461   <node id='3' lon='20' lat='15' changeset='#{changeset_id}' version='1'/>
462   <way id='1' changeset='#{changeset_id}' version='1'>
463    <nd ref='3'/>
464    <nd ref='-1'/>
465    <nd ref='-2'/>
466    <nd ref='-3'/>
467   </way>
468  </modify>
469 </osmChange>
470 EOF
471
472     # upload it
473     content diff
474     post :upload, :id => changeset_id
475     assert_response :success, 
476       "can't upload multiple versions of an element in a diff: #{@response.body}"
477     
478     get :download, :id => changeset_id
479     assert_response :success
480
481     assert_select "osmChange", 1
482     assert_select "osmChange>create", 3
483     assert_select "osmChange>delete", 1
484     assert_select "osmChange>modify", 2
485     assert_select "osmChange>create>node", 3
486     assert_select "osmChange>delete>node", 1 
487     assert_select "osmChange>modify>node", 1
488     assert_select "osmChange>modify>way", 1
489   end
490   
491 end