]> git.openstreetmap.org Git - rails.git/blob - test/controllers/api/ways_controller_test.rb
Split create way tests
[rails.git] / test / controllers / api / ways_controller_test.rb
1 require "test_helper"
2 require_relative "elements_test_helper"
3
4 module Api
5   class WaysControllerTest < ActionDispatch::IntegrationTest
6     include ElementsTestHelper
7
8     ##
9     # test all routes which lead to this controller
10     def test_routes
11       assert_routing(
12         { :path => "/api/0.6/ways", :method => :get },
13         { :controller => "api/ways", :action => "index" }
14       )
15       assert_routing(
16         { :path => "/api/0.6/ways.json", :method => :get },
17         { :controller => "api/ways", :action => "index", :format => "json" }
18       )
19       assert_routing(
20         { :path => "/api/0.6/ways", :method => :post },
21         { :controller => "api/ways", :action => "create" }
22       )
23       assert_routing(
24         { :path => "/api/0.6/way/1", :method => :get },
25         { :controller => "api/ways", :action => "show", :id => "1" }
26       )
27       assert_routing(
28         { :path => "/api/0.6/way/1.json", :method => :get },
29         { :controller => "api/ways", :action => "show", :id => "1", :format => "json" }
30       )
31       assert_routing(
32         { :path => "/api/0.6/way/1/full", :method => :get },
33         { :controller => "api/ways", :action => "show", :full => true, :id => "1" }
34       )
35       assert_routing(
36         { :path => "/api/0.6/way/1/full.json", :method => :get },
37         { :controller => "api/ways", :action => "show", :full => true, :id => "1", :format => "json" }
38       )
39       assert_routing(
40         { :path => "/api/0.6/way/1", :method => :put },
41         { :controller => "api/ways", :action => "update", :id => "1" }
42       )
43       assert_routing(
44         { :path => "/api/0.6/way/1", :method => :delete },
45         { :controller => "api/ways", :action => "destroy", :id => "1" }
46       )
47
48       assert_recognizes(
49         { :controller => "api/ways", :action => "create" },
50         { :path => "/api/0.6/way/create", :method => :put }
51       )
52     end
53
54     ##
55     # test fetching multiple ways
56     def test_index
57       way1 = create(:way)
58       way2 = create(:way, :deleted)
59       way3 = create(:way)
60       way4 = create(:way)
61
62       # check error when no parameter provided
63       get api_ways_path
64       assert_response :bad_request
65
66       # check error when no parameter value provided
67       get api_ways_path(:ways => "")
68       assert_response :bad_request
69
70       # test a working call
71       get api_ways_path(:ways => "#{way1.id},#{way2.id},#{way3.id},#{way4.id}")
72       assert_response :success
73       assert_select "osm" do
74         assert_select "way", :count => 4
75         assert_select "way[id='#{way1.id}'][visible='true']", :count => 1
76         assert_select "way[id='#{way2.id}'][visible='false']", :count => 1
77         assert_select "way[id='#{way3.id}'][visible='true']", :count => 1
78         assert_select "way[id='#{way4.id}'][visible='true']", :count => 1
79       end
80
81       # test a working call with json format
82       get api_ways_path(:ways => "#{way1.id},#{way2.id},#{way3.id},#{way4.id}", :format => "json")
83
84       js = ActiveSupport::JSON.decode(@response.body)
85       assert_not_nil js
86       assert_equal 4, js["elements"].count
87       assert_equal 4, (js["elements"].count { |a| a["type"] == "way" })
88       assert_equal 1, (js["elements"].count { |a| a["id"] == way1.id && a["visible"].nil? })
89       assert_equal 1, (js["elements"].count { |a| a["id"] == way2.id && a["visible"] == false })
90       assert_equal 1, (js["elements"].count { |a| a["id"] == way3.id && a["visible"].nil? })
91       assert_equal 1, (js["elements"].count { |a| a["id"] == way4.id && a["visible"].nil? })
92
93       # check error when a non-existent way is included
94       get api_ways_path(:ways => "#{way1.id},#{way2.id},#{way3.id},#{way4.id},0")
95       assert_response :not_found
96     end
97
98     # -------------------------------------
99     # Test showing ways.
100     # -------------------------------------
101
102     def test_show_not_found
103       get api_way_path(0)
104       assert_response :not_found
105     end
106
107     def test_show_deleted
108       get api_way_path(create(:way, :deleted))
109       assert_response :gone
110     end
111
112     def test_show
113       way = create(:way, :timestamp => "2021-02-03T00:00:00Z")
114       node = create(:node, :timestamp => "2021-04-05T00:00:00Z")
115       create(:way_node, :way => way, :node => node)
116
117       get api_way_path(way)
118
119       assert_response :success
120       assert_not_nil @response.header["Last-Modified"]
121       assert_equal "2021-02-03T00:00:00Z", Time.parse(@response.header["Last-Modified"]).utc.xmlschema
122     end
123
124     def test_show_json
125       way = create(:way_with_nodes, :nodes_count => 3)
126
127       get api_way_path(way, :format => "json")
128
129       assert_response :success
130
131       js = ActiveSupport::JSON.decode(@response.body)
132       assert_not_nil js
133       assert_equal 1, js["elements"].count
134       js_ways = js["elements"].filter { |e| e["type"] == "way" }
135       assert_equal 1, js_ways.count
136       assert_equal way.id, js_ways[0]["id"]
137       assert_equal 1, js_ways[0]["version"]
138     end
139
140     ##
141     # check the "full" mode
142     def test_show_full
143       way = create(:way_with_nodes, :nodes_count => 3)
144
145       get api_way_path(way, :full => true)
146
147       assert_response :success
148
149       # Check the way is correctly returned
150       assert_select "osm way[id='#{way.id}'][version='1'][visible='true']", 1
151
152       # check that each node in the way appears once in the output as a
153       # reference and as the node element.
154       way.nodes.each do |n|
155         assert_select "osm way nd[ref='#{n.id}']", 1
156         assert_select "osm node[id='#{n.id}'][version='1'][lat='#{format('%<lat>.7f', :lat => n.lat)}'][lon='#{format('%<lon>.7f', :lon => n.lon)}']", 1
157       end
158     end
159
160     def test_show_full_json
161       way = create(:way_with_nodes, :nodes_count => 3)
162
163       get api_way_path(way, :full => true, :format => "json")
164
165       assert_response :success
166
167       # Check the way is correctly returned
168       js = ActiveSupport::JSON.decode(@response.body)
169       assert_not_nil js
170       assert_equal 4, js["elements"].count
171       js_ways = js["elements"].filter { |e| e["type"] == "way" }
172       assert_equal 1, js_ways.count
173       assert_equal way.id, js_ways[0]["id"]
174       assert_equal 1, js_ways[0]["version"]
175
176       # check that each node in the way appears once in the output as a
177       # reference and as the node element.
178       js_nodes = js["elements"].filter { |e| e["type"] == "node" }
179       assert_equal 3, js_nodes.count
180
181       way.nodes.each_with_index do |n, i|
182         assert_equal n.id, js_ways[0]["nodes"][i]
183         js_nodes_with_id = js_nodes.filter { |e| e["id"] == n.id }
184         assert_equal 1, js_nodes_with_id.count
185         assert_equal n.id, js_nodes_with_id[0]["id"]
186         assert_equal 1, js_nodes_with_id[0]["version"]
187         assert_equal n.lat, js_nodes_with_id[0]["lat"]
188         assert_equal n.lon, js_nodes_with_id[0]["lon"]
189       end
190     end
191
192     def test_show_full_deleted
193       way = create(:way, :deleted)
194
195       get api_way_path(way, :full => true)
196
197       assert_response :gone
198     end
199
200     # -------------------------------------
201     # Test creating ways.
202     # -------------------------------------
203
204     def test_create_by_private_user
205       node1 = create(:node)
206       node2 = create(:node)
207
208       with_unchanging_request([:data_public => false]) do |headers, changeset|
209         osm = <<~OSM
210           <osm>
211             <way changeset='#{changeset.id}'>
212               <nd ref='#{node1.id}'/>
213               <nd ref='#{node2.id}'/>
214               <tag k='test' v='yes' />
215             </way>
216           </osm>
217         OSM
218
219         post api_ways_path, :params => osm, :headers => headers
220
221         assert_response :forbidden, "way upload did not return forbidden status"
222       end
223     end
224
225     def test_create
226       node1 = create(:node)
227       node2 = create(:node)
228
229       with_request do |headers, changeset|
230         assert_difference "Way.count" => 1,
231                           "WayNode.count" => 2 do
232           osm = <<~OSM
233             <osm>
234               <way changeset='#{changeset.id}'>
235                 <nd ref='#{node1.id}'/>
236                 <nd ref='#{node2.id}'/>
237                 <tag k='test' v='yes' />
238               </way>
239             </osm>
240           OSM
241
242           post api_ways_path, :params => osm, :headers => headers
243
244           assert_response :success, "way upload did not return success status"
245         end
246
247         created_way_id = @response.body
248         way = Way.find(created_way_id)
249         assert_equal [node1, node2], way.nodes
250         assert_equal changeset.id, way.changeset_id, "saved way does not belong to the correct changeset"
251         assert way.visible, "saved way is not visible"
252       end
253     end
254
255     # -------------------------------------
256     # Test creating some invalid ways.
257     # -------------------------------------
258
259     def test_create_invalid
260       node = create(:node)
261       private_user = create(:user, :data_public => false)
262       private_open_changeset = create(:changeset, :user => private_user)
263       private_closed_changeset = create(:changeset, :closed, :user => private_user)
264       user = create(:user)
265       open_changeset = create(:changeset, :user => user)
266       closed_changeset = create(:changeset, :closed, :user => user)
267
268       ## First test with a private user to make sure that they are not authorized
269       auth_header = bearer_authorization_header private_user
270
271       # use the first user's open changeset
272       # create a way with non-existing node
273       xml = <<~OSM
274         <osm>
275           <way changeset='#{private_open_changeset.id}'>
276             <nd ref='0'/>
277           </way>
278         </osm>
279       OSM
280       post api_ways_path, :params => xml, :headers => auth_header
281       # expect failure
282       assert_response :forbidden,
283                       "way upload with invalid node using a private user did not return 'forbidden'"
284
285       # create a way with no nodes
286       xml = <<~OSM
287         <osm>
288           <way changeset='#{private_open_changeset.id}'>
289           </way>
290         </osm>
291       OSM
292       post api_ways_path, :params => xml, :headers => auth_header
293       # expect failure
294       assert_response :forbidden,
295                       "way upload with no node using a private userdid not return 'forbidden'"
296
297       # create a way inside a closed changeset
298       xml = <<~OSM
299         <osm>
300           <way changeset='#{private_closed_changeset.id}'>
301             <nd ref='#{node.id}'/>
302           </way>
303         </osm>
304       OSM
305       post api_ways_path, :params => xml, :headers => auth_header
306       # expect failure
307       assert_response :forbidden,
308                       "way upload to closed changeset with a private user did not return 'forbidden'"
309
310       ## Now test with a public user
311       auth_header = bearer_authorization_header user
312
313       # use the first user's open changeset
314       # create a way with non-existing node
315       xml = <<~OSM
316         <osm>
317           <way changeset='#{open_changeset.id}'>
318             <nd ref='0'/>
319           </way>
320         </osm>
321       OSM
322       post api_ways_path, :params => xml, :headers => auth_header
323       # expect failure
324       assert_response :precondition_failed,
325                       "way upload with invalid node did not return 'precondition failed'"
326       assert_equal "Precondition failed: Way  requires the nodes with id in (0), which either do not exist, or are not visible.", @response.body
327
328       # create a way with no nodes
329       xml = <<~OSM
330         <osm>
331           <way changeset='#{open_changeset.id}'>
332           </way>
333         </osm>
334       OSM
335       post api_ways_path, :params => xml, :headers => auth_header
336       # expect failure
337       assert_response :precondition_failed,
338                       "way upload with no node did not return 'precondition failed'"
339       assert_equal "Precondition failed: Cannot create way: data is invalid.", @response.body
340
341       # create a way inside a closed changeset
342       xml = <<~OSM
343         <osm>
344           <way changeset='#{closed_changeset.id}'>
345             <nd ref='#{node.id}'/>
346           </way>
347         </osm>
348       OSM
349       post api_ways_path, :params => xml, :headers => auth_header
350       # expect failure
351       assert_response :conflict,
352                       "way upload to closed changeset did not return 'conflict'"
353
354       # create a way with a tag which is too long
355       xml = <<~OSM
356         <osm>
357           <way changeset='#{open_changeset.id}'>
358             <nd ref='#{node.id}'/>
359             <tag k='foo' v='#{'x' * 256}'/>
360           </way>
361         </osm>
362       OSM
363       post api_ways_path, :params => xml, :headers => auth_header
364       # expect failure
365       assert_response :bad_request,
366                       "way upload to with too long tag did not return 'bad_request'"
367     end
368
369     # -------------------------------------
370     # Test deleting ways.
371     # -------------------------------------
372
373     def test_destroy
374       private_user = create(:user, :data_public => false)
375       private_open_changeset = create(:changeset, :user => private_user)
376       private_closed_changeset = create(:changeset, :closed, :user => private_user)
377       private_way = create(:way, :changeset => private_open_changeset)
378       private_deleted_way = create(:way, :deleted, :changeset => private_open_changeset)
379       private_used_way = create(:way, :changeset => private_open_changeset)
380       create(:relation_member, :member => private_used_way)
381       user = create(:user)
382       open_changeset = create(:changeset, :user => user)
383       closed_changeset = create(:changeset, :closed, :user => user)
384       way = create(:way, :changeset => open_changeset)
385       deleted_way = create(:way, :deleted, :changeset => open_changeset)
386       used_way = create(:way, :changeset => open_changeset)
387       relation_member = create(:relation_member, :member => used_way)
388       relation = relation_member.relation
389
390       # first try to delete way without auth
391       delete api_way_path(way)
392       assert_response :unauthorized
393
394       # now set auth using the private user
395       auth_header = bearer_authorization_header private_user
396
397       # this shouldn't work as with the 0.6 api we need pay load to delete
398       delete api_way_path(private_way), :headers => auth_header
399       assert_response :forbidden
400
401       # Now try without having a changeset
402       xml = "<osm><way id='#{private_way.id}'/></osm>"
403       delete api_way_path(private_way), :params => xml.to_s, :headers => auth_header
404       assert_response :forbidden
405
406       # try to delete with an invalid (closed) changeset
407       xml = update_changeset(xml_for_way(private_way), private_closed_changeset.id)
408       delete api_way_path(private_way), :params => xml.to_s, :headers => auth_header
409       assert_response :forbidden
410
411       # try to delete with an invalid (non-existent) changeset
412       xml = update_changeset(xml_for_way(private_way), 0)
413       delete api_way_path(private_way), :params => xml.to_s, :headers => auth_header
414       assert_response :forbidden
415
416       # Now try with a valid changeset
417       xml = xml_for_way(private_way)
418       delete api_way_path(private_way), :params => xml.to_s, :headers => auth_header
419       assert_response :forbidden
420
421       # check the returned value - should be the new version number
422       # valid delete should return the new version number, which should
423       # be greater than the old version number
424       # assert @response.body.to_i > current_ways(:visible_way).version,
425       #   "delete request should return a new version number for way"
426
427       # this won't work since the way is already deleted
428       xml = xml_for_way(private_deleted_way)
429       delete api_way_path(private_deleted_way), :params => xml.to_s, :headers => auth_header
430       assert_response :forbidden
431
432       # this shouldn't work as the way is used in a relation
433       xml = xml_for_way(private_used_way)
434       delete api_way_path(private_used_way), :params => xml.to_s, :headers => auth_header
435       assert_response :forbidden,
436                       "shouldn't be able to delete a way used in a relation (#{@response.body}), when done by a private user"
437
438       # this won't work since the way never existed
439       delete api_way_path(0), :headers => auth_header
440       assert_response :forbidden
441
442       ### Now check with a public user
443       # now set auth
444       auth_header = bearer_authorization_header user
445
446       # this shouldn't work as with the 0.6 api we need pay load to delete
447       delete api_way_path(way), :headers => auth_header
448       assert_response :bad_request
449
450       # Now try without having a changeset
451       xml = "<osm><way id='#{way.id}'/></osm>"
452       delete api_way_path(way), :params => xml.to_s, :headers => auth_header
453       assert_response :bad_request
454
455       # try to delete with an invalid (closed) changeset
456       xml = update_changeset(xml_for_way(way), closed_changeset.id)
457       delete api_way_path(way), :params => xml.to_s, :headers => auth_header
458       assert_response :conflict
459
460       # try to delete with an invalid (non-existent) changeset
461       xml = update_changeset(xml_for_way(way), 0)
462       delete api_way_path(way), :params => xml.to_s, :headers => auth_header
463       assert_response :conflict
464
465       # Now try with a valid changeset
466       xml = xml_for_way(way)
467       delete api_way_path(way), :params => xml.to_s, :headers => auth_header
468       assert_response :success
469
470       # check the returned value - should be the new version number
471       # valid delete should return the new version number, which should
472       # be greater than the old version number
473       assert_operator @response.body.to_i, :>, way.version, "delete request should return a new version number for way"
474
475       # this won't work since the way is already deleted
476       xml = xml_for_way(deleted_way)
477       delete api_way_path(deleted_way), :params => xml.to_s, :headers => auth_header
478       assert_response :gone
479
480       # this shouldn't work as the way is used in a relation
481       xml = xml_for_way(used_way)
482       delete api_way_path(used_way), :params => xml.to_s, :headers => auth_header
483       assert_response :precondition_failed,
484                       "shouldn't be able to delete a way used in a relation (#{@response.body})"
485       assert_equal "Precondition failed: Way #{used_way.id} is still used by relations #{relation.id}.", @response.body
486
487       # this won't work since the way never existed
488       delete api_way_path(0), :params => xml.to_s, :headers => auth_header
489       assert_response :not_found
490     end
491
492     ##
493     # tests whether the API works and prevents incorrect use while trying
494     # to update ways.
495     def test_update
496       private_user = create(:user, :data_public => false)
497       private_way = create(:way, :changeset => create(:changeset, :user => private_user))
498       user = create(:user)
499       way = create(:way, :changeset => create(:changeset, :user => user))
500       node = create(:node)
501       create(:way_node, :way => private_way, :node => node)
502       create(:way_node, :way => way, :node => node)
503
504       ## First test with no user credentials
505       # try and update a way without authorisation
506       xml = xml_for_way(way)
507       put api_way_path(way), :params => xml.to_s
508       assert_response :unauthorized
509
510       ## Second test with the private user
511
512       # setup auth
513       auth_header = bearer_authorization_header private_user
514
515       ## trying to break changesets
516
517       # try and update in someone else's changeset
518       xml = update_changeset(xml_for_way(private_way),
519                              create(:changeset).id)
520       put api_way_path(private_way), :params => xml.to_s, :headers => auth_header
521       assert_require_public_data "update with other user's changeset should be forbidden when date isn't public"
522
523       # try and update in a closed changeset
524       xml = update_changeset(xml_for_way(private_way),
525                              create(:changeset, :closed, :user => private_user).id)
526       put api_way_path(private_way), :params => xml.to_s, :headers => auth_header
527       assert_require_public_data "update with closed changeset should be forbidden, when data isn't public"
528
529       # try and update in a non-existant changeset
530       xml = update_changeset(xml_for_way(private_way), 0)
531       put api_way_path(private_way), :params => xml.to_s, :headers => auth_header
532       assert_require_public_data("update with changeset=0 should be forbidden, when data isn't public")
533
534       ## try and submit invalid updates
535       xml = xml_replace_node(xml_for_way(private_way), node.id, 9999)
536       put api_way_path(private_way), :params => xml.to_s, :headers => auth_header
537       assert_require_public_data "way with non-existent node should be forbidden, when data isn't public"
538
539       xml = xml_replace_node(xml_for_way(private_way), node.id, create(:node, :deleted).id)
540       put api_way_path(private_way), :params => xml.to_s, :headers => auth_header
541       assert_require_public_data "way with deleted node should be forbidden, when data isn't public"
542
543       ## finally, produce a good request which will still not work
544       xml = xml_for_way(private_way)
545       put api_way_path(private_way), :params => xml.to_s, :headers => auth_header
546       assert_require_public_data "should have failed with a forbidden when data isn't public"
547
548       ## Finally test with the public user
549
550       # setup auth
551       auth_header = bearer_authorization_header user
552
553       ## trying to break changesets
554
555       # try and update in someone else's changeset
556       xml = update_changeset(xml_for_way(way),
557                              create(:changeset).id)
558       put api_way_path(way), :params => xml.to_s, :headers => auth_header
559       assert_response :conflict, "update with other user's changeset should be rejected"
560
561       # try and update in a closed changeset
562       xml = update_changeset(xml_for_way(way),
563                              create(:changeset, :closed, :user => user).id)
564       put api_way_path(way), :params => xml.to_s, :headers => auth_header
565       assert_response :conflict, "update with closed changeset should be rejected"
566
567       # try and update in a non-existant changeset
568       xml = update_changeset(xml_for_way(way), 0)
569       put api_way_path(way), :params => xml.to_s, :headers => auth_header
570       assert_response :conflict, "update with changeset=0 should be rejected"
571
572       ## try and submit invalid updates
573       xml = xml_replace_node(xml_for_way(way), node.id, 9999)
574       put api_way_path(way), :params => xml.to_s, :headers => auth_header
575       assert_response :precondition_failed, "way with non-existent node should be rejected"
576
577       xml = xml_replace_node(xml_for_way(way), node.id, create(:node, :deleted).id)
578       put api_way_path(way), :params => xml.to_s, :headers => auth_header
579       assert_response :precondition_failed, "way with deleted node should be rejected"
580
581       ## next, attack the versioning
582       current_way_version = way.version
583
584       # try and submit a version behind
585       xml = xml_attr_rewrite(xml_for_way(way),
586                              "version", current_way_version - 1)
587       put api_way_path(way), :params => xml.to_s, :headers => auth_header
588       assert_response :conflict, "should have failed on old version number"
589
590       # try and submit a version ahead
591       xml = xml_attr_rewrite(xml_for_way(way),
592                              "version", current_way_version + 1)
593       put api_way_path(way), :params => xml.to_s, :headers => auth_header
594       assert_response :conflict, "should have failed on skipped version number"
595
596       # try and submit total crap in the version field
597       xml = xml_attr_rewrite(xml_for_way(way),
598                              "version", "p1r4t3s!")
599       put api_way_path(way), :params => xml.to_s, :headers => auth_header
600       assert_response :conflict,
601                       "should not be able to put 'p1r4at3s!' in the version field"
602
603       ## try an update with the wrong ID
604       xml = xml_for_way(create(:way))
605       put api_way_path(way), :params => xml.to_s, :headers => auth_header
606       assert_response :bad_request,
607                       "should not be able to update a way with a different ID from the XML"
608
609       ## try an update with a minimal valid XML doc which isn't a well-formed OSM doc.
610       xml = "<update/>"
611       put api_way_path(way), :params => xml.to_s, :headers => auth_header
612       assert_response :bad_request,
613                       "should not be able to update a way with non-OSM XML doc."
614
615       ## finally, produce a good request which should work
616       xml = xml_for_way(way)
617       put api_way_path(way), :params => xml.to_s, :headers => auth_header
618       assert_response :success, "a valid update request failed"
619     end
620
621     # ------------------------------------------------------------
622     # test tags handling
623     # ------------------------------------------------------------
624
625     ##
626     # Try adding a new tag to a way
627     def test_add_tags
628       private_user = create(:user, :data_public => false)
629       private_way = create(:way_with_nodes, :nodes_count => 2, :changeset => create(:changeset, :user => private_user))
630       user = create(:user)
631       way = create(:way_with_nodes, :nodes_count => 2, :changeset => create(:changeset, :user => user))
632
633       ## Try with the non-public user
634       # setup auth
635       auth_header = bearer_authorization_header private_user
636
637       # add an identical tag to the way
638       tag_xml = XML::Node.new("tag")
639       tag_xml["k"] = "new"
640       tag_xml["v"] = "yes"
641
642       # add the tag into the existing xml
643       way_xml = xml_for_way(private_way)
644       way_xml.find("//osm/way").first << tag_xml
645
646       # try and upload it
647       put api_way_path(private_way), :params => way_xml.to_s, :headers => auth_header
648       assert_response :forbidden,
649                       "adding a duplicate tag to a way for a non-public should fail with 'forbidden'"
650
651       ## Now try with the public user
652       # setup auth
653       auth_header = bearer_authorization_header user
654
655       # add an identical tag to the way
656       tag_xml = XML::Node.new("tag")
657       tag_xml["k"] = "new"
658       tag_xml["v"] = "yes"
659
660       # add the tag into the existing xml
661       way_xml = xml_for_way(way)
662       way_xml.find("//osm/way").first << tag_xml
663
664       # try and upload it
665       put api_way_path(way), :params => way_xml.to_s, :headers => auth_header
666       assert_response :success,
667                       "adding a new tag to a way should succeed"
668       assert_equal way.version + 1, @response.body.to_i
669     end
670
671     ##
672     # Try adding a duplicate of an existing tag to a way
673     def test_add_duplicate_tags
674       private_user = create(:user, :data_public => false)
675       private_way = create(:way, :changeset => create(:changeset, :user => private_user))
676       private_existing_tag = create(:way_tag, :way => private_way)
677       user = create(:user)
678       way = create(:way, :changeset => create(:changeset, :user => user))
679       existing_tag = create(:way_tag, :way => way)
680
681       ## Try with the non-public user
682       # setup auth
683       auth_header = bearer_authorization_header private_user
684
685       # add an identical tag to the way
686       tag_xml = XML::Node.new("tag")
687       tag_xml["k"] = private_existing_tag.k
688       tag_xml["v"] = private_existing_tag.v
689
690       # add the tag into the existing xml
691       way_xml = xml_for_way(private_way)
692       way_xml.find("//osm/way").first << tag_xml
693
694       # try and upload it
695       put api_way_path(private_way), :params => way_xml.to_s, :headers => auth_header
696       assert_response :forbidden,
697                       "adding a duplicate tag to a way for a non-public should fail with 'forbidden'"
698
699       ## Now try with the public user
700       # setup auth
701       auth_header = bearer_authorization_header user
702
703       # add an identical tag to the way
704       tag_xml = XML::Node.new("tag")
705       tag_xml["k"] = existing_tag.k
706       tag_xml["v"] = existing_tag.v
707
708       # add the tag into the existing xml
709       way_xml = xml_for_way(way)
710       way_xml.find("//osm/way").first << tag_xml
711
712       # try and upload it
713       put api_way_path(way), :params => way_xml.to_s, :headers => auth_header
714       assert_response :bad_request,
715                       "adding a duplicate tag to a way should fail with 'bad request'"
716       assert_equal "Element way/#{way.id} has duplicate tags with key #{existing_tag.k}", @response.body
717     end
718
719     ##
720     # Try adding a new duplicate tags to a way
721     def test_new_duplicate_tags
722       private_user = create(:user, :data_public => false)
723       private_way = create(:way, :changeset => create(:changeset, :user => private_user))
724       user = create(:user)
725       way = create(:way, :changeset => create(:changeset, :user => user))
726
727       ## First test with the non-public user so should be rejected
728       # setup auth
729       auth_header = bearer_authorization_header private_user
730
731       # create duplicate tag
732       tag_xml = XML::Node.new("tag")
733       tag_xml["k"] = "i_am_a_duplicate"
734       tag_xml["v"] = "foobar"
735
736       # add the tag into the existing xml
737       way_xml = xml_for_way(private_way)
738
739       # add two copies of the tag
740       way_xml.find("//osm/way").first << tag_xml.copy(true) << tag_xml
741
742       # try and upload it
743       put api_way_path(private_way), :params => way_xml.to_s, :headers => auth_header
744       assert_response :forbidden,
745                       "adding new duplicate tags to a way using a non-public user should fail with 'forbidden'"
746
747       ## Now test with the public user
748       # setup auth
749       auth_header = bearer_authorization_header user
750
751       # create duplicate tag
752       tag_xml = XML::Node.new("tag")
753       tag_xml["k"] = "i_am_a_duplicate"
754       tag_xml["v"] = "foobar"
755
756       # add the tag into the existing xml
757       way_xml = xml_for_way(way)
758
759       # add two copies of the tag
760       way_xml.find("//osm/way").first << tag_xml.copy(true) << tag_xml
761
762       # try and upload it
763       put api_way_path(way), :params => way_xml.to_s, :headers => auth_header
764       assert_response :bad_request,
765                       "adding new duplicate tags to a way should fail with 'bad request'"
766       assert_equal "Element way/#{way.id} has duplicate tags with key i_am_a_duplicate", @response.body
767     end
768
769     ##
770     # Try adding a new duplicate tags to a way.
771     # But be a bit subtle - use unicode decoding ambiguities to use different
772     # binary strings which have the same decoding.
773     def test_invalid_duplicate_tags
774       private_user = create(:user, :data_public => false)
775       private_changeset = create(:changeset, :user => private_user)
776       user = create(:user)
777       changeset = create(:changeset, :user => user)
778
779       ## First make sure that you can't with a non-public user
780       # setup auth
781       auth_header = bearer_authorization_header private_user
782
783       # add the tag into the existing xml
784       way_str = <<~OSM
785         <osm>
786           <way changeset='#{private_changeset.id}'>
787             <tag k='addr:housenumber' v='1'/>
788             <tag k='addr:housenumber' v='2'/>
789           </way>
790         </osm>
791       OSM
792
793       # try and upload it
794       post api_ways_path, :params => way_str, :headers => auth_header
795       assert_response :forbidden,
796                       "adding new duplicate tags to a way with a non-public user should fail with 'forbidden'"
797
798       ## Now do it with a public user
799       # setup auth
800       auth_header = bearer_authorization_header user
801
802       # add the tag into the existing xml
803       way_str = <<~OSM
804         <osm>
805           <way changeset='#{changeset.id}'>
806             <tag k='addr:housenumber' v='1'/>
807             <tag k='addr:housenumber' v='2'/>
808           </way>
809         </osm>
810       OSM
811
812       # try and upload it
813       post api_ways_path, :params => way_str, :headers => auth_header
814       assert_response :bad_request,
815                       "adding new duplicate tags to a way should fail with 'bad request'"
816       assert_equal "Element way/ has duplicate tags with key addr:housenumber", @response.body
817     end
818
819     ##
820     # test initial rate limit
821     def test_initial_rate_limit
822       # create a user
823       user = create(:user)
824
825       # create some nodes
826       node1 = create(:node)
827       node2 = create(:node)
828
829       # create a changeset that puts us near the initial rate limit
830       changeset = create(:changeset, :user => user,
831                                      :created_at => Time.now.utc - 5.minutes,
832                                      :num_changes => Settings.initial_changes_per_hour - 1)
833
834       # create authentication header
835       auth_header = bearer_authorization_header user
836
837       # try creating a way
838       xml = <<~OSM
839         <osm>
840           <way changeset='#{changeset.id}'>
841             <nd ref='#{node1.id}'/>
842             <nd ref='#{node2.id}'/>
843           </way>
844         </osm>
845       OSM
846       post api_ways_path, :params => xml, :headers => auth_header
847       assert_response :success, "way create did not return success status"
848
849       # get the id of the way we created
850       wayid = @response.body
851
852       # try updating the way, which should be rate limited
853       xml = <<~OSM
854         <osm>
855           <way id='#{wayid}' version='1' changeset='#{changeset.id}'>
856             <nd ref='#{node2.id}'/>
857             <nd ref='#{node1.id}'/>
858           </way>
859         </osm>
860       OSM
861       put api_way_path(wayid), :params => xml, :headers => auth_header
862       assert_response :too_many_requests, "way update did not hit rate limit"
863
864       # try deleting the way, which should be rate limited
865       xml = "<osm><way id='#{wayid}' version='2' changeset='#{changeset.id}'/></osm>"
866       delete api_way_path(wayid), :params => xml, :headers => auth_header
867       assert_response :too_many_requests, "way delete did not hit rate limit"
868
869       # try creating a way, which should be rate limited
870       xml = <<~OSM
871         <osm>
872           <way changeset='#{changeset.id}'>
873             <nd ref='#{node1.id}'/>
874             <nd ref='#{node2.id}'/>
875           </way>
876         </osm>
877       OSM
878       post api_ways_path, :params => xml, :headers => auth_header
879       assert_response :too_many_requests, "way create did not hit rate limit"
880     end
881
882     ##
883     # test maximum rate limit
884     def test_maximum_rate_limit
885       # create a user
886       user = create(:user)
887
888       # create some nodes
889       node1 = create(:node)
890       node2 = create(:node)
891
892       # create a changeset to establish our initial edit time
893       changeset = create(:changeset, :user => user,
894                                      :created_at => Time.now.utc - 28.days)
895
896       # create changeset to put us near the maximum rate limit
897       total_changes = Settings.max_changes_per_hour - 1
898       while total_changes.positive?
899         changes = [total_changes, Changeset::MAX_ELEMENTS].min
900         changeset = create(:changeset, :user => user,
901                                        :created_at => Time.now.utc - 5.minutes,
902                                        :num_changes => changes)
903         total_changes -= changes
904       end
905
906       # create authentication header
907       auth_header = bearer_authorization_header user
908
909       # try creating a way
910       xml = <<~OSM
911         <osm>
912           <way changeset='#{changeset.id}'>
913             <nd ref='#{node1.id}'/>
914             <nd ref='#{node2.id}'/>
915           </way>
916         </osm>
917       OSM
918       post api_ways_path, :params => xml, :headers => auth_header
919       assert_response :success, "way create did not return success status"
920
921       # get the id of the way we created
922       wayid = @response.body
923
924       # try updating the way, which should be rate limited
925       xml = <<~OSM
926         <osm>
927           <way id='#{wayid}' version='1' changeset='#{changeset.id}'>
928             <nd ref='#{node2.id}'/>
929             <nd ref='#{node1.id}'/>
930           </way>
931         </osm>
932       OSM
933       put api_way_path(wayid), :params => xml, :headers => auth_header
934       assert_response :too_many_requests, "way update did not hit rate limit"
935
936       # try deleting the way, which should be rate limited
937       xml = "<osm><way id='#{wayid}' version='2' changeset='#{changeset.id}'/></osm>"
938       delete api_way_path(wayid), :params => xml, :headers => auth_header
939       assert_response :too_many_requests, "way delete did not hit rate limit"
940
941       # try creating a way, which should be rate limited
942       xml = <<~OSM
943         <osm>
944           <way changeset='#{changeset.id}'>
945             <nd ref='#{node1.id}'/>
946             <nd ref='#{node2.id}'/>
947           </way>
948         </osm>
949       OSM
950       post api_ways_path, :params => xml, :headers => auth_header
951       assert_response :too_many_requests, "way create did not hit rate limit"
952     end
953
954     private
955
956     def affected_models
957       [Way, WayNode, WayTag,
958        OldWay, OldWayNode, OldWayTag]
959     end
960
961     ##
962     # update an attribute in the way element
963     def xml_attr_rewrite(xml, name, value)
964       xml.find("//osm/way").first[name] = value.to_s
965       xml
966     end
967
968     ##
969     # replace a node in a way element
970     def xml_replace_node(xml, old_node, new_node)
971       xml.find("//osm/way/nd[@ref='#{old_node}']").first["ref"] = new_node.to_s
972       xml
973     end
974   end
975 end