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