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