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