]> git.openstreetmap.org Git - rails.git/blob - test/controllers/api/ways_controller_test.rb
Split update way with new 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     ##
889     # Try adding a duplicate of an existing tag to a way
890     def test_add_duplicate_tags
891       private_user = create(:user, :data_public => false)
892       private_way = create(:way, :changeset => create(:changeset, :user => private_user))
893       private_existing_tag = create(:way_tag, :way => private_way)
894       user = create(:user)
895       way = create(:way, :changeset => create(:changeset, :user => user))
896       existing_tag = create(:way_tag, :way => way)
897
898       ## Try with the non-public user
899       # setup auth
900       auth_header = bearer_authorization_header private_user
901
902       # add an identical tag to the way
903       tag_xml = XML::Node.new("tag")
904       tag_xml["k"] = private_existing_tag.k
905       tag_xml["v"] = private_existing_tag.v
906
907       # add the tag into the existing xml
908       way_xml = xml_for_way(private_way)
909       way_xml.find("//osm/way").first << tag_xml
910
911       # try and upload it
912       put api_way_path(private_way), :params => way_xml.to_s, :headers => auth_header
913       assert_response :forbidden,
914                       "adding a duplicate tag to a way for a non-public should fail with 'forbidden'"
915
916       ## Now try with the public user
917       # setup auth
918       auth_header = bearer_authorization_header user
919
920       # add an identical tag to the way
921       tag_xml = XML::Node.new("tag")
922       tag_xml["k"] = existing_tag.k
923       tag_xml["v"] = existing_tag.v
924
925       # add the tag into the existing xml
926       way_xml = xml_for_way(way)
927       way_xml.find("//osm/way").first << tag_xml
928
929       # try and upload it
930       put api_way_path(way), :params => way_xml.to_s, :headers => auth_header
931       assert_response :bad_request,
932                       "adding a duplicate tag to a way should fail with 'bad request'"
933       assert_equal "Element way/#{way.id} has duplicate tags with key #{existing_tag.k}", @response.body
934     end
935
936     ##
937     # Try adding a new duplicate tags to a way
938     def test_new_duplicate_tags
939       private_user = create(:user, :data_public => false)
940       private_way = create(:way, :changeset => create(:changeset, :user => private_user))
941       user = create(:user)
942       way = create(:way, :changeset => create(:changeset, :user => user))
943
944       ## First test with the non-public user so should be rejected
945       # setup auth
946       auth_header = bearer_authorization_header private_user
947
948       # create duplicate tag
949       tag_xml = XML::Node.new("tag")
950       tag_xml["k"] = "i_am_a_duplicate"
951       tag_xml["v"] = "foobar"
952
953       # add the tag into the existing xml
954       way_xml = xml_for_way(private_way)
955
956       # add two copies of the tag
957       way_xml.find("//osm/way").first << tag_xml.copy(true) << tag_xml
958
959       # try and upload it
960       put api_way_path(private_way), :params => way_xml.to_s, :headers => auth_header
961       assert_response :forbidden,
962                       "adding new duplicate tags to a way using a non-public user should fail with 'forbidden'"
963
964       ## Now test with the public user
965       # setup auth
966       auth_header = bearer_authorization_header user
967
968       # create duplicate tag
969       tag_xml = XML::Node.new("tag")
970       tag_xml["k"] = "i_am_a_duplicate"
971       tag_xml["v"] = "foobar"
972
973       # add the tag into the existing xml
974       way_xml = xml_for_way(way)
975
976       # add two copies of the tag
977       way_xml.find("//osm/way").first << tag_xml.copy(true) << tag_xml
978
979       # try and upload it
980       put api_way_path(way), :params => way_xml.to_s, :headers => auth_header
981       assert_response :bad_request,
982                       "adding new duplicate tags to a way should fail with 'bad request'"
983       assert_equal "Element way/#{way.id} has duplicate tags with key i_am_a_duplicate", @response.body
984     end
985
986     ##
987     # Try adding a new duplicate tags to a way.
988     # But be a bit subtle - use unicode decoding ambiguities to use different
989     # binary strings which have the same decoding.
990     def test_invalid_duplicate_tags
991       private_user = create(:user, :data_public => false)
992       private_changeset = create(:changeset, :user => private_user)
993       user = create(:user)
994       changeset = create(:changeset, :user => user)
995
996       ## First make sure that you can't with a non-public user
997       # setup auth
998       auth_header = bearer_authorization_header private_user
999
1000       # add the tag into the existing xml
1001       way_str = <<~OSM
1002         <osm>
1003           <way changeset='#{private_changeset.id}'>
1004             <tag k='addr:housenumber' v='1'/>
1005             <tag k='addr:housenumber' v='2'/>
1006           </way>
1007         </osm>
1008       OSM
1009
1010       # try and upload it
1011       post api_ways_path, :params => way_str, :headers => auth_header
1012       assert_response :forbidden,
1013                       "adding new duplicate tags to a way with a non-public user should fail with 'forbidden'"
1014
1015       ## Now do it with a public user
1016       # setup auth
1017       auth_header = bearer_authorization_header user
1018
1019       # add the tag into the existing xml
1020       way_str = <<~OSM
1021         <osm>
1022           <way changeset='#{changeset.id}'>
1023             <tag k='addr:housenumber' v='1'/>
1024             <tag k='addr:housenumber' v='2'/>
1025           </way>
1026         </osm>
1027       OSM
1028
1029       # try and upload it
1030       post api_ways_path, :params => way_str, :headers => auth_header
1031       assert_response :bad_request,
1032                       "adding new duplicate tags to a way should fail with 'bad request'"
1033       assert_equal "Element way/ has duplicate tags with key addr:housenumber", @response.body
1034     end
1035
1036     ##
1037     # test initial rate limit
1038     def test_initial_rate_limit
1039       # create a user
1040       user = create(:user)
1041
1042       # create some nodes
1043       node1 = create(:node)
1044       node2 = create(:node)
1045
1046       # create a changeset that puts us near the initial rate limit
1047       changeset = create(:changeset, :user => user,
1048                                      :created_at => Time.now.utc - 5.minutes,
1049                                      :num_changes => Settings.initial_changes_per_hour - 1)
1050
1051       # create authentication header
1052       auth_header = bearer_authorization_header user
1053
1054       # try creating a way
1055       xml = <<~OSM
1056         <osm>
1057           <way changeset='#{changeset.id}'>
1058             <nd ref='#{node1.id}'/>
1059             <nd ref='#{node2.id}'/>
1060           </way>
1061         </osm>
1062       OSM
1063       post api_ways_path, :params => xml, :headers => auth_header
1064       assert_response :success, "way create did not return success status"
1065
1066       # get the id of the way we created
1067       wayid = @response.body
1068
1069       # try updating the way, which should be rate limited
1070       xml = <<~OSM
1071         <osm>
1072           <way id='#{wayid}' version='1' changeset='#{changeset.id}'>
1073             <nd ref='#{node2.id}'/>
1074             <nd ref='#{node1.id}'/>
1075           </way>
1076         </osm>
1077       OSM
1078       put api_way_path(wayid), :params => xml, :headers => auth_header
1079       assert_response :too_many_requests, "way update did not hit rate limit"
1080
1081       # try deleting the way, which should be rate limited
1082       xml = "<osm><way id='#{wayid}' version='2' changeset='#{changeset.id}'/></osm>"
1083       delete api_way_path(wayid), :params => xml, :headers => auth_header
1084       assert_response :too_many_requests, "way delete did not hit rate limit"
1085
1086       # try creating a way, which should be rate limited
1087       xml = <<~OSM
1088         <osm>
1089           <way changeset='#{changeset.id}'>
1090             <nd ref='#{node1.id}'/>
1091             <nd ref='#{node2.id}'/>
1092           </way>
1093         </osm>
1094       OSM
1095       post api_ways_path, :params => xml, :headers => auth_header
1096       assert_response :too_many_requests, "way create did not hit rate limit"
1097     end
1098
1099     ##
1100     # test maximum rate limit
1101     def test_maximum_rate_limit
1102       # create a user
1103       user = create(:user)
1104
1105       # create some nodes
1106       node1 = create(:node)
1107       node2 = create(:node)
1108
1109       # create a changeset to establish our initial edit time
1110       changeset = create(:changeset, :user => user,
1111                                      :created_at => Time.now.utc - 28.days)
1112
1113       # create changeset to put us near the maximum rate limit
1114       total_changes = Settings.max_changes_per_hour - 1
1115       while total_changes.positive?
1116         changes = [total_changes, Changeset::MAX_ELEMENTS].min
1117         changeset = create(:changeset, :user => user,
1118                                        :created_at => Time.now.utc - 5.minutes,
1119                                        :num_changes => changes)
1120         total_changes -= changes
1121       end
1122
1123       # create authentication header
1124       auth_header = bearer_authorization_header user
1125
1126       # try creating a way
1127       xml = <<~OSM
1128         <osm>
1129           <way changeset='#{changeset.id}'>
1130             <nd ref='#{node1.id}'/>
1131             <nd ref='#{node2.id}'/>
1132           </way>
1133         </osm>
1134       OSM
1135       post api_ways_path, :params => xml, :headers => auth_header
1136       assert_response :success, "way create did not return success status"
1137
1138       # get the id of the way we created
1139       wayid = @response.body
1140
1141       # try updating the way, which should be rate limited
1142       xml = <<~OSM
1143         <osm>
1144           <way id='#{wayid}' version='1' changeset='#{changeset.id}'>
1145             <nd ref='#{node2.id}'/>
1146             <nd ref='#{node1.id}'/>
1147           </way>
1148         </osm>
1149       OSM
1150       put api_way_path(wayid), :params => xml, :headers => auth_header
1151       assert_response :too_many_requests, "way update did not hit rate limit"
1152
1153       # try deleting the way, which should be rate limited
1154       xml = "<osm><way id='#{wayid}' version='2' changeset='#{changeset.id}'/></osm>"
1155       delete api_way_path(wayid), :params => xml, :headers => auth_header
1156       assert_response :too_many_requests, "way delete did not hit rate limit"
1157
1158       # try creating a way, which should be rate limited
1159       xml = <<~OSM
1160         <osm>
1161           <way changeset='#{changeset.id}'>
1162             <nd ref='#{node1.id}'/>
1163             <nd ref='#{node2.id}'/>
1164           </way>
1165         </osm>
1166       OSM
1167       post api_ways_path, :params => xml, :headers => auth_header
1168       assert_response :too_many_requests, "way create did not hit rate limit"
1169     end
1170
1171     private
1172
1173     def affected_models
1174       [Way, WayNode, WayTag,
1175        OldWay, OldWayNode, OldWayTag]
1176     end
1177
1178     ##
1179     # update an attribute in the way element
1180     def xml_attr_rewrite(xml, name, value)
1181       xml.find("//osm/way").first[name] = value.to_s
1182       xml
1183     end
1184
1185     ##
1186     # replace a node in a way element
1187     def xml_replace_node(xml, old_node, new_node)
1188       xml.find("//osm/way/nd[@ref='#{old_node}']").first["ref"] = new_node.to_s
1189       xml
1190     end
1191   end
1192 end