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