]> git.openstreetmap.org Git - rails.git/blob - test/controllers/api/ways_controller_test.rb
Split update way with existing duplicate tags 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     # Test updating ways.
592     # -------------------------------------
593
594     def test_update_when_unauthorized
595       with_unchanging(:way_with_nodes) do |way|
596         osm_xml = xml_for_way way
597
598         put api_way_path(way), :params => osm_xml.to_s
599
600         assert_response :unauthorized
601       end
602     end
603
604     def test_update_in_changeset_of_other_user_by_private_user
605       with_unchanging(:way_with_nodes) do |way|
606         other_user = create(:user)
607
608         with_unchanging_request([:data_public => false], [:user => other_user]) do |headers, changeset|
609           osm_xml = xml_for_way way
610           osm_xml = update_changeset osm_xml, changeset.id
611
612           put api_way_path(way), :params => osm_xml.to_s, :headers => headers
613
614           assert_require_public_data "update with other user's changeset should be forbidden when date isn't public"
615         end
616       end
617     end
618
619     def test_update_in_closed_changeset_by_private_user
620       with_unchanging(:way_with_nodes) do |way|
621         with_unchanging_request([:data_public => false], [:closed]) do |headers, changeset|
622           osm_xml = xml_for_way way
623           osm_xml = update_changeset osm_xml, changeset.id
624
625           put api_way_path(way), :params => osm_xml.to_s, :headers => headers
626
627           assert_require_public_data "update with closed changeset should be forbidden, when data isn't public"
628         end
629       end
630     end
631
632     def test_update_in_missing_changeset_by_private_user
633       with_unchanging(:way_with_nodes) do |way|
634         with_unchanging_request([:data_public => false]) do |headers|
635           osm_xml = xml_for_way way
636           osm_xml = update_changeset osm_xml, 0
637
638           put api_way_path(way), :params => osm_xml.to_s, :headers => headers
639
640           assert_require_public_data "update with changeset=0 should be forbidden, when data isn't public"
641         end
642       end
643     end
644
645     def test_update_with_missing_node_by_private_user
646       with_unchanging(:way) do |way|
647         node = create(:node)
648         create(:way_node, :way => way, :node => node)
649
650         with_unchanging_request([:data_public => false]) do |headers, changeset|
651           osm_xml = xml_for_way way
652           osm_xml = xml_replace_node osm_xml, node.id, 9999
653           osm_xml = update_changeset osm_xml, changeset.id
654
655           put api_way_path(way), :params => osm_xml.to_s, :headers => headers
656
657           assert_require_public_data "way with non-existent node should be forbidden, when data isn't public"
658         end
659       end
660     end
661
662     def test_update_with_deleted_node_by_private_user
663       with_unchanging(:way) do |way|
664         node = create(:node)
665         deleted_node = create(:node, :deleted)
666         create(:way_node, :way => way, :node => node)
667
668         with_unchanging_request([:data_public => false]) do |headers, changeset|
669           osm_xml = xml_for_way way
670           osm_xml = xml_replace_node osm_xml, node.id, deleted_node.id
671           osm_xml = update_changeset osm_xml, changeset.id
672
673           put api_way_path(way), :params => osm_xml.to_s, :headers => headers
674
675           assert_require_public_data "way with deleted node should be forbidden, when data isn't public"
676         end
677       end
678     end
679
680     def test_update_by_private_user
681       with_unchanging(:way_with_nodes) do |way|
682         with_unchanging_request([:data_public => false]) do |headers, changeset|
683           osm_xml = xml_for_way way
684           osm_xml = update_changeset osm_xml, changeset.id
685
686           put api_way_path(way), :params => osm_xml.to_s, :headers => headers
687
688           assert_require_public_data "should have failed with a forbidden when data isn't public"
689         end
690       end
691     end
692
693     def test_update_in_changeset_of_other_user
694       with_unchanging(:way_with_nodes) do |way|
695         other_user = create(:user)
696
697         with_unchanging_request([], [:user => other_user]) do |headers, changeset|
698           osm_xml = xml_for_way way
699           osm_xml = update_changeset osm_xml, changeset.id
700
701           put api_way_path(way), :params => osm_xml.to_s, :headers => headers
702
703           assert_response :conflict, "update with other user's changeset should be rejected"
704         end
705       end
706     end
707
708     def test_update_in_closed_changeset
709       with_unchanging(:way_with_nodes) do |way|
710         with_unchanging_request([], [:closed]) do |headers, changeset|
711           osm_xml = xml_for_way way
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_response :conflict, "update with closed changeset should be rejected"
717         end
718       end
719     end
720
721     def test_update_in_missing_changeset
722       with_unchanging(:way_with_nodes) do |way|
723         with_unchanging_request do |headers|
724           osm_xml = xml_for_way way
725           osm_xml = update_changeset osm_xml, 0
726
727           put api_way_path(way), :params => osm_xml.to_s, :headers => headers
728
729           assert_response :conflict, "update with changeset=0 should be rejected"
730         end
731       end
732     end
733
734     def test_update_with_missing_node
735       with_unchanging(:way) do |way|
736         node = create(:node)
737         create(:way_node, :way => way, :node => node)
738
739         with_unchanging_request do |headers, changeset|
740           osm_xml = xml_for_way way
741           osm_xml = xml_replace_node osm_xml, node.id, 9999
742           osm_xml = update_changeset osm_xml, changeset.id
743
744           put api_way_path(way), :params => osm_xml.to_s, :headers => headers
745
746           assert_response :precondition_failed, "way with non-existent node should be rejected"
747         end
748       end
749     end
750
751     def test_update_with_deleted_node
752       with_unchanging(:way) do |way|
753         node = create(:node)
754         deleted_node = create(:node, :deleted)
755         create(:way_node, :way => way, :node => node)
756
757         with_unchanging_request do |headers, changeset|
758           osm_xml = xml_for_way way
759           osm_xml = xml_replace_node osm_xml, node.id, deleted_node.id
760           osm_xml = update_changeset osm_xml, changeset.id
761
762           put api_way_path(way), :params => osm_xml.to_s, :headers => headers
763
764           assert_response :precondition_failed, "way with deleted node should be rejected"
765         end
766       end
767     end
768
769     def test_update_with_version_behind
770       with_unchanging(:way_with_nodes, :version => 2) do |way|
771         with_unchanging_request do |headers, changeset|
772           osm_xml = xml_for_way way
773           osm_xml = xml_attr_rewrite osm_xml, "version", way.version - 1
774           osm_xml = update_changeset osm_xml, changeset.id
775
776           put api_way_path(way), :params => osm_xml.to_s, :headers => headers
777
778           assert_response :conflict, "should have failed on old version number"
779         end
780       end
781     end
782
783     def test_update_with_version_ahead
784       with_unchanging(:way_with_nodes, :version => 2) do |way|
785         with_unchanging_request do |headers, changeset|
786           osm_xml = xml_for_way way
787           osm_xml = xml_attr_rewrite osm_xml, "version", way.version + 1
788           osm_xml = update_changeset osm_xml, changeset.id
789
790           put api_way_path(way), :params => osm_xml.to_s, :headers => headers
791
792           assert_response :conflict, "should have failed on skipped version number"
793         end
794       end
795     end
796
797     def test_update_with_invalid_version
798       with_unchanging(:way_with_nodes) do |way|
799         with_unchanging_request do |headers, changeset|
800           osm_xml = xml_for_way way
801           osm_xml = xml_attr_rewrite osm_xml, "version", "p1r4t3s!"
802           osm_xml = update_changeset osm_xml, changeset.id
803
804           put api_way_path(way), :params => osm_xml.to_s, :headers => headers
805
806           assert_response :conflict, "should not be able to put 'p1r4at3s!' in the version field"
807         end
808       end
809     end
810
811     def test_update_other_way
812       with_unchanging(:way_with_nodes) do |way|
813         with_unchanging(:way_with_nodes) do |other_way|
814           with_unchanging_request do |headers, changeset|
815             osm_xml = xml_for_way other_way
816             osm_xml = update_changeset osm_xml, changeset.id
817
818             put api_way_path(way), :params => osm_xml.to_s, :headers => headers
819
820             assert_response :bad_request, "should not be able to update a way with a different ID from the XML"
821           end
822         end
823       end
824     end
825
826     def test_update_with_invalid_osm_structure
827       with_unchanging(:way_with_nodes) do |way|
828         with_unchanging_request do |headers|
829           osm = "<update/>"
830
831           put api_way_path(way), :params => osm, :headers => headers
832
833           assert_response :bad_request, "should not be able to update a way with non-OSM XML doc."
834         end
835       end
836     end
837
838     def test_update
839       way = create(:way_with_nodes)
840
841       with_request do |headers, changeset|
842         osm_xml = xml_for_way way
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 :success, "a valid update request failed"
848       end
849     end
850
851     def test_update_with_new_tags_by_private_user
852       with_unchanging(:way_with_nodes, :nodes_count => 2) do |way|
853         with_unchanging_request([:data_public => false]) do |headers, changeset|
854           tag_xml = XML::Node.new("tag")
855           tag_xml["k"] = "new"
856           tag_xml["v"] = "yes"
857
858           osm_xml = xml_for_way way
859           osm_xml.find("//osm/way").first << tag_xml
860           osm_xml = update_changeset osm_xml, changeset.id
861
862           put api_way_path(way), :params => osm_xml.to_s, :headers => headers
863
864           assert_response :forbidden, "adding a tag to a way for a non-public should fail with 'forbidden'"
865         end
866       end
867     end
868
869     def test_update_with_new_tags
870       way = create(:way_with_nodes, :nodes_count => 2)
871
872       with_request do |headers, changeset|
873         tag_xml = XML::Node.new("tag")
874         tag_xml["k"] = "new"
875         tag_xml["v"] = "yes"
876
877         osm_xml = xml_for_way way
878         osm_xml.find("//osm/way").first << tag_xml
879         osm_xml = update_changeset osm_xml, changeset.id
880
881         put api_way_path(way), :params => osm_xml.to_s, :headers => headers
882
883         assert_response :success, "adding a new tag to a way should succeed"
884         assert_equal way.version + 1, @response.body.to_i
885       end
886     end
887
888     def test_update_with_duplicated_existing_tags_by_private_user
889       with_unchanging(:way_with_nodes) do |way|
890         create(:way_tag, :way => way, :k => "key_to_duplicate", :v => "value_to_duplicate")
891
892         with_unchanging_request([:data_public => false]) do |headers, changeset|
893           tag_xml = XML::Node.new("tag")
894           tag_xml["k"] = "key_to_duplicate"
895           tag_xml["v"] = "value_to_duplicate"
896
897           osm_xml = xml_for_way way
898           osm_xml.find("//osm/way").first << tag_xml
899           osm_xml = update_changeset osm_xml, changeset.id
900
901           put api_way_path(way), :params => osm_xml.to_s, :headers => headers
902
903           assert_response :forbidden, "adding a duplicate tag to a way for a non-public should fail with 'forbidden'"
904         end
905       end
906     end
907
908     def test_update_with_duplicated_existing_tags
909       with_unchanging(:way_with_nodes) do |way|
910         create(:way_tag, :way => way, :k => "key_to_duplicate", :v => "value_to_duplicate")
911
912         with_unchanging_request do |headers, changeset|
913           tag_xml = XML::Node.new("tag")
914           tag_xml["k"] = "key_to_duplicate"
915           tag_xml["v"] = "value_to_duplicate"
916
917           osm_xml = xml_for_way way
918           osm_xml.find("//osm/way").first << tag_xml
919           osm_xml = update_changeset osm_xml, changeset.id
920
921           put api_way_path(way), :params => osm_xml.to_s, :headers => headers
922
923           assert_response :bad_request, "adding a duplicate tag to a way should fail with 'bad request'"
924           assert_equal "Element way/#{way.id} has duplicate tags with key key_to_duplicate", @response.body
925         end
926       end
927     end
928
929     ##
930     # Try adding a new duplicate tags to a way
931     def test_new_duplicate_tags
932       private_user = create(:user, :data_public => false)
933       private_way = create(:way, :changeset => create(:changeset, :user => private_user))
934       user = create(:user)
935       way = create(:way, :changeset => create(:changeset, :user => user))
936
937       ## First test with the non-public user so should be rejected
938       # setup auth
939       auth_header = bearer_authorization_header private_user
940
941       # create duplicate tag
942       tag_xml = XML::Node.new("tag")
943       tag_xml["k"] = "i_am_a_duplicate"
944       tag_xml["v"] = "foobar"
945
946       # add the tag into the existing xml
947       way_xml = xml_for_way(private_way)
948
949       # add two copies of the tag
950       way_xml.find("//osm/way").first << tag_xml.copy(true) << tag_xml
951
952       # try and upload it
953       put api_way_path(private_way), :params => way_xml.to_s, :headers => auth_header
954       assert_response :forbidden,
955                       "adding new duplicate tags to a way using a non-public user should fail with 'forbidden'"
956
957       ## Now test with the public user
958       # setup auth
959       auth_header = bearer_authorization_header user
960
961       # create duplicate tag
962       tag_xml = XML::Node.new("tag")
963       tag_xml["k"] = "i_am_a_duplicate"
964       tag_xml["v"] = "foobar"
965
966       # add the tag into the existing xml
967       way_xml = xml_for_way(way)
968
969       # add two copies of the tag
970       way_xml.find("//osm/way").first << tag_xml.copy(true) << tag_xml
971
972       # try and upload it
973       put api_way_path(way), :params => way_xml.to_s, :headers => auth_header
974       assert_response :bad_request,
975                       "adding new duplicate tags to a way should fail with 'bad request'"
976       assert_equal "Element way/#{way.id} has duplicate tags with key i_am_a_duplicate", @response.body
977     end
978
979     ##
980     # Try adding a new duplicate tags to a way.
981     # But be a bit subtle - use unicode decoding ambiguities to use different
982     # binary strings which have the same decoding.
983     def test_invalid_duplicate_tags
984       private_user = create(:user, :data_public => false)
985       private_changeset = create(:changeset, :user => private_user)
986       user = create(:user)
987       changeset = create(:changeset, :user => user)
988
989       ## First make sure that you can't with a non-public user
990       # setup auth
991       auth_header = bearer_authorization_header private_user
992
993       # add the tag into the existing xml
994       way_str = <<~OSM
995         <osm>
996           <way changeset='#{private_changeset.id}'>
997             <tag k='addr:housenumber' v='1'/>
998             <tag k='addr:housenumber' v='2'/>
999           </way>
1000         </osm>
1001       OSM
1002
1003       # try and upload it
1004       post api_ways_path, :params => way_str, :headers => auth_header
1005       assert_response :forbidden,
1006                       "adding new duplicate tags to a way with a non-public user should fail with 'forbidden'"
1007
1008       ## Now do it with a public user
1009       # setup auth
1010       auth_header = bearer_authorization_header user
1011
1012       # add the tag into the existing xml
1013       way_str = <<~OSM
1014         <osm>
1015           <way changeset='#{changeset.id}'>
1016             <tag k='addr:housenumber' v='1'/>
1017             <tag k='addr:housenumber' v='2'/>
1018           </way>
1019         </osm>
1020       OSM
1021
1022       # try and upload it
1023       post api_ways_path, :params => way_str, :headers => auth_header
1024       assert_response :bad_request,
1025                       "adding new duplicate tags to a way should fail with 'bad request'"
1026       assert_equal "Element way/ has duplicate tags with key addr:housenumber", @response.body
1027     end
1028
1029     ##
1030     # test initial rate limit
1031     def test_initial_rate_limit
1032       # create a user
1033       user = create(:user)
1034
1035       # create some nodes
1036       node1 = create(:node)
1037       node2 = create(:node)
1038
1039       # create a changeset that puts us near the initial rate limit
1040       changeset = create(:changeset, :user => user,
1041                                      :created_at => Time.now.utc - 5.minutes,
1042                                      :num_changes => Settings.initial_changes_per_hour - 1)
1043
1044       # create authentication header
1045       auth_header = bearer_authorization_header user
1046
1047       # try creating a way
1048       xml = <<~OSM
1049         <osm>
1050           <way changeset='#{changeset.id}'>
1051             <nd ref='#{node1.id}'/>
1052             <nd ref='#{node2.id}'/>
1053           </way>
1054         </osm>
1055       OSM
1056       post api_ways_path, :params => xml, :headers => auth_header
1057       assert_response :success, "way create did not return success status"
1058
1059       # get the id of the way we created
1060       wayid = @response.body
1061
1062       # try updating the way, which should be rate limited
1063       xml = <<~OSM
1064         <osm>
1065           <way id='#{wayid}' version='1' changeset='#{changeset.id}'>
1066             <nd ref='#{node2.id}'/>
1067             <nd ref='#{node1.id}'/>
1068           </way>
1069         </osm>
1070       OSM
1071       put api_way_path(wayid), :params => xml, :headers => auth_header
1072       assert_response :too_many_requests, "way update did not hit rate limit"
1073
1074       # try deleting the way, which should be rate limited
1075       xml = "<osm><way id='#{wayid}' version='2' changeset='#{changeset.id}'/></osm>"
1076       delete api_way_path(wayid), :params => xml, :headers => auth_header
1077       assert_response :too_many_requests, "way delete did not hit rate limit"
1078
1079       # try creating a way, which should be rate limited
1080       xml = <<~OSM
1081         <osm>
1082           <way changeset='#{changeset.id}'>
1083             <nd ref='#{node1.id}'/>
1084             <nd ref='#{node2.id}'/>
1085           </way>
1086         </osm>
1087       OSM
1088       post api_ways_path, :params => xml, :headers => auth_header
1089       assert_response :too_many_requests, "way create did not hit rate limit"
1090     end
1091
1092     ##
1093     # test maximum rate limit
1094     def test_maximum_rate_limit
1095       # create a user
1096       user = create(:user)
1097
1098       # create some nodes
1099       node1 = create(:node)
1100       node2 = create(:node)
1101
1102       # create a changeset to establish our initial edit time
1103       changeset = create(:changeset, :user => user,
1104                                      :created_at => Time.now.utc - 28.days)
1105
1106       # create changeset to put us near the maximum rate limit
1107       total_changes = Settings.max_changes_per_hour - 1
1108       while total_changes.positive?
1109         changes = [total_changes, Changeset::MAX_ELEMENTS].min
1110         changeset = create(:changeset, :user => user,
1111                                        :created_at => Time.now.utc - 5.minutes,
1112                                        :num_changes => changes)
1113         total_changes -= changes
1114       end
1115
1116       # create authentication header
1117       auth_header = bearer_authorization_header user
1118
1119       # try creating a way
1120       xml = <<~OSM
1121         <osm>
1122           <way changeset='#{changeset.id}'>
1123             <nd ref='#{node1.id}'/>
1124             <nd ref='#{node2.id}'/>
1125           </way>
1126         </osm>
1127       OSM
1128       post api_ways_path, :params => xml, :headers => auth_header
1129       assert_response :success, "way create did not return success status"
1130
1131       # get the id of the way we created
1132       wayid = @response.body
1133
1134       # try updating the way, which should be rate limited
1135       xml = <<~OSM
1136         <osm>
1137           <way id='#{wayid}' version='1' changeset='#{changeset.id}'>
1138             <nd ref='#{node2.id}'/>
1139             <nd ref='#{node1.id}'/>
1140           </way>
1141         </osm>
1142       OSM
1143       put api_way_path(wayid), :params => xml, :headers => auth_header
1144       assert_response :too_many_requests, "way update did not hit rate limit"
1145
1146       # try deleting the way, which should be rate limited
1147       xml = "<osm><way id='#{wayid}' version='2' changeset='#{changeset.id}'/></osm>"
1148       delete api_way_path(wayid), :params => xml, :headers => auth_header
1149       assert_response :too_many_requests, "way delete did not hit rate limit"
1150
1151       # try creating a way, which should be rate limited
1152       xml = <<~OSM
1153         <osm>
1154           <way changeset='#{changeset.id}'>
1155             <nd ref='#{node1.id}'/>
1156             <nd ref='#{node2.id}'/>
1157           </way>
1158         </osm>
1159       OSM
1160       post api_ways_path, :params => xml, :headers => auth_header
1161       assert_response :too_many_requests, "way create did not hit rate limit"
1162     end
1163
1164     private
1165
1166     def affected_models
1167       [Way, WayNode, WayTag,
1168        OldWay, OldWayNode, OldWayTag]
1169     end
1170
1171     ##
1172     # update an attribute in the way element
1173     def xml_attr_rewrite(xml, name, value)
1174       xml.find("//osm/way").first[name] = value.to_s
1175       xml
1176     end
1177
1178     ##
1179     # replace a node in a way element
1180     def xml_replace_node(xml, old_node, new_node)
1181       xml.find("//osm/way/nd[@ref='#{old_node}']").first["ref"] = new_node.to_s
1182       xml
1183     end
1184   end
1185 end