]> git.openstreetmap.org Git - rails.git/blob - test/controllers/api/ways_controller_test.rb
Test if api relation show/full responses contain required elements
[rails.git] / test / controllers / api / ways_controller_test.rb
1 require "test_helper"
2
3 module Api
4   class WaysControllerTest < ActionDispatch::IntegrationTest
5     ##
6     # test all routes which lead to this controller
7     def test_routes
8       assert_routing(
9         { :path => "/api/0.6/ways", :method => :get },
10         { :controller => "api/ways", :action => "index" }
11       )
12       assert_routing(
13         { :path => "/api/0.6/ways.json", :method => :get },
14         { :controller => "api/ways", :action => "index", :format => "json" }
15       )
16       assert_routing(
17         { :path => "/api/0.6/ways", :method => :post },
18         { :controller => "api/ways", :action => "create" }
19       )
20       assert_routing(
21         { :path => "/api/0.6/way/1", :method => :get },
22         { :controller => "api/ways", :action => "show", :id => "1" }
23       )
24       assert_routing(
25         { :path => "/api/0.6/way/1.json", :method => :get },
26         { :controller => "api/ways", :action => "show", :id => "1", :format => "json" }
27       )
28       assert_routing(
29         { :path => "/api/0.6/way/1/full", :method => :get },
30         { :controller => "api/ways", :action => "full", :id => "1" }
31       )
32       assert_routing(
33         { :path => "/api/0.6/way/1/full.json", :method => :get },
34         { :controller => "api/ways", :action => "full", :id => "1", :format => "json" }
35       )
36       assert_routing(
37         { :path => "/api/0.6/way/1", :method => :put },
38         { :controller => "api/ways", :action => "update", :id => "1" }
39       )
40       assert_routing(
41         { :path => "/api/0.6/way/1", :method => :delete },
42         { :controller => "api/ways", :action => "destroy", :id => "1" }
43       )
44
45       assert_recognizes(
46         { :controller => "api/ways", :action => "create" },
47         { :path => "/api/0.6/way/create", :method => :put }
48       )
49     end
50
51     ##
52     # test fetching multiple ways
53     def test_index
54       way1 = create(:way)
55       way2 = create(:way, :deleted)
56       way3 = create(:way)
57       way4 = create(:way)
58
59       # check error when no parameter provided
60       get api_ways_path
61       assert_response :bad_request
62
63       # check error when no parameter value provided
64       get api_ways_path(:ways => "")
65       assert_response :bad_request
66
67       # test a working call
68       get api_ways_path(:ways => "#{way1.id},#{way2.id},#{way3.id},#{way4.id}")
69       assert_response :success
70       assert_select "osm" do
71         assert_select "way", :count => 4
72         assert_select "way[id='#{way1.id}'][visible='true']", :count => 1
73         assert_select "way[id='#{way2.id}'][visible='false']", :count => 1
74         assert_select "way[id='#{way3.id}'][visible='true']", :count => 1
75         assert_select "way[id='#{way4.id}'][visible='true']", :count => 1
76       end
77
78       # test a working call with json format
79       get api_ways_path(:ways => "#{way1.id},#{way2.id},#{way3.id},#{way4.id}", :format => "json")
80
81       js = ActiveSupport::JSON.decode(@response.body)
82       assert_not_nil js
83       assert_equal 4, js["elements"].count
84       assert_equal 4, (js["elements"].count { |a| a["type"] == "way" })
85       assert_equal 1, (js["elements"].count { |a| a["id"] == way1.id && a["visible"].nil? })
86       assert_equal 1, (js["elements"].count { |a| a["id"] == way2.id && a["visible"] == false })
87       assert_equal 1, (js["elements"].count { |a| a["id"] == way3.id && a["visible"].nil? })
88       assert_equal 1, (js["elements"].count { |a| a["id"] == way4.id && a["visible"].nil? })
89
90       # check error when a non-existent way is included
91       get api_ways_path(:ways => "#{way1.id},#{way2.id},#{way3.id},#{way4.id},0")
92       assert_response :not_found
93     end
94
95     # -------------------------------------
96     # Test showing ways.
97     # -------------------------------------
98
99     def test_show_not_found
100       get api_way_path(0)
101       assert_response :not_found
102     end
103
104     def test_show_deleted
105       get api_way_path(create(:way, :deleted))
106       assert_response :gone
107     end
108
109     def test_show
110       get api_way_path(create(:way))
111       assert_response :success
112     end
113
114     def test_show_json
115       way = create(:way_with_nodes, :nodes_count => 3)
116
117       get api_way_path(way, :format => "json")
118
119       assert_response :success
120
121       js = ActiveSupport::JSON.decode(@response.body)
122       assert_not_nil js
123       assert_equal 1, js["elements"].count
124       js_ways = js["elements"].filter { |e| e["type"] == "way" }
125       assert_equal 1, js_ways.count
126       assert_equal way.id, js_ways[0]["id"]
127       assert_equal 1, js_ways[0]["version"]
128     end
129
130     ##
131     # check the "full" mode
132     def test_full
133       way = create(:way_with_nodes, :nodes_count => 3)
134
135       get way_full_path(way)
136
137       assert_response :success
138
139       # Check the way is correctly returned
140       assert_select "osm way[id='#{way.id}'][version='1'][visible='true']", 1
141
142       # check that each node in the way appears once in the output as a
143       # reference and as the node element.
144       way.nodes.each do |n|
145         assert_select "osm way nd[ref='#{n.id}']", 1
146         assert_select "osm node[id='#{n.id}'][version='1'][lat='#{format('%<lat>.7f', :lat => n.lat)}'][lon='#{format('%<lon>.7f', :lon => n.lon)}']", 1
147       end
148     end
149
150     def test_full_deleted
151       way = create(:way, :deleted)
152
153       get way_full_path(way)
154
155       assert_response :gone
156     end
157
158     # -------------------------------------
159     # Test simple way creation.
160     # -------------------------------------
161
162     def test_create
163       node1 = create(:node)
164       node2 = create(:node)
165       private_user = create(:user, :data_public => false)
166       private_changeset = create(:changeset, :user => private_user)
167       user = create(:user)
168       changeset = create(:changeset, :user => user)
169
170       ## First check that it fails when creating a way using a non-public user
171       auth_header = bearer_authorization_header private_user
172
173       # use the first user's open changeset
174       changeset_id = private_changeset.id
175
176       # create a way with pre-existing nodes
177       xml = "<osm><way changeset='#{changeset_id}'>" \
178             "<nd ref='#{node1.id}'/><nd ref='#{node2.id}'/>" \
179             "<tag k='test' v='yes' /></way></osm>"
180       post api_ways_path, :params => xml, :headers => auth_header
181       # hope for failure
182       assert_response :forbidden,
183                       "way upload did not return forbidden status"
184
185       ## Now use a public user
186       auth_header = bearer_authorization_header user
187
188       # use the first user's open changeset
189       changeset_id = changeset.id
190
191       # create a way with pre-existing nodes
192       xml = "<osm><way changeset='#{changeset_id}'>" \
193             "<nd ref='#{node1.id}'/><nd ref='#{node2.id}'/>" \
194             "<tag k='test' v='yes' /></way></osm>"
195       post api_ways_path, :params => xml, :headers => auth_header
196       # hope for success
197       assert_response :success,
198                       "way upload did not return success status"
199       # read id of created way and search for it
200       wayid = @response.body
201       checkway = Way.find(wayid)
202       assert_not_nil checkway,
203                      "uploaded way not found in data base after upload"
204       # compare values
205       assert_equal(2, checkway.nds.length, "saved way does not contain exactly one node")
206       assert_equal checkway.nds[0], node1.id,
207                    "saved way does not contain the right node on pos 0"
208       assert_equal checkway.nds[1], node2.id,
209                    "saved way does not contain the right node on pos 1"
210       assert_equal checkway.changeset_id, changeset_id,
211                    "saved way does not belong to the correct changeset"
212       assert_equal user.id, checkway.changeset.user_id,
213                    "saved way does not belong to user that created it"
214       assert checkway.visible,
215              "saved way is not visible"
216     end
217
218     # -------------------------------------
219     # Test creating some invalid ways.
220     # -------------------------------------
221
222     def test_create_invalid
223       node = create(:node)
224       private_user = create(:user, :data_public => false)
225       private_open_changeset = create(:changeset, :user => private_user)
226       private_closed_changeset = create(:changeset, :closed, :user => private_user)
227       user = create(:user)
228       open_changeset = create(:changeset, :user => user)
229       closed_changeset = create(:changeset, :closed, :user => user)
230
231       ## First test with a private user to make sure that they are not authorized
232       auth_header = bearer_authorization_header private_user
233
234       # use the first user's open changeset
235       # create a way with non-existing node
236       xml = "<osm><way changeset='#{private_open_changeset.id}'>" \
237             "<nd ref='0'/><tag k='test' v='yes' /></way></osm>"
238       post api_ways_path, :params => xml, :headers => auth_header
239       # expect failure
240       assert_response :forbidden,
241                       "way upload with invalid node using a private user did not return 'forbidden'"
242
243       # create a way with no nodes
244       xml = "<osm><way changeset='#{private_open_changeset.id}'>" \
245             "<tag k='test' v='yes' /></way></osm>"
246       post api_ways_path, :params => xml, :headers => auth_header
247       # expect failure
248       assert_response :forbidden,
249                       "way upload with no node using a private userdid not return 'forbidden'"
250
251       # create a way inside a closed changeset
252       xml = "<osm><way changeset='#{private_closed_changeset.id}'>" \
253             "<nd ref='#{node.id}'/></way></osm>"
254       post api_ways_path, :params => xml, :headers => auth_header
255       # expect failure
256       assert_response :forbidden,
257                       "way upload to closed changeset with a private user did not return 'forbidden'"
258
259       ## Now test with a public user
260       auth_header = bearer_authorization_header user
261
262       # use the first user's open changeset
263       # create a way with non-existing node
264       xml = "<osm><way changeset='#{open_changeset.id}'>" \
265             "<nd ref='0'/><tag k='test' v='yes' /></way></osm>"
266       post api_ways_path, :params => xml, :headers => auth_header
267       # expect failure
268       assert_response :precondition_failed,
269                       "way upload with invalid node did not return 'precondition failed'"
270       assert_equal "Precondition failed: Way  requires the nodes with id in (0), which either do not exist, or are not visible.", @response.body
271
272       # create a way with no nodes
273       xml = "<osm><way changeset='#{open_changeset.id}'>" \
274             "<tag k='test' v='yes' /></way></osm>"
275       post api_ways_path, :params => xml, :headers => auth_header
276       # expect failure
277       assert_response :precondition_failed,
278                       "way upload with no node did not return 'precondition failed'"
279       assert_equal "Precondition failed: Cannot create way: data is invalid.", @response.body
280
281       # create a way inside a closed changeset
282       xml = "<osm><way changeset='#{closed_changeset.id}'>" \
283             "<nd ref='#{node.id}'/></way></osm>"
284       post api_ways_path, :params => xml, :headers => auth_header
285       # expect failure
286       assert_response :conflict,
287                       "way upload to closed changeset did not return 'conflict'"
288
289       # create a way with a tag which is too long
290       xml = "<osm><way changeset='#{open_changeset.id}'>" \
291             "<nd ref='#{node.id}'/>" \
292             "<tag k='foo' v='#{'x' * 256}'/>" \
293             "</way></osm>"
294       post api_ways_path, :params => xml, :headers => auth_header
295       # expect failure
296       assert_response :bad_request,
297                       "way upload to with too long tag did not return 'bad_request'"
298     end
299
300     # -------------------------------------
301     # Test deleting ways.
302     # -------------------------------------
303
304     def test_destroy
305       private_user = create(:user, :data_public => false)
306       private_open_changeset = create(:changeset, :user => private_user)
307       private_closed_changeset = create(:changeset, :closed, :user => private_user)
308       private_way = create(:way, :changeset => private_open_changeset)
309       private_deleted_way = create(:way, :deleted, :changeset => private_open_changeset)
310       private_used_way = create(:way, :changeset => private_open_changeset)
311       create(:relation_member, :member => private_used_way)
312       user = create(:user)
313       open_changeset = create(:changeset, :user => user)
314       closed_changeset = create(:changeset, :closed, :user => user)
315       way = create(:way, :changeset => open_changeset)
316       deleted_way = create(:way, :deleted, :changeset => open_changeset)
317       used_way = create(:way, :changeset => open_changeset)
318       relation_member = create(:relation_member, :member => used_way)
319       relation = relation_member.relation
320
321       # first try to delete way without auth
322       delete api_way_path(way)
323       assert_response :unauthorized
324
325       # now set auth using the private user
326       auth_header = bearer_authorization_header private_user
327
328       # this shouldn't work as with the 0.6 api we need pay load to delete
329       delete api_way_path(private_way), :headers => auth_header
330       assert_response :forbidden
331
332       # Now try without having a changeset
333       xml = "<osm><way id='#{private_way.id}'/></osm>"
334       delete api_way_path(private_way), :params => xml.to_s, :headers => auth_header
335       assert_response :forbidden
336
337       # try to delete with an invalid (closed) changeset
338       xml = update_changeset(xml_for_way(private_way), private_closed_changeset.id)
339       delete api_way_path(private_way), :params => xml.to_s, :headers => auth_header
340       assert_response :forbidden
341
342       # try to delete with an invalid (non-existent) changeset
343       xml = update_changeset(xml_for_way(private_way), 0)
344       delete api_way_path(private_way), :params => xml.to_s, :headers => auth_header
345       assert_response :forbidden
346
347       # Now try with a valid changeset
348       xml = xml_for_way(private_way)
349       delete api_way_path(private_way), :params => xml.to_s, :headers => auth_header
350       assert_response :forbidden
351
352       # check the returned value - should be the new version number
353       # valid delete should return the new version number, which should
354       # be greater than the old version number
355       # assert @response.body.to_i > current_ways(:visible_way).version,
356       #   "delete request should return a new version number for way"
357
358       # this won't work since the way is already deleted
359       xml = xml_for_way(private_deleted_way)
360       delete api_way_path(private_deleted_way), :params => xml.to_s, :headers => auth_header
361       assert_response :forbidden
362
363       # this shouldn't work as the way is used in a relation
364       xml = xml_for_way(private_used_way)
365       delete api_way_path(private_used_way), :params => xml.to_s, :headers => auth_header
366       assert_response :forbidden,
367                       "shouldn't be able to delete a way used in a relation (#{@response.body}), when done by a private user"
368
369       # this won't work since the way never existed
370       delete api_way_path(0), :headers => auth_header
371       assert_response :forbidden
372
373       ### Now check with a public user
374       # now set auth
375       auth_header = bearer_authorization_header user
376
377       # this shouldn't work as with the 0.6 api we need pay load to delete
378       delete api_way_path(way), :headers => auth_header
379       assert_response :bad_request
380
381       # Now try without having a changeset
382       xml = "<osm><way id='#{way.id}'/></osm>"
383       delete api_way_path(way), :params => xml.to_s, :headers => auth_header
384       assert_response :bad_request
385
386       # try to delete with an invalid (closed) changeset
387       xml = update_changeset(xml_for_way(way), closed_changeset.id)
388       delete api_way_path(way), :params => xml.to_s, :headers => auth_header
389       assert_response :conflict
390
391       # try to delete with an invalid (non-existent) changeset
392       xml = update_changeset(xml_for_way(way), 0)
393       delete api_way_path(way), :params => xml.to_s, :headers => auth_header
394       assert_response :conflict
395
396       # Now try with a valid changeset
397       xml = xml_for_way(way)
398       delete api_way_path(way), :params => xml.to_s, :headers => auth_header
399       assert_response :success
400
401       # check the returned value - should be the new version number
402       # valid delete should return the new version number, which should
403       # be greater than the old version number
404       assert_operator @response.body.to_i, :>, way.version, "delete request should return a new version number for way"
405
406       # this won't work since the way is already deleted
407       xml = xml_for_way(deleted_way)
408       delete api_way_path(deleted_way), :params => xml.to_s, :headers => auth_header
409       assert_response :gone
410
411       # this shouldn't work as the way is used in a relation
412       xml = xml_for_way(used_way)
413       delete api_way_path(used_way), :params => xml.to_s, :headers => auth_header
414       assert_response :precondition_failed,
415                       "shouldn't be able to delete a way used in a relation (#{@response.body})"
416       assert_equal "Precondition failed: Way #{used_way.id} is still used by relations #{relation.id}.", @response.body
417
418       # this won't work since the way never existed
419       delete api_way_path(0), :params => xml.to_s, :headers => auth_header
420       assert_response :not_found
421     end
422
423     ##
424     # tests whether the API works and prevents incorrect use while trying
425     # to update ways.
426     def test_update
427       private_user = create(:user, :data_public => false)
428       private_way = create(:way, :changeset => create(:changeset, :user => private_user))
429       user = create(:user)
430       way = create(:way, :changeset => create(:changeset, :user => user))
431       node = create(:node)
432       create(:way_node, :way => private_way, :node => node)
433       create(:way_node, :way => way, :node => node)
434
435       ## First test with no user credentials
436       # try and update a way without authorisation
437       xml = xml_for_way(way)
438       put api_way_path(way), :params => xml.to_s
439       assert_response :unauthorized
440
441       ## Second test with the private user
442
443       # setup auth
444       auth_header = bearer_authorization_header private_user
445
446       ## trying to break changesets
447
448       # try and update in someone else's changeset
449       xml = update_changeset(xml_for_way(private_way),
450                              create(:changeset).id)
451       put api_way_path(private_way), :params => xml.to_s, :headers => auth_header
452       assert_require_public_data "update with other user's changeset should be forbidden when date isn't public"
453
454       # try and update in a closed changeset
455       xml = update_changeset(xml_for_way(private_way),
456                              create(:changeset, :closed, :user => private_user).id)
457       put api_way_path(private_way), :params => xml.to_s, :headers => auth_header
458       assert_require_public_data "update with closed changeset should be forbidden, when data isn't public"
459
460       # try and update in a non-existant changeset
461       xml = update_changeset(xml_for_way(private_way), 0)
462       put api_way_path(private_way), :params => xml.to_s, :headers => auth_header
463       assert_require_public_data("update with changeset=0 should be forbidden, when data isn't public")
464
465       ## try and submit invalid updates
466       xml = xml_replace_node(xml_for_way(private_way), node.id, 9999)
467       put api_way_path(private_way), :params => xml.to_s, :headers => auth_header
468       assert_require_public_data "way with non-existent node should be forbidden, when data isn't public"
469
470       xml = xml_replace_node(xml_for_way(private_way), node.id, create(:node, :deleted).id)
471       put api_way_path(private_way), :params => xml.to_s, :headers => auth_header
472       assert_require_public_data "way with deleted node should be forbidden, when data isn't public"
473
474       ## finally, produce a good request which will still not work
475       xml = xml_for_way(private_way)
476       put api_way_path(private_way), :params => xml.to_s, :headers => auth_header
477       assert_require_public_data "should have failed with a forbidden when data isn't public"
478
479       ## Finally test with the public user
480
481       # setup auth
482       auth_header = bearer_authorization_header user
483
484       ## trying to break changesets
485
486       # try and update in someone else's changeset
487       xml = update_changeset(xml_for_way(way),
488                              create(:changeset).id)
489       put api_way_path(way), :params => xml.to_s, :headers => auth_header
490       assert_response :conflict, "update with other user's changeset should be rejected"
491
492       # try and update in a closed changeset
493       xml = update_changeset(xml_for_way(way),
494                              create(:changeset, :closed, :user => user).id)
495       put api_way_path(way), :params => xml.to_s, :headers => auth_header
496       assert_response :conflict, "update with closed changeset should be rejected"
497
498       # try and update in a non-existant changeset
499       xml = update_changeset(xml_for_way(way), 0)
500       put api_way_path(way), :params => xml.to_s, :headers => auth_header
501       assert_response :conflict, "update with changeset=0 should be rejected"
502
503       ## try and submit invalid updates
504       xml = xml_replace_node(xml_for_way(way), node.id, 9999)
505       put api_way_path(way), :params => xml.to_s, :headers => auth_header
506       assert_response :precondition_failed, "way with non-existent node should be rejected"
507
508       xml = xml_replace_node(xml_for_way(way), node.id, create(:node, :deleted).id)
509       put api_way_path(way), :params => xml.to_s, :headers => auth_header
510       assert_response :precondition_failed, "way with deleted node should be rejected"
511
512       ## next, attack the versioning
513       current_way_version = way.version
514
515       # try and submit a version behind
516       xml = xml_attr_rewrite(xml_for_way(way),
517                              "version", current_way_version - 1)
518       put api_way_path(way), :params => xml.to_s, :headers => auth_header
519       assert_response :conflict, "should have failed on old version number"
520
521       # try and submit a version ahead
522       xml = xml_attr_rewrite(xml_for_way(way),
523                              "version", current_way_version + 1)
524       put api_way_path(way), :params => xml.to_s, :headers => auth_header
525       assert_response :conflict, "should have failed on skipped version number"
526
527       # try and submit total crap in the version field
528       xml = xml_attr_rewrite(xml_for_way(way),
529                              "version", "p1r4t3s!")
530       put api_way_path(way), :params => xml.to_s, :headers => auth_header
531       assert_response :conflict,
532                       "should not be able to put 'p1r4at3s!' in the version field"
533
534       ## try an update with the wrong ID
535       xml = xml_for_way(create(:way))
536       put api_way_path(way), :params => xml.to_s, :headers => auth_header
537       assert_response :bad_request,
538                       "should not be able to update a way with a different ID from the XML"
539
540       ## try an update with a minimal valid XML doc which isn't a well-formed OSM doc.
541       xml = "<update/>"
542       put api_way_path(way), :params => xml.to_s, :headers => auth_header
543       assert_response :bad_request,
544                       "should not be able to update a way with non-OSM XML doc."
545
546       ## finally, produce a good request which should work
547       xml = xml_for_way(way)
548       put api_way_path(way), :params => xml.to_s, :headers => auth_header
549       assert_response :success, "a valid update request failed"
550     end
551
552     # ------------------------------------------------------------
553     # test tags handling
554     # ------------------------------------------------------------
555
556     ##
557     # Try adding a new tag to a way
558     def test_add_tags
559       private_user = create(:user, :data_public => false)
560       private_way = create(:way_with_nodes, :nodes_count => 2, :changeset => create(:changeset, :user => private_user))
561       user = create(:user)
562       way = create(:way_with_nodes, :nodes_count => 2, :changeset => create(:changeset, :user => user))
563
564       ## Try with the non-public user
565       # setup auth
566       auth_header = bearer_authorization_header private_user
567
568       # add an identical tag to the way
569       tag_xml = XML::Node.new("tag")
570       tag_xml["k"] = "new"
571       tag_xml["v"] = "yes"
572
573       # add the tag into the existing xml
574       way_xml = xml_for_way(private_way)
575       way_xml.find("//osm/way").first << tag_xml
576
577       # try and upload it
578       put api_way_path(private_way), :params => way_xml.to_s, :headers => auth_header
579       assert_response :forbidden,
580                       "adding a duplicate tag to a way for a non-public should fail with 'forbidden'"
581
582       ## Now try with the public user
583       # setup auth
584       auth_header = bearer_authorization_header user
585
586       # add an identical tag to the way
587       tag_xml = XML::Node.new("tag")
588       tag_xml["k"] = "new"
589       tag_xml["v"] = "yes"
590
591       # add the tag into the existing xml
592       way_xml = xml_for_way(way)
593       way_xml.find("//osm/way").first << tag_xml
594
595       # try and upload it
596       put api_way_path(way), :params => way_xml.to_s, :headers => auth_header
597       assert_response :success,
598                       "adding a new tag to a way should succeed"
599       assert_equal way.version + 1, @response.body.to_i
600     end
601
602     ##
603     # Try adding a duplicate of an existing tag to a way
604     def test_add_duplicate_tags
605       private_user = create(:user, :data_public => false)
606       private_way = create(:way, :changeset => create(:changeset, :user => private_user))
607       private_existing_tag = create(:way_tag, :way => private_way)
608       user = create(:user)
609       way = create(:way, :changeset => create(:changeset, :user => user))
610       existing_tag = create(:way_tag, :way => way)
611
612       ## Try with the non-public user
613       # setup auth
614       auth_header = bearer_authorization_header private_user
615
616       # add an identical tag to the way
617       tag_xml = XML::Node.new("tag")
618       tag_xml["k"] = private_existing_tag.k
619       tag_xml["v"] = private_existing_tag.v
620
621       # add the tag into the existing xml
622       way_xml = xml_for_way(private_way)
623       way_xml.find("//osm/way").first << tag_xml
624
625       # try and upload it
626       put api_way_path(private_way), :params => way_xml.to_s, :headers => auth_header
627       assert_response :forbidden,
628                       "adding a duplicate tag to a way for a non-public should fail with 'forbidden'"
629
630       ## Now try with the public user
631       # setup auth
632       auth_header = bearer_authorization_header user
633
634       # add an identical tag to the way
635       tag_xml = XML::Node.new("tag")
636       tag_xml["k"] = existing_tag.k
637       tag_xml["v"] = existing_tag.v
638
639       # add the tag into the existing xml
640       way_xml = xml_for_way(way)
641       way_xml.find("//osm/way").first << tag_xml
642
643       # try and upload it
644       put api_way_path(way), :params => way_xml.to_s, :headers => auth_header
645       assert_response :bad_request,
646                       "adding a duplicate tag to a way should fail with 'bad request'"
647       assert_equal "Element way/#{way.id} has duplicate tags with key #{existing_tag.k}", @response.body
648     end
649
650     ##
651     # Try adding a new duplicate tags to a way
652     def test_new_duplicate_tags
653       private_user = create(:user, :data_public => false)
654       private_way = create(:way, :changeset => create(:changeset, :user => private_user))
655       user = create(:user)
656       way = create(:way, :changeset => create(:changeset, :user => user))
657
658       ## First test with the non-public user so should be rejected
659       # setup auth
660       auth_header = bearer_authorization_header private_user
661
662       # create duplicate tag
663       tag_xml = XML::Node.new("tag")
664       tag_xml["k"] = "i_am_a_duplicate"
665       tag_xml["v"] = "foobar"
666
667       # add the tag into the existing xml
668       way_xml = xml_for_way(private_way)
669
670       # add two copies of the tag
671       way_xml.find("//osm/way").first << tag_xml.copy(true) << tag_xml
672
673       # try and upload it
674       put api_way_path(private_way), :params => way_xml.to_s, :headers => auth_header
675       assert_response :forbidden,
676                       "adding new duplicate tags to a way using a non-public user should fail with 'forbidden'"
677
678       ## Now test with the public user
679       # setup auth
680       auth_header = bearer_authorization_header user
681
682       # create duplicate tag
683       tag_xml = XML::Node.new("tag")
684       tag_xml["k"] = "i_am_a_duplicate"
685       tag_xml["v"] = "foobar"
686
687       # add the tag into the existing xml
688       way_xml = xml_for_way(way)
689
690       # add two copies of the tag
691       way_xml.find("//osm/way").first << tag_xml.copy(true) << tag_xml
692
693       # try and upload it
694       put api_way_path(way), :params => way_xml.to_s, :headers => auth_header
695       assert_response :bad_request,
696                       "adding new duplicate tags to a way should fail with 'bad request'"
697       assert_equal "Element way/#{way.id} has duplicate tags with key i_am_a_duplicate", @response.body
698     end
699
700     ##
701     # Try adding a new duplicate tags to a way.
702     # But be a bit subtle - use unicode decoding ambiguities to use different
703     # binary strings which have the same decoding.
704     def test_invalid_duplicate_tags
705       private_user = create(:user, :data_public => false)
706       private_changeset = create(:changeset, :user => private_user)
707       user = create(:user)
708       changeset = create(:changeset, :user => user)
709
710       ## First make sure that you can't with a non-public user
711       # setup auth
712       auth_header = bearer_authorization_header private_user
713
714       # add the tag into the existing xml
715       way_str = "<osm><way changeset='#{private_changeset.id}'>"
716       way_str << "<tag k='addr:housenumber' v='1'/>"
717       way_str << "<tag k='addr:housenumber' v='2'/>"
718       way_str << "</way></osm>"
719
720       # try and upload it
721       post api_ways_path, :params => way_str, :headers => auth_header
722       assert_response :forbidden,
723                       "adding new duplicate tags to a way with a non-public user should fail with 'forbidden'"
724
725       ## Now do it with a public user
726       # setup auth
727       auth_header = bearer_authorization_header user
728
729       # add the tag into the existing xml
730       way_str = "<osm><way changeset='#{changeset.id}'>"
731       way_str << "<tag k='addr:housenumber' v='1'/>"
732       way_str << "<tag k='addr:housenumber' v='2'/>"
733       way_str << "</way></osm>"
734
735       # try and upload it
736       post api_ways_path, :params => way_str, :headers => auth_header
737       assert_response :bad_request,
738                       "adding new duplicate tags to a way should fail with 'bad request'"
739       assert_equal "Element way/ has duplicate tags with key addr:housenumber", @response.body
740     end
741
742     ##
743     # test that a call to ways_for_node returns all ways that contain the node
744     # and none that don't.
745     def test_ways_for_node
746       node = create(:node)
747       way1 = create(:way)
748       way2 = create(:way)
749       create(:way_node, :way => way1, :node => node)
750       create(:way_node, :way => way2, :node => node)
751       # create an unrelated way
752       create(:way_with_nodes, :nodes_count => 2)
753       # create a way which used to use the node
754       way3_v1 = create(:old_way, :version => 1)
755       _way3_v2 = create(:old_way, :current_way => way3_v1.current_way, :version => 2)
756       create(:old_way_node, :old_way => way3_v1, :node => node)
757
758       get node_ways_path(node)
759       assert_response :success
760       ways_xml = XML::Parser.string(@response.body).parse
761       assert_not_nil ways_xml, "failed to parse ways_for_node response"
762
763       # check that the set of IDs match expectations
764       expected_way_ids = [way1.id,
765                           way2.id]
766       found_way_ids = ways_xml.find("//osm/way").collect { |w| w["id"].to_i }
767       assert_equal expected_way_ids.sort, found_way_ids.sort,
768                    "expected ways for node #{node.id} did not match found"
769
770       # check the full ways to ensure we're not missing anything
771       expected_way_ids.each do |id|
772         way_xml = ways_xml.find("//osm/way[@id='#{id}']").first
773         assert_ways_are_equal(Way.find(id),
774                               Way.from_xml_node(way_xml))
775       end
776     end
777
778     ##
779     # test initial rate limit
780     def test_initial_rate_limit
781       # create a user
782       user = create(:user)
783
784       # create some nodes
785       node1 = create(:node)
786       node2 = create(:node)
787
788       # create a changeset that puts us near the initial rate limit
789       changeset = create(:changeset, :user => user,
790                                      :created_at => Time.now.utc - 5.minutes,
791                                      :num_changes => Settings.initial_changes_per_hour - 1)
792
793       # create authentication header
794       auth_header = bearer_authorization_header user
795
796       # try creating a way
797       xml = "<osm><way changeset='#{changeset.id}'>" \
798             "<nd ref='#{node1.id}'/><nd ref='#{node2.id}'/>" \
799             "<tag k='test' v='yes' /></way></osm>"
800       post api_ways_path, :params => xml, :headers => auth_header
801       assert_response :success, "way create did not return success status"
802
803       # get the id of the way we created
804       wayid = @response.body
805
806       # try updating the way, which should be rate limited
807       xml = "<osm><way id='#{wayid}' version='1' changeset='#{changeset.id}'>" \
808             "<nd ref='#{node2.id}'/><nd ref='#{node1.id}'/>" \
809             "<tag k='test' v='yes' /></way></osm>"
810       put api_way_path(wayid), :params => xml, :headers => auth_header
811       assert_response :too_many_requests, "way update did not hit rate limit"
812
813       # try deleting the way, which should be rate limited
814       xml = "<osm><way id='#{wayid}' version='2' changeset='#{changeset.id}'/></osm>"
815       delete api_way_path(wayid), :params => xml, :headers => auth_header
816       assert_response :too_many_requests, "way delete did not hit rate limit"
817
818       # try creating a way, which should be rate limited
819       xml = "<osm><way changeset='#{changeset.id}'>" \
820             "<nd ref='#{node1.id}'/><nd ref='#{node2.id}'/>" \
821             "<tag k='test' v='yes' /></way></osm>"
822       post api_ways_path, :params => xml, :headers => auth_header
823       assert_response :too_many_requests, "way create did not hit rate limit"
824     end
825
826     ##
827     # test maximum rate limit
828     def test_maximum_rate_limit
829       # create a user
830       user = create(:user)
831
832       # create some nodes
833       node1 = create(:node)
834       node2 = create(:node)
835
836       # create a changeset to establish our initial edit time
837       changeset = create(:changeset, :user => user,
838                                      :created_at => Time.now.utc - 28.days)
839
840       # create changeset to put us near the maximum rate limit
841       total_changes = Settings.max_changes_per_hour - 1
842       while total_changes.positive?
843         changes = [total_changes, Changeset::MAX_ELEMENTS].min
844         changeset = create(:changeset, :user => user,
845                                        :created_at => Time.now.utc - 5.minutes,
846                                        :num_changes => changes)
847         total_changes -= changes
848       end
849
850       # create authentication header
851       auth_header = bearer_authorization_header user
852
853       # try creating a way
854       xml = "<osm><way changeset='#{changeset.id}'>" \
855             "<nd ref='#{node1.id}'/><nd ref='#{node2.id}'/>" \
856             "<tag k='test' v='yes' /></way></osm>"
857       post api_ways_path, :params => xml, :headers => auth_header
858       assert_response :success, "way create did not return success status"
859
860       # get the id of the way we created
861       wayid = @response.body
862
863       # try updating the way, which should be rate limited
864       xml = "<osm><way id='#{wayid}' version='1' changeset='#{changeset.id}'>" \
865             "<nd ref='#{node2.id}'/><nd ref='#{node1.id}'/>" \
866             "<tag k='test' v='yes' /></way></osm>"
867       put api_way_path(wayid), :params => xml, :headers => auth_header
868       assert_response :too_many_requests, "way update did not hit rate limit"
869
870       # try deleting the way, which should be rate limited
871       xml = "<osm><way id='#{wayid}' version='2' changeset='#{changeset.id}'/></osm>"
872       delete api_way_path(wayid), :params => xml, :headers => auth_header
873       assert_response :too_many_requests, "way delete did not hit rate limit"
874
875       # try creating a way, which should be rate limited
876       xml = "<osm><way changeset='#{changeset.id}'>" \
877             "<nd ref='#{node1.id}'/><nd ref='#{node2.id}'/>" \
878             "<tag k='test' v='yes' /></way></osm>"
879       post api_ways_path, :params => xml, :headers => auth_header
880       assert_response :too_many_requests, "way create did not hit rate limit"
881     end
882
883     private
884
885     ##
886     # update the changeset_id of a way element
887     def update_changeset(xml, changeset_id)
888       xml_attr_rewrite(xml, "changeset", changeset_id)
889     end
890
891     ##
892     # update an attribute in the way element
893     def xml_attr_rewrite(xml, name, value)
894       xml.find("//osm/way").first[name] = value.to_s
895       xml
896     end
897
898     ##
899     # replace a node in a way element
900     def xml_replace_node(xml, old_node, new_node)
901       xml.find("//osm/way/nd[@ref='#{old_node}']").first["ref"] = new_node.to_s
902       xml
903     end
904   end
905 end