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