Rail 3 has renamed fieldWithErrors to field_with_errors
[rails.git] / test / functional / way_controller_test.rb
1 require File.dirname(__FILE__) + '/../test_helper'
2 require 'way_controller'
3
4 class WayControllerTest < ActionController::TestCase
5   api_fixtures
6
7   # -------------------------------------
8   # Test reading ways.
9   # -------------------------------------
10
11   def test_read
12     # check that a visible way is returned properly
13     get :read, :id => current_ways(:visible_way).id
14     assert_response :success
15
16     # check that an invisible way is not returned
17     get :read, :id => current_ways(:invisible_way).id
18     assert_response :gone
19
20     # check chat a non-existent way is not returned
21     get :read, :id => 0
22     assert_response :not_found
23   end
24
25   ##
26   # check the "full" mode
27   def test_full
28     Way.find(:all).each do |way|
29       get :full, :id => way.id
30
31       # full call should say "gone" for non-visible ways...
32       unless way.visible
33         assert_response :gone
34         next
35       end
36
37       # otherwise it should say success
38       assert_response :success
39       
40       # Check the way is correctly returned
41       assert_select "osm way[id=#{way.id}][version=#{way.version}][visible=#{way.visible}]", 1
42       
43       # check that each node in the way appears once in the output as a 
44       # reference and as the node element.
45       way.nodes.each do |n|
46         count = (way.nodes - (way.nodes - [n])).length
47         assert_select "osm way nd[ref=#{n.id}]", count
48         assert_select "osm node[id=#{n.id}][version=#{n.version}][lat=#{n.lat}][lon=#{n.lon}]", 1
49       end
50     end
51   end
52
53   # -------------------------------------
54   # Test simple way creation.
55   # -------------------------------------
56
57   def test_create
58     ## First check that it fails when creating a way using a non-public user
59     nid1 = current_nodes(:used_node_1).id
60     nid2 = current_nodes(:used_node_2).id
61     basic_authorization users(:normal_user).email, "test"
62
63     # use the first user's open changeset
64     changeset_id = changesets(:normal_user_first_change).id
65     
66     # create a way with pre-existing nodes
67     content "<osm><way changeset='#{changeset_id}'>" +
68       "<nd ref='#{nid1}'/><nd ref='#{nid2}'/>" + 
69       "<tag k='test' v='yes' /></way></osm>"
70     put :create
71     # hope for success
72     assert_response :forbidden, 
73         "way upload did not return success status"
74     # read id of created way and search for it
75     wayid = @response.body
76
77     ## Now use a public user
78     nid1 = current_nodes(:used_node_1).id
79     nid2 = current_nodes(:used_node_2).id
80     basic_authorization users(:public_user).email, "test"
81
82     # use the first user's open changeset
83     changeset_id = changesets(:public_user_first_change).id
84     
85     # create a way with pre-existing nodes
86     content "<osm><way changeset='#{changeset_id}'>" +
87       "<nd ref='#{nid1}'/><nd ref='#{nid2}'/>" + 
88       "<tag k='test' v='yes' /></way></osm>"
89     put :create
90     # hope for success
91     assert_response :success, 
92         "way upload did not return success status"
93     # read id of created way and search for it
94     wayid = @response.body
95     checkway = Way.find(wayid)
96     assert_not_nil checkway, 
97         "uploaded way not found in data base after upload"
98     # compare values
99     assert_equal checkway.nds.length, 2, 
100         "saved way does not contain exactly one node"
101     assert_equal checkway.nds[0], nid1, 
102         "saved way does not contain the right node on pos 0"
103     assert_equal checkway.nds[1], nid2, 
104         "saved way does not contain the right node on pos 1"
105     assert_equal checkway.changeset_id, changeset_id,
106         "saved way does not belong to the correct changeset"
107     assert_equal users(:public_user).id, checkway.changeset.user_id, 
108         "saved way does not belong to user that created it"
109     assert_equal true, checkway.visible, 
110         "saved way is not visible"
111   end
112
113   # -------------------------------------
114   # Test creating some invalid ways.
115   # -------------------------------------
116
117   def test_create_invalid
118     ## First test with a private user to make sure that they are not authorized
119     basic_authorization users(:normal_user).email, "test"
120
121     # use the first user's open changeset
122     open_changeset_id = changesets(:normal_user_first_change).id
123     closed_changeset_id = changesets(:normal_user_closed_change).id
124     nid1 = current_nodes(:used_node_1).id
125
126     # create a way with non-existing node
127     content "<osm><way changeset='#{open_changeset_id}'>" + 
128       "<nd ref='0'/><tag k='test' v='yes' /></way></osm>"
129     put :create
130     # expect failure
131     assert_response :forbidden, 
132     "way upload with invalid node using a private user did not return 'forbidden'"
133
134     # create a way with no nodes
135     content "<osm><way changeset='#{open_changeset_id}'>" +
136       "<tag k='test' v='yes' /></way></osm>"
137     put :create
138     # expect failure
139     assert_response :forbidden, 
140     "way upload with no node using a private userdid not return 'forbidden'"
141
142     # create a way inside a closed changeset
143     content "<osm><way changeset='#{closed_changeset_id}'>" +
144       "<nd ref='#{nid1}'/></way></osm>"
145     put :create
146     # expect failure
147     assert_response :forbidden, 
148     "way upload to closed changeset with a private user did not return 'forbidden'"    
149
150     
151     ## Now test with a public user
152     basic_authorization users(:public_user).email, "test"
153
154     # use the first user's open changeset
155     open_changeset_id = changesets(:public_user_first_change).id
156     closed_changeset_id = changesets(:public_user_closed_change).id
157     nid1 = current_nodes(:used_node_1).id
158
159     # create a way with non-existing node
160     content "<osm><way changeset='#{open_changeset_id}'>" + 
161       "<nd ref='0'/><tag k='test' v='yes' /></way></osm>"
162     put :create
163     # expect failure
164     assert_response :precondition_failed, 
165         "way upload with invalid node did not return 'precondition failed'"
166     assert_equal "Precondition failed: Way  requires the nodes with id in (0), which either do not exist, or are not visible.", @response.body
167
168     # create a way with no nodes
169     content "<osm><way changeset='#{open_changeset_id}'>" +
170       "<tag k='test' v='yes' /></way></osm>"
171     put :create
172     # expect failure
173     assert_response :precondition_failed, 
174         "way upload with no node did not return 'precondition failed'"
175     assert_equal "Precondition failed: Cannot create way: data is invalid.", @response.body
176
177     # create a way inside a closed changeset
178     content "<osm><way changeset='#{closed_changeset_id}'>" +
179       "<nd ref='#{nid1}'/></way></osm>"
180     put :create
181     # expect failure
182     assert_response :conflict, 
183         "way upload to closed changeset did not return 'conflict'"    
184
185     # create a way with a tag which is too long
186     content "<osm><way changeset='#{open_changeset_id}'>" +
187       "<nd ref='#{nid1}'/>" +
188       "<tag k='foo' v='#{'x'*256}'/>" +
189       "</way></osm>"
190     put :create
191     # expect failure
192     assert_response :bad_request, 
193         "way upload to with too long tag did not return 'bad_request'"
194   end
195
196   # -------------------------------------
197   # Test deleting ways.
198   # -------------------------------------
199   
200   def test_delete
201     # first try to delete way without auth
202     delete :delete, :id => current_ways(:visible_way).id
203     assert_response :unauthorized
204
205     # now set auth using the private user
206     basic_authorization(users(:normal_user).email, "test");  
207
208     # this shouldn't work as with the 0.6 api we need pay load to delete
209     delete :delete, :id => current_ways(:visible_way).id
210     assert_response :forbidden
211     
212     # Now try without having a changeset
213     content "<osm><way id='#{current_ways(:visible_way).id}'/></osm>"
214     delete :delete, :id => current_ways(:visible_way).id
215     assert_response :forbidden
216     
217     # try to delete with an invalid (closed) changeset
218     content update_changeset(current_ways(:visible_way).to_xml,
219                              changesets(:normal_user_closed_change).id)
220     delete :delete, :id => current_ways(:visible_way).id
221     assert_response :forbidden
222
223     # try to delete with an invalid (non-existent) changeset
224     content update_changeset(current_ways(:visible_way).to_xml,0)
225     delete :delete, :id => current_ways(:visible_way).id
226     assert_response :forbidden
227
228     # Now try with a valid changeset
229     content current_ways(:visible_way).to_xml
230     delete :delete, :id => current_ways(:visible_way).id
231     assert_response :forbidden
232
233     # check the returned value - should be the new version number
234     # valid delete should return the new version number, which should
235     # be greater than the old version number
236     #assert @response.body.to_i > current_ways(:visible_way).version,
237     #   "delete request should return a new version number for way"
238
239     # this won't work since the way is already deleted
240     content current_ways(:invisible_way).to_xml
241     delete :delete, :id => current_ways(:invisible_way).id
242     assert_response :forbidden
243
244     # this shouldn't work as the way is used in a relation
245     content current_ways(:used_way).to_xml
246     delete :delete, :id => current_ways(:used_way).id
247     assert_response :forbidden, 
248     "shouldn't be able to delete a way used in a relation (#{@response.body}), when done by a private user"
249
250     # this won't work since the way never existed
251     delete :delete, :id => 0
252     assert_response :forbidden
253
254     
255     ### Now check with a public user
256     # now set auth
257     basic_authorization(users(:public_user).email, "test");  
258
259     # this shouldn't work as with the 0.6 api we need pay load to delete
260     delete :delete, :id => current_ways(:visible_way).id
261     assert_response :bad_request
262     
263     # Now try without having a changeset
264     content "<osm><way id='#{current_ways(:visible_way).id}'/></osm>"
265     delete :delete, :id => current_ways(:visible_way).id
266     assert_response :bad_request
267     
268     # try to delete with an invalid (closed) changeset
269     content update_changeset(current_ways(:visible_way).to_xml,
270                              changesets(:public_user_closed_change).id)
271     delete :delete, :id => current_ways(:visible_way).id
272     assert_response :conflict
273
274     # try to delete with an invalid (non-existent) changeset
275     content update_changeset(current_ways(:visible_way).to_xml,0)
276     delete :delete, :id => current_ways(:visible_way).id
277     assert_response :conflict
278
279     # Now try with a valid changeset
280     content current_ways(:visible_way).to_xml
281     delete :delete, :id => current_ways(:visible_way).id
282     assert_response :success
283
284     # check the returned value - should be the new version number
285     # valid delete should return the new version number, which should
286     # be greater than the old version number
287     assert @response.body.to_i > current_ways(:visible_way).version,
288        "delete request should return a new version number for way"
289
290     # this won't work since the way is already deleted
291     content current_ways(:invisible_way).to_xml
292     delete :delete, :id => current_ways(:invisible_way).id
293     assert_response :gone
294
295     # this shouldn't work as the way is used in a relation
296     content current_ways(:used_way).to_xml
297     delete :delete, :id => current_ways(:used_way).id
298     assert_response :precondition_failed, 
299        "shouldn't be able to delete a way used in a relation (#{@response.body})"
300     assert_equal "Precondition failed: Way 3 still used by relation 1.", @response.body
301
302     # this won't work since the way never existed
303     delete :delete, :id => 0
304     assert_response :not_found
305   end
306
307   # ------------------------------------------------------------
308   # test tags handling
309   # ------------------------------------------------------------
310
311   ##
312   # Try adding a duplicate of an existing tag to a way
313   def test_add_duplicate_tags
314     ## Try with the non-public user
315     # setup auth
316     basic_authorization(users(:normal_user).email, "test")
317
318     # add an identical tag to the way
319     tag_xml = XML::Node.new("tag")
320     tag_xml['k'] = current_way_tags(:t1).k
321     tag_xml['v'] = current_way_tags(:t1).v
322
323     # add the tag into the existing xml
324     way_xml = current_ways(:visible_way).to_xml
325     way_xml.find("//osm/way").first << tag_xml
326
327     # try and upload it
328     content way_xml
329     put :update, :id => current_ways(:visible_way).id
330     assert_response :forbidden, 
331     "adding a duplicate tag to a way for a non-public should fail with 'forbidden'"
332
333     ## Now try with the public user
334     # setup auth
335     basic_authorization(users(:public_user).email, "test")
336
337     # add an identical tag to the way
338     tag_xml = XML::Node.new("tag")
339     tag_xml['k'] = current_way_tags(:t1).k
340     tag_xml['v'] = current_way_tags(:t1).v
341
342     # add the tag into the existing xml
343     way_xml = current_ways(:visible_way).to_xml
344     way_xml.find("//osm/way").first << tag_xml
345
346     # try and upload it
347     content way_xml
348     put :update, :id => current_ways(:visible_way).id
349     assert_response :bad_request, 
350        "adding a duplicate tag to a way should fail with 'bad request'"
351     assert_equal "Element way/#{current_ways(:visible_way).id} has duplicate tags with key #{current_way_tags(:t1).k}", @response.body
352   end
353
354   ##
355   # Try adding a new duplicate tags to a way
356   def test_new_duplicate_tags
357     ## First test with the non-public user so should be rejected
358     # setup auth
359     basic_authorization(users(:normal_user).email, "test")
360
361     # create duplicate tag
362     tag_xml = XML::Node.new("tag")
363     tag_xml['k'] = "i_am_a_duplicate"
364     tag_xml['v'] = "foobar"
365
366     # add the tag into the existing xml
367     way_xml = current_ways(:visible_way).to_xml
368
369     # add two copies of the tag
370     way_xml.find("//osm/way").first << tag_xml.copy(true) << tag_xml
371
372     # try and upload it
373     content way_xml
374     put :update, :id => current_ways(:visible_way).id
375     assert_response :forbidden, 
376     "adding new duplicate tags to a way using a non-public user should fail with 'forbidden'"
377     
378     ## Now test with the public user
379     # setup auth
380     basic_authorization(users(:public_user).email, "test")
381
382     # create duplicate tag
383     tag_xml = XML::Node.new("tag")
384     tag_xml['k'] = "i_am_a_duplicate"
385     tag_xml['v'] = "foobar"
386
387     # add the tag into the existing xml
388     way_xml = current_ways(:visible_way).to_xml
389
390     # add two copies of the tag
391     way_xml.find("//osm/way").first << tag_xml.copy(true) << tag_xml
392
393     # try and upload it
394     content way_xml
395     put :update, :id => current_ways(:visible_way).id
396     assert_response :bad_request, 
397        "adding new duplicate tags to a way should fail with 'bad request'"
398     assert_equal "Element way/#{current_ways(:visible_way).id} has duplicate tags with key i_am_a_duplicate", @response.body
399     
400   end
401
402   ##
403   # Try adding a new duplicate tags to a way.
404   # But be a bit subtle - use unicode decoding ambiguities to use different
405   # binary strings which have the same decoding.
406   def test_invalid_duplicate_tags
407     ## First make sure that you can't with a non-public user
408     # setup auth
409     basic_authorization(users(:normal_user).email, "test")
410
411     # add the tag into the existing xml
412     way_str = "<osm><way changeset='1'>"
413     way_str << "<tag k='addr:housenumber' v='1'/>"
414     way_str << "<tag k='addr:housenumber' v='2'/>"
415     way_str << "</way></osm>";
416
417     # try and upload it
418     content way_str
419     put :create
420     assert_response :forbidden, 
421     "adding new duplicate tags to a way with a non-public user should fail with 'forbidden'"
422     
423     ## Now do it with a public user
424     # setup auth
425     basic_authorization(users(:public_user).email, "test")
426
427     # add the tag into the existing xml
428     way_str = "<osm><way changeset='1'>"
429     way_str << "<tag k='addr:housenumber' v='1'/>"
430     way_str << "<tag k='addr:housenumber' v='2'/>"
431     way_str << "</way></osm>";
432
433     # try and upload it
434     content way_str
435     put :create
436     assert_response :bad_request, 
437     "adding new duplicate tags to a way should fail with 'bad request'"
438     assert_equal "Element way/ has duplicate tags with key addr:housenumber", @response.body
439   end
440
441   ##
442   # test that a call to ways_for_node returns all ways that contain the node
443   # and none that don't.
444   def test_ways_for_node
445     # in current fixtures ways 1 and 3 all use node 3. ways 2 and 4 
446     # *used* to use it but doesn't.
447     get :ways_for_node, :id => current_nodes(:used_node_1).id
448     assert_response :success
449     ways_xml = XML::Parser.string(@response.body).parse
450     assert_not_nil ways_xml, "failed to parse ways_for_node response"
451
452     # check that the set of IDs match expectations
453     expected_way_ids = [ current_ways(:visible_way).id,
454                          current_ways(:used_way).id
455                        ]
456     found_way_ids = ways_xml.find("//osm/way").collect { |w| w["id"].to_i }
457     assert_equal expected_way_ids, found_way_ids,
458       "expected ways for node #{current_nodes(:used_node_1).id} did not match found"
459     
460     # check the full ways to ensure we're not missing anything
461     expected_way_ids.each do |id|
462       way_xml = ways_xml.find("//osm/way[@id=#{id}]").first
463       assert_ways_are_equal(Way.find(id),
464                             Way.from_xml_node(way_xml))
465     end
466   end
467
468   ##
469   # update the changeset_id of a node element
470   def update_changeset(xml, changeset_id)
471     xml_attr_rewrite(xml, 'changeset', changeset_id)
472   end
473
474   ##
475   # update an attribute in the node element
476   def xml_attr_rewrite(xml, name, value)
477     xml.find("//osm/way").first[name] = value.to_s
478     return xml
479   end
480 end