]> git.openstreetmap.org Git - rails.git/blob - test/controllers/api/ways_controller_test.rb
Split destroy way 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_when_unauthorized
377       with_unchanging(:way) do |way|
378         delete api_way_path(way)
379
380         assert_response :unauthorized
381       end
382     end
383
384     def test_destroy_without_payload_by_private_user
385       with_unchanging(:way) do |way|
386         with_unchanging_request([:data_public => false]) do |headers|
387           delete api_way_path(way), :headers => headers
388
389           assert_response :forbidden
390         end
391       end
392     end
393
394     def test_destroy_without_changeset_id_by_private_user
395       with_unchanging(:way) do |way|
396         with_unchanging_request([:data_public => false]) do |headers|
397           osm = "<osm><way id='#{way.id}'/></osm>"
398
399           delete api_way_path(way), :params => osm, :headers => headers
400
401           assert_response :forbidden
402         end
403       end
404     end
405
406     def test_destroy_in_closed_changeset_by_private_user
407       with_unchanging(:way) do |way|
408         with_unchanging_request([:data_public => false], [:closed]) do |headers, changeset|
409           osm_xml = xml_for_way way
410           osm_xml = update_changeset osm_xml, changeset.id
411
412           delete api_way_path(way), :params => osm_xml.to_s, :headers => headers
413
414           assert_response :forbidden
415         end
416       end
417     end
418
419     def test_destroy_in_missing_changeset_by_private_user
420       with_unchanging(:way) do |way|
421         with_unchanging_request([:data_public => false]) do |headers|
422           osm_xml = xml_for_way way
423           osm_xml = update_changeset osm_xml, 0
424
425           delete api_way_path(way), :params => osm_xml.to_s, :headers => headers
426
427           assert_response :forbidden
428         end
429       end
430     end
431
432     def test_destroy_by_private_user
433       with_unchanging(:way) do |way|
434         with_unchanging_request([:data_public => false]) do |headers, changeset|
435           osm_xml = xml_for_way way
436           osm_xml = update_changeset osm_xml, changeset.id
437
438           delete api_way_path(way), :params => osm_xml.to_s, :headers => headers
439
440           assert_response :forbidden
441         end
442       end
443     end
444
445     def test_destroy_deleted_way_by_private_user
446       with_unchanging(:way, :deleted) do |way|
447         with_unchanging_request([:data_public => false]) do |headers, changeset|
448           osm_xml = xml_for_way way
449           osm_xml = update_changeset osm_xml, changeset.id
450
451           delete api_way_path(way), :params => osm_xml.to_s, :headers => headers
452
453           assert_response :forbidden
454         end
455       end
456     end
457
458     def test_destroy_way_in_relation_by_private_user
459       with_unchanging(:way) do |way|
460         create(:relation_member, :member => way)
461
462         with_unchanging_request([:data_public => false]) do |headers, changeset|
463           osm_xml = xml_for_way way
464           osm_xml = update_changeset osm_xml, changeset.id
465
466           delete api_way_path(way), :params => osm_xml.to_s, :headers => headers
467
468           assert_response :forbidden, "shouldn't be able to delete a way used in a relation (#{@response.body}), when done by a private user"
469         end
470       end
471     end
472
473     def test_destroy_missing_way_by_private_user
474       with_unchanging_request([:data_public => false]) do |headers|
475         delete api_way_path(0), :headers => headers
476
477         assert_response :forbidden
478       end
479     end
480
481     def test_destroy_without_payload
482       with_unchanging(:way) do |way|
483         with_unchanging_request do |headers|
484           delete api_way_path(way), :headers => headers
485
486           assert_response :bad_request
487         end
488       end
489     end
490
491     def test_destroy_without_changeset_id
492       with_unchanging(:way) do |way|
493         with_unchanging_request do |headers|
494           osm = "<osm><way id='#{way.id}'/></osm>"
495
496           delete api_way_path(way), :params => osm, :headers => headers
497
498           assert_response :bad_request
499         end
500       end
501     end
502
503     def test_destroy_in_closed_changeset
504       with_unchanging(:way) do |way|
505         with_unchanging_request([], [:closed]) do |headers, changeset|
506           osm_xml = xml_for_way way
507           osm_xml = update_changeset osm_xml, changeset.id
508
509           delete api_way_path(way), :params => osm_xml.to_s, :headers => headers
510
511           assert_response :conflict
512         end
513       end
514     end
515
516     def test_destroy_in_missing_changeset
517       with_unchanging(:way) do |way|
518         with_unchanging_request do |headers|
519           osm_xml = xml_for_way way
520           osm_xml = update_changeset osm_xml, 0
521
522           delete api_way_path(way), :params => osm_xml.to_s, :headers => headers
523
524           assert_response :conflict
525         end
526       end
527     end
528
529     def test_destroy
530       way = create(:way)
531
532       with_request do |headers, changeset|
533         osm_xml = xml_for_way way
534         osm_xml = update_changeset osm_xml, changeset.id
535
536         delete api_way_path(way), :params => osm_xml.to_s, :headers => headers
537
538         assert_response :success
539
540         response_way_version = @response.body.to_i
541         assert_operator response_way_version, :>, way.version, "delete request should return a new version number for way"
542         way.reload
543         assert_not_predicate way, :visible?
544         assert_equal response_way_version, way.version
545       end
546     end
547
548     def test_destroy_deleted_way
549       with_unchanging(:way, :deleted) do |way|
550         with_unchanging_request do |headers, changeset|
551           osm_xml = xml_for_way way
552           osm_xml = update_changeset osm_xml, changeset.id
553
554           delete api_way_path(way), :params => osm_xml.to_s, :headers => headers
555
556           assert_response :gone
557         end
558       end
559     end
560
561     def test_destroy_way_in_relation
562       with_unchanging(:way) do |way|
563         relation_member = create(:relation_member, :member => way)
564
565         with_unchanging_request do |headers, changeset|
566           osm_xml = xml_for_way way
567           osm_xml = update_changeset osm_xml, changeset.id
568
569           delete api_way_path(way), :params => osm_xml.to_s, :headers => headers
570
571           assert_response :precondition_failed, "shouldn't be able to delete a way used in a relation (#{@response.body})"
572           assert_equal "Precondition failed: Way #{way.id} is still used by relations #{relation_member.relation.id}.", @response.body
573         end
574       end
575     end
576
577     def test_destroy_missing_way_with_payload
578       with_unchanging(:way) do |way|
579         with_unchanging_request do |headers, changeset|
580           osm_xml = xml_for_way way
581           osm_xml = update_changeset osm_xml, changeset.id
582
583           delete api_way_path(0), :params => osm_xml.to_s, :headers => headers
584
585           assert_response :not_found
586         end
587       end
588     end
589
590     ##
591     # tests whether the API works and prevents incorrect use while trying
592     # to update ways.
593     def test_update
594       private_user = create(:user, :data_public => false)
595       private_way = create(:way, :changeset => create(:changeset, :user => private_user))
596       user = create(:user)
597       way = create(:way, :changeset => create(:changeset, :user => user))
598       node = create(:node)
599       create(:way_node, :way => private_way, :node => node)
600       create(:way_node, :way => way, :node => node)
601
602       ## First test with no user credentials
603       # try and update a way without authorisation
604       xml = xml_for_way(way)
605       put api_way_path(way), :params => xml.to_s
606       assert_response :unauthorized
607
608       ## Second test with the private user
609
610       # setup auth
611       auth_header = bearer_authorization_header private_user
612
613       ## trying to break changesets
614
615       # try and update in someone else's changeset
616       xml = update_changeset(xml_for_way(private_way),
617                              create(:changeset).id)
618       put api_way_path(private_way), :params => xml.to_s, :headers => auth_header
619       assert_require_public_data "update with other user's changeset should be forbidden when date isn't public"
620
621       # try and update in a closed changeset
622       xml = update_changeset(xml_for_way(private_way),
623                              create(:changeset, :closed, :user => private_user).id)
624       put api_way_path(private_way), :params => xml.to_s, :headers => auth_header
625       assert_require_public_data "update with closed changeset should be forbidden, when data isn't public"
626
627       # try and update in a non-existant changeset
628       xml = update_changeset(xml_for_way(private_way), 0)
629       put api_way_path(private_way), :params => xml.to_s, :headers => auth_header
630       assert_require_public_data("update with changeset=0 should be forbidden, when data isn't public")
631
632       ## try and submit invalid updates
633       xml = xml_replace_node(xml_for_way(private_way), node.id, 9999)
634       put api_way_path(private_way), :params => xml.to_s, :headers => auth_header
635       assert_require_public_data "way with non-existent node should be forbidden, when data isn't public"
636
637       xml = xml_replace_node(xml_for_way(private_way), node.id, create(:node, :deleted).id)
638       put api_way_path(private_way), :params => xml.to_s, :headers => auth_header
639       assert_require_public_data "way with deleted node should be forbidden, when data isn't public"
640
641       ## finally, produce a good request which will still not work
642       xml = xml_for_way(private_way)
643       put api_way_path(private_way), :params => xml.to_s, :headers => auth_header
644       assert_require_public_data "should have failed with a forbidden when data isn't public"
645
646       ## Finally test with the public user
647
648       # setup auth
649       auth_header = bearer_authorization_header user
650
651       ## trying to break changesets
652
653       # try and update in someone else's changeset
654       xml = update_changeset(xml_for_way(way),
655                              create(:changeset).id)
656       put api_way_path(way), :params => xml.to_s, :headers => auth_header
657       assert_response :conflict, "update with other user's changeset should be rejected"
658
659       # try and update in a closed changeset
660       xml = update_changeset(xml_for_way(way),
661                              create(:changeset, :closed, :user => user).id)
662       put api_way_path(way), :params => xml.to_s, :headers => auth_header
663       assert_response :conflict, "update with closed changeset should be rejected"
664
665       # try and update in a non-existant changeset
666       xml = update_changeset(xml_for_way(way), 0)
667       put api_way_path(way), :params => xml.to_s, :headers => auth_header
668       assert_response :conflict, "update with changeset=0 should be rejected"
669
670       ## try and submit invalid updates
671       xml = xml_replace_node(xml_for_way(way), node.id, 9999)
672       put api_way_path(way), :params => xml.to_s, :headers => auth_header
673       assert_response :precondition_failed, "way with non-existent node should be rejected"
674
675       xml = xml_replace_node(xml_for_way(way), node.id, create(:node, :deleted).id)
676       put api_way_path(way), :params => xml.to_s, :headers => auth_header
677       assert_response :precondition_failed, "way with deleted node should be rejected"
678
679       ## next, attack the versioning
680       current_way_version = way.version
681
682       # try and submit a version behind
683       xml = xml_attr_rewrite(xml_for_way(way),
684                              "version", current_way_version - 1)
685       put api_way_path(way), :params => xml.to_s, :headers => auth_header
686       assert_response :conflict, "should have failed on old version number"
687
688       # try and submit a version ahead
689       xml = xml_attr_rewrite(xml_for_way(way),
690                              "version", current_way_version + 1)
691       put api_way_path(way), :params => xml.to_s, :headers => auth_header
692       assert_response :conflict, "should have failed on skipped version number"
693
694       # try and submit total crap in the version field
695       xml = xml_attr_rewrite(xml_for_way(way),
696                              "version", "p1r4t3s!")
697       put api_way_path(way), :params => xml.to_s, :headers => auth_header
698       assert_response :conflict,
699                       "should not be able to put 'p1r4at3s!' in the version field"
700
701       ## try an update with the wrong ID
702       xml = xml_for_way(create(:way))
703       put api_way_path(way), :params => xml.to_s, :headers => auth_header
704       assert_response :bad_request,
705                       "should not be able to update a way with a different ID from the XML"
706
707       ## try an update with a minimal valid XML doc which isn't a well-formed OSM doc.
708       xml = "<update/>"
709       put api_way_path(way), :params => xml.to_s, :headers => auth_header
710       assert_response :bad_request,
711                       "should not be able to update a way with non-OSM XML doc."
712
713       ## finally, produce a good request which should work
714       xml = xml_for_way(way)
715       put api_way_path(way), :params => xml.to_s, :headers => auth_header
716       assert_response :success, "a valid update request failed"
717     end
718
719     # ------------------------------------------------------------
720     # test tags handling
721     # ------------------------------------------------------------
722
723     ##
724     # Try adding a new tag to a way
725     def test_add_tags
726       private_user = create(:user, :data_public => false)
727       private_way = create(:way_with_nodes, :nodes_count => 2, :changeset => create(:changeset, :user => private_user))
728       user = create(:user)
729       way = create(:way_with_nodes, :nodes_count => 2, :changeset => create(:changeset, :user => user))
730
731       ## Try with the non-public user
732       # setup auth
733       auth_header = bearer_authorization_header private_user
734
735       # add an identical tag to the way
736       tag_xml = XML::Node.new("tag")
737       tag_xml["k"] = "new"
738       tag_xml["v"] = "yes"
739
740       # add the tag into the existing xml
741       way_xml = xml_for_way(private_way)
742       way_xml.find("//osm/way").first << tag_xml
743
744       # try and upload it
745       put api_way_path(private_way), :params => way_xml.to_s, :headers => auth_header
746       assert_response :forbidden,
747                       "adding a duplicate tag to a way for a non-public should fail with 'forbidden'"
748
749       ## Now try with the public user
750       # setup auth
751       auth_header = bearer_authorization_header user
752
753       # add an identical tag to the way
754       tag_xml = XML::Node.new("tag")
755       tag_xml["k"] = "new"
756       tag_xml["v"] = "yes"
757
758       # add the tag into the existing xml
759       way_xml = xml_for_way(way)
760       way_xml.find("//osm/way").first << tag_xml
761
762       # try and upload it
763       put api_way_path(way), :params => way_xml.to_s, :headers => auth_header
764       assert_response :success,
765                       "adding a new tag to a way should succeed"
766       assert_equal way.version + 1, @response.body.to_i
767     end
768
769     ##
770     # Try adding a duplicate of an existing tag to a way
771     def test_add_duplicate_tags
772       private_user = create(:user, :data_public => false)
773       private_way = create(:way, :changeset => create(:changeset, :user => private_user))
774       private_existing_tag = create(:way_tag, :way => private_way)
775       user = create(:user)
776       way = create(:way, :changeset => create(:changeset, :user => user))
777       existing_tag = create(:way_tag, :way => way)
778
779       ## Try with the non-public user
780       # setup auth
781       auth_header = bearer_authorization_header private_user
782
783       # add an identical tag to the way
784       tag_xml = XML::Node.new("tag")
785       tag_xml["k"] = private_existing_tag.k
786       tag_xml["v"] = private_existing_tag.v
787
788       # add the tag into the existing xml
789       way_xml = xml_for_way(private_way)
790       way_xml.find("//osm/way").first << tag_xml
791
792       # try and upload it
793       put api_way_path(private_way), :params => way_xml.to_s, :headers => auth_header
794       assert_response :forbidden,
795                       "adding a duplicate tag to a way for a non-public should fail with 'forbidden'"
796
797       ## Now try with the public user
798       # setup auth
799       auth_header = bearer_authorization_header user
800
801       # add an identical tag to the way
802       tag_xml = XML::Node.new("tag")
803       tag_xml["k"] = existing_tag.k
804       tag_xml["v"] = existing_tag.v
805
806       # add the tag into the existing xml
807       way_xml = xml_for_way(way)
808       way_xml.find("//osm/way").first << tag_xml
809
810       # try and upload it
811       put api_way_path(way), :params => way_xml.to_s, :headers => auth_header
812       assert_response :bad_request,
813                       "adding a duplicate tag to a way should fail with 'bad request'"
814       assert_equal "Element way/#{way.id} has duplicate tags with key #{existing_tag.k}", @response.body
815     end
816
817     ##
818     # Try adding a new duplicate tags to a way
819     def test_new_duplicate_tags
820       private_user = create(:user, :data_public => false)
821       private_way = create(:way, :changeset => create(:changeset, :user => private_user))
822       user = create(:user)
823       way = create(:way, :changeset => create(:changeset, :user => user))
824
825       ## First test with the non-public user so should be rejected
826       # setup auth
827       auth_header = bearer_authorization_header private_user
828
829       # create duplicate tag
830       tag_xml = XML::Node.new("tag")
831       tag_xml["k"] = "i_am_a_duplicate"
832       tag_xml["v"] = "foobar"
833
834       # add the tag into the existing xml
835       way_xml = xml_for_way(private_way)
836
837       # add two copies of the tag
838       way_xml.find("//osm/way").first << tag_xml.copy(true) << tag_xml
839
840       # try and upload it
841       put api_way_path(private_way), :params => way_xml.to_s, :headers => auth_header
842       assert_response :forbidden,
843                       "adding new duplicate tags to a way using a non-public user should fail with 'forbidden'"
844
845       ## Now test with the public user
846       # setup auth
847       auth_header = bearer_authorization_header user
848
849       # create duplicate tag
850       tag_xml = XML::Node.new("tag")
851       tag_xml["k"] = "i_am_a_duplicate"
852       tag_xml["v"] = "foobar"
853
854       # add the tag into the existing xml
855       way_xml = xml_for_way(way)
856
857       # add two copies of the tag
858       way_xml.find("//osm/way").first << tag_xml.copy(true) << tag_xml
859
860       # try and upload it
861       put api_way_path(way), :params => way_xml.to_s, :headers => auth_header
862       assert_response :bad_request,
863                       "adding new duplicate tags to a way should fail with 'bad request'"
864       assert_equal "Element way/#{way.id} has duplicate tags with key i_am_a_duplicate", @response.body
865     end
866
867     ##
868     # Try adding a new duplicate tags to a way.
869     # But be a bit subtle - use unicode decoding ambiguities to use different
870     # binary strings which have the same decoding.
871     def test_invalid_duplicate_tags
872       private_user = create(:user, :data_public => false)
873       private_changeset = create(:changeset, :user => private_user)
874       user = create(:user)
875       changeset = create(:changeset, :user => user)
876
877       ## First make sure that you can't with a non-public user
878       # setup auth
879       auth_header = bearer_authorization_header private_user
880
881       # add the tag into the existing xml
882       way_str = <<~OSM
883         <osm>
884           <way changeset='#{private_changeset.id}'>
885             <tag k='addr:housenumber' v='1'/>
886             <tag k='addr:housenumber' v='2'/>
887           </way>
888         </osm>
889       OSM
890
891       # try and upload it
892       post api_ways_path, :params => way_str, :headers => auth_header
893       assert_response :forbidden,
894                       "adding new duplicate tags to a way with a non-public user should fail with 'forbidden'"
895
896       ## Now do it with a public user
897       # setup auth
898       auth_header = bearer_authorization_header user
899
900       # add the tag into the existing xml
901       way_str = <<~OSM
902         <osm>
903           <way changeset='#{changeset.id}'>
904             <tag k='addr:housenumber' v='1'/>
905             <tag k='addr:housenumber' v='2'/>
906           </way>
907         </osm>
908       OSM
909
910       # try and upload it
911       post api_ways_path, :params => way_str, :headers => auth_header
912       assert_response :bad_request,
913                       "adding new duplicate tags to a way should fail with 'bad request'"
914       assert_equal "Element way/ has duplicate tags with key addr:housenumber", @response.body
915     end
916
917     ##
918     # test initial rate limit
919     def test_initial_rate_limit
920       # create a user
921       user = create(:user)
922
923       # create some nodes
924       node1 = create(:node)
925       node2 = create(:node)
926
927       # create a changeset that puts us near the initial rate limit
928       changeset = create(:changeset, :user => user,
929                                      :created_at => Time.now.utc - 5.minutes,
930                                      :num_changes => Settings.initial_changes_per_hour - 1)
931
932       # create authentication header
933       auth_header = bearer_authorization_header user
934
935       # try creating a way
936       xml = <<~OSM
937         <osm>
938           <way changeset='#{changeset.id}'>
939             <nd ref='#{node1.id}'/>
940             <nd ref='#{node2.id}'/>
941           </way>
942         </osm>
943       OSM
944       post api_ways_path, :params => xml, :headers => auth_header
945       assert_response :success, "way create did not return success status"
946
947       # get the id of the way we created
948       wayid = @response.body
949
950       # try updating the way, which should be rate limited
951       xml = <<~OSM
952         <osm>
953           <way id='#{wayid}' version='1' changeset='#{changeset.id}'>
954             <nd ref='#{node2.id}'/>
955             <nd ref='#{node1.id}'/>
956           </way>
957         </osm>
958       OSM
959       put api_way_path(wayid), :params => xml, :headers => auth_header
960       assert_response :too_many_requests, "way update did not hit rate limit"
961
962       # try deleting the way, which should be rate limited
963       xml = "<osm><way id='#{wayid}' version='2' changeset='#{changeset.id}'/></osm>"
964       delete api_way_path(wayid), :params => xml, :headers => auth_header
965       assert_response :too_many_requests, "way delete did not hit rate limit"
966
967       # try creating a way, which should be rate limited
968       xml = <<~OSM
969         <osm>
970           <way changeset='#{changeset.id}'>
971             <nd ref='#{node1.id}'/>
972             <nd ref='#{node2.id}'/>
973           </way>
974         </osm>
975       OSM
976       post api_ways_path, :params => xml, :headers => auth_header
977       assert_response :too_many_requests, "way create did not hit rate limit"
978     end
979
980     ##
981     # test maximum rate limit
982     def test_maximum_rate_limit
983       # create a user
984       user = create(:user)
985
986       # create some nodes
987       node1 = create(:node)
988       node2 = create(:node)
989
990       # create a changeset to establish our initial edit time
991       changeset = create(:changeset, :user => user,
992                                      :created_at => Time.now.utc - 28.days)
993
994       # create changeset to put us near the maximum rate limit
995       total_changes = Settings.max_changes_per_hour - 1
996       while total_changes.positive?
997         changes = [total_changes, Changeset::MAX_ELEMENTS].min
998         changeset = create(:changeset, :user => user,
999                                        :created_at => Time.now.utc - 5.minutes,
1000                                        :num_changes => changes)
1001         total_changes -= changes
1002       end
1003
1004       # create authentication header
1005       auth_header = bearer_authorization_header user
1006
1007       # try creating a way
1008       xml = <<~OSM
1009         <osm>
1010           <way changeset='#{changeset.id}'>
1011             <nd ref='#{node1.id}'/>
1012             <nd ref='#{node2.id}'/>
1013           </way>
1014         </osm>
1015       OSM
1016       post api_ways_path, :params => xml, :headers => auth_header
1017       assert_response :success, "way create did not return success status"
1018
1019       # get the id of the way we created
1020       wayid = @response.body
1021
1022       # try updating the way, which should be rate limited
1023       xml = <<~OSM
1024         <osm>
1025           <way id='#{wayid}' version='1' changeset='#{changeset.id}'>
1026             <nd ref='#{node2.id}'/>
1027             <nd ref='#{node1.id}'/>
1028           </way>
1029         </osm>
1030       OSM
1031       put api_way_path(wayid), :params => xml, :headers => auth_header
1032       assert_response :too_many_requests, "way update did not hit rate limit"
1033
1034       # try deleting the way, which should be rate limited
1035       xml = "<osm><way id='#{wayid}' version='2' changeset='#{changeset.id}'/></osm>"
1036       delete api_way_path(wayid), :params => xml, :headers => auth_header
1037       assert_response :too_many_requests, "way delete did not hit rate limit"
1038
1039       # try creating a way, which should be rate limited
1040       xml = <<~OSM
1041         <osm>
1042           <way changeset='#{changeset.id}'>
1043             <nd ref='#{node1.id}'/>
1044             <nd ref='#{node2.id}'/>
1045           </way>
1046         </osm>
1047       OSM
1048       post api_ways_path, :params => xml, :headers => auth_header
1049       assert_response :too_many_requests, "way create did not hit rate limit"
1050     end
1051
1052     private
1053
1054     def affected_models
1055       [Way, WayNode, WayTag,
1056        OldWay, OldWayNode, OldWayTag]
1057     end
1058
1059     ##
1060     # update an attribute in the way element
1061     def xml_attr_rewrite(xml, name, value)
1062       xml.find("//osm/way").first[name] = value.to_s
1063       xml
1064     end
1065
1066     ##
1067     # replace a node in a way element
1068     def xml_replace_node(xml, old_node, new_node)
1069       xml.find("//osm/way/nd[@ref='#{old_node}']").first["ref"] = new_node.to_s
1070       xml
1071     end
1072   end
1073 end