]> git.openstreetmap.org Git - rails.git/blob - test/controllers/api/nodes_controller_test.rb
Add frozen_string_literal comments to ruby files
[rails.git] / test / controllers / api / nodes_controller_test.rb
1 # frozen_string_literal: true
2
3 require "test_helper"
4 require_relative "elements_test_helper"
5
6 module Api
7   class NodesControllerTest < ActionDispatch::IntegrationTest
8     include ElementsTestHelper
9
10     ##
11     # test all routes which lead to this controller
12     def test_routes
13       assert_routing(
14         { :path => "/api/0.6/nodes", :method => :get },
15         { :controller => "api/nodes", :action => "index" }
16       )
17       assert_routing(
18         { :path => "/api/0.6/nodes.json", :method => :get },
19         { :controller => "api/nodes", :action => "index", :format => "json" }
20       )
21       assert_routing(
22         { :path => "/api/0.6/nodes", :method => :post },
23         { :controller => "api/nodes", :action => "create" }
24       )
25       assert_routing(
26         { :path => "/api/0.6/node/1", :method => :get },
27         { :controller => "api/nodes", :action => "show", :id => "1" }
28       )
29       assert_routing(
30         { :path => "/api/0.6/node/1.json", :method => :get },
31         { :controller => "api/nodes", :action => "show", :id => "1", :format => "json" }
32       )
33       assert_routing(
34         { :path => "/api/0.6/node/1", :method => :put },
35         { :controller => "api/nodes", :action => "update", :id => "1" }
36       )
37       assert_routing(
38         { :path => "/api/0.6/node/1", :method => :delete },
39         { :controller => "api/nodes", :action => "destroy", :id => "1" }
40       )
41
42       assert_recognizes(
43         { :controller => "api/nodes", :action => "create" },
44         { :path => "/api/0.6/node/create", :method => :put }
45       )
46     end
47
48     ##
49     # test fetching multiple nodes
50     def test_index
51       node1 = create(:node)
52       node2 = create(:node, :deleted)
53       node3 = create(:node)
54       node4 = create(:node, :with_history, :version => 2)
55       node5 = create(:node, :deleted, :with_history, :version => 2)
56
57       # check error when no parameter provided
58       get api_nodes_path
59       assert_response :bad_request
60
61       # check error when no parameter value provided
62       get api_nodes_path(:nodes => "")
63       assert_response :bad_request
64
65       # test a working call
66       get api_nodes_path(:nodes => "#{node1.id},#{node2.id},#{node3.id},#{node4.id},#{node5.id}")
67       assert_response :success
68       assert_select "osm" do
69         assert_select "node", :count => 5
70         assert_select "node[id='#{node1.id}'][visible='true']", :count => 1
71         assert_select "node[id='#{node2.id}'][visible='false']", :count => 1
72         assert_select "node[id='#{node3.id}'][visible='true']", :count => 1
73         assert_select "node[id='#{node4.id}'][visible='true']", :count => 1
74         assert_select "node[id='#{node5.id}'][visible='false']", :count => 1
75       end
76
77       # test a working call with json format
78       get api_nodes_path(:nodes => "#{node1.id},#{node2.id},#{node3.id},#{node4.id},#{node5.id}", :format => "json")
79
80       js = ActiveSupport::JSON.decode(@response.body)
81       assert_not_nil js
82       assert_equal 5, js["elements"].count
83       assert_equal(5, js["elements"].count { |a| a["type"] == "node" })
84       assert_equal(1, js["elements"].count { |a| a["id"] == node1.id && a["visible"].nil? })
85       assert_equal(1, js["elements"].count { |a| a["id"] == node2.id && a["visible"] == false })
86       assert_equal(1, js["elements"].count { |a| a["id"] == node3.id && a["visible"].nil? })
87       assert_equal(1, js["elements"].count { |a| a["id"] == node4.id && a["visible"].nil? })
88       assert_equal(1, js["elements"].count { |a| a["id"] == node5.id && a["visible"] == false })
89
90       # check error when a non-existent node is included
91       get api_nodes_path(:nodes => "#{node1.id},#{node2.id},#{node3.id},#{node4.id},#{node5.id},0")
92       assert_response :not_found
93     end
94
95     def test_create_when_unauthorized
96       with_unchanging_request do |_headers, changeset|
97         osm = "<osm><node lat='0' lon='0' changeset='#{changeset.id}'/></osm>"
98
99         post api_nodes_path, :params => osm
100
101         assert_response :unauthorized
102       end
103     end
104
105     def test_create_by_private_user
106       with_unchanging_request([:data_public => false]) do |headers, changeset|
107         osm = "<osm><node lat='0' lon='0' changeset='#{changeset.id}'/></osm>"
108
109         post api_nodes_path, :params => osm, :headers => headers
110
111         assert_require_public_data "node create did not return forbidden status"
112       end
113     end
114
115     def test_create
116       with_request do |headers, changeset|
117         lat = rand(-50..50) + rand
118         lon = rand(-50..50) + rand
119
120         assert_difference "Node.count", 1 do
121           osm = "<osm><node lat='#{lat}' lon='#{lon}' changeset='#{changeset.id}'/></osm>"
122
123           post api_nodes_path, :params => osm, :headers => headers
124
125           assert_response :success, "node upload did not return success status"
126         end
127
128         created_node_id = @response.body
129         node = Node.find(created_node_id)
130         assert_in_delta lat * 10000000, node.latitude, 1, "saved node does not match requested latitude"
131         assert_in_delta lon * 10000000, node.longitude, 1, "saved node does not match requested longitude"
132         assert_equal changeset.id, node.changeset_id, "saved node does not belong to changeset that it was created in"
133         assert node.visible, "saved node is not visible"
134
135         changeset.reload
136         assert_equal 1, changeset.num_changes
137         assert_predicate changeset, :num_type_changes_in_sync?
138         assert_equal 1, changeset.num_created_nodes
139       end
140     end
141
142     def test_create_in_missing_changeset
143       with_unchanging_request do |headers|
144         osm = "<osm><node lat='0' lon='0' changeset='0'/></osm>"
145
146         post api_nodes_path, :params => osm, :headers => headers
147
148         assert_response :conflict
149       end
150     end
151
152     def test_create_with_invalid_osm_structure
153       with_unchanging_request do |headers|
154         osm = "<create/>"
155
156         post api_nodes_path, :params => osm, :headers => headers
157
158         assert_response :bad_request, "node upload did not return bad_request status"
159         assert_equal "Cannot parse valid node from xml string <create/>. XML doesn't contain an osm/node element.", @response.body
160       end
161     end
162
163     def test_create_without_lat
164       with_unchanging_request do |headers, changeset|
165         osm = "<osm><node lon='3.23' changeset='#{changeset.id}'/></osm>"
166
167         post api_nodes_path, :params => osm, :headers => headers
168
169         assert_response :bad_request, "node upload did not return bad_request status"
170         assert_equal "Cannot parse valid node from xml string <node lon=\"3.23\" changeset=\"#{changeset.id}\"/>. lat missing", @response.body
171       end
172     end
173
174     def test_create_without_lon
175       with_unchanging_request do |headers, changeset|
176         osm = "<osm><node lat='3.434' changeset='#{changeset.id}'/></osm>"
177
178         post api_nodes_path, :params => osm, :headers => headers
179
180         assert_response :bad_request, "node upload did not return bad_request status"
181         assert_equal "Cannot parse valid node from xml string <node lat=\"3.434\" changeset=\"#{changeset.id}\"/>. lon missing", @response.body
182       end
183     end
184
185     def test_create_with_non_numeric_lat
186       with_unchanging_request do |headers, changeset|
187         osm = "<osm><node lat='abc' lon='3.23' changeset='#{changeset.id}'/></osm>"
188
189         post api_nodes_path, :params => osm, :headers => headers
190
191         assert_response :bad_request, "node upload did not return bad_request status"
192         assert_equal "Cannot parse valid node from xml string <node lat=\"abc\" lon=\"3.23\" changeset=\"#{changeset.id}\"/>. lat not a number", @response.body
193       end
194     end
195
196     def test_create_with_non_numeric_lon
197       with_unchanging_request do |headers, changeset|
198         osm = "<osm><node lat='3.434' lon='abc' changeset='#{changeset.id}'/></osm>"
199
200         post api_nodes_path, :params => osm, :headers => headers
201
202         assert_response :bad_request, "node upload did not return bad_request status"
203         assert_equal "Cannot parse valid node from xml string <node lat=\"3.434\" lon=\"abc\" changeset=\"#{changeset.id}\"/>. lon not a number", @response.body
204       end
205     end
206
207     def test_create_with_tag_too_long
208       with_unchanging_request do |headers, changeset|
209         osm = "<osm><node lat='3.434' lon='3.23' changeset='#{changeset.id}'><tag k='foo' v='#{'x' * 256}'/></node></osm>"
210
211         post api_nodes_path, :params => osm, :headers => headers
212
213         assert_response :bad_request, "node upload did not return bad_request status"
214         assert_match(/ v: is too long \(maximum is 255 characters\) /, @response.body)
215       end
216     end
217
218     ##
219     # try and put something into a string that the API might
220     # use unquoted and therefore allow code injection
221     def test_create_with_string_injection_by_private_user
222       with_unchanging_request([:data_public => false]) do |headers, changeset|
223         osm = <<~OSM
224           <osm>
225             <node lat='0' lon='0' changeset='#{changeset.id}'>
226               <tag k='\#{@user.inspect}' v='0'/>
227             </node>
228           </osm>
229         OSM
230
231         post api_nodes_path, :params => osm, :headers => headers
232
233         assert_require_public_data "Shouldn't be able to create with non-public user"
234       end
235     end
236
237     ##
238     # try and put something into a string that the API might
239     # use unquoted and therefore allow code injection
240     def test_create_with_string_injection
241       with_request do |headers, changeset|
242         assert_difference "Node.count", 1 do
243           osm = <<~OSM
244             <osm>
245               <node lat='0' lon='0' changeset='#{changeset.id}'>
246                 <tag k='\#{@user.inspect}' v='0'/>
247               </node>
248             </osm>
249           OSM
250
251           post api_nodes_path, :params => osm, :headers => headers
252
253           assert_response :success
254         end
255
256         created_node_id = @response.body
257         db_node = Node.find(created_node_id)
258
259         get api_node_path(created_node_id)
260
261         assert_response :success
262
263         api_node = Node.from_xml(@response.body)
264         assert_not_nil api_node, "downloaded node is nil, but shouldn't be"
265         assert_equal db_node.tags, api_node.tags, "tags are corrupted"
266         assert_includes api_node.tags, "\#{@user.inspect}"
267       end
268     end
269
270     def test_create_race_condition
271       user = create(:user)
272       changeset = create(:changeset, :user => user)
273       auth_header = bearer_authorization_header user
274       path = api_nodes_path
275       concurrency_level = 16
276
277       threads = Array.new(concurrency_level) do
278         Thread.new do
279           osm = "<osm><node lat='0' lon='0' changeset='#{changeset.id}'/></osm>"
280           post path, :params => osm, :headers => auth_header
281         end
282       end
283       threads.each(&:join)
284
285       changeset.reload
286       assert_equal concurrency_level, changeset.num_changes
287       assert_predicate changeset, :num_type_changes_in_sync?
288       assert_equal concurrency_level, changeset.num_created_nodes
289     end
290
291     def test_show_not_found
292       get api_node_path(0)
293       assert_response :not_found
294     end
295
296     def test_show_deleted
297       get api_node_path(create(:node, :deleted))
298       assert_response :gone
299     end
300
301     def test_show
302       node = create(:node, :timestamp => "2021-02-03T00:00:00Z")
303
304       get api_node_path(node)
305
306       assert_response :success
307       assert_not_nil @response.header["Last-Modified"]
308       assert_equal "2021-02-03T00:00:00Z", Time.parse(@response.header["Last-Modified"]).utc.xmlschema
309     end
310
311     def test_show_lat_lon_decimal_format
312       node = create(:node, :latitude => (0.00004 * OldNode::SCALE).to_i, :longitude => (0.00008 * OldNode::SCALE).to_i)
313
314       get api_node_path(node)
315
316       assert_match(/lat="0.0000400"/, response.body)
317       assert_match(/lon="0.0000800"/, response.body)
318     end
319
320     def test_destroy_when_unauthorized
321       with_unchanging(:node) do |node|
322         delete api_node_path(node)
323
324         assert_response :unauthorized
325       end
326     end
327
328     def test_destroy_in_closed_changeset_by_private_user
329       with_unchanging(:node) do |node|
330         with_unchanging_request([:data_public => false], [:closed]) do |headers, changeset|
331           osm_xml = xml_for_node node
332           osm_xml = update_changeset osm_xml, changeset.id
333
334           delete api_node_path(node), :params => osm_xml.to_s, :headers => headers
335
336           assert_require_public_data "non-public user shouldn't be able to delete node"
337         end
338       end
339     end
340
341     def test_destroy_in_missing_changeset_by_private_user
342       with_unchanging(:node) do |node|
343         with_unchanging_request([:data_public => false]) do |headers|
344           osm_xml = xml_for_node node
345           osm_xml = update_changeset osm_xml, 0
346
347           delete api_node_path(node), :params => osm_xml.to_s, :headers => headers
348
349           assert_require_public_data "shouldn't be able to delete node, when user's data is private"
350         end
351       end
352     end
353
354     def test_destroy_by_private_user
355       with_unchanging(:node) do |node|
356         with_unchanging_request([:data_public => false]) do |headers, changeset|
357           osm_xml = xml_for_node node
358           osm_xml = update_changeset osm_xml, changeset.id
359
360           delete api_node_path(node), :params => osm_xml.to_s, :headers => headers
361
362           assert_require_public_data "shouldn't be able to delete node when user's data isn't public'"
363         end
364       end
365     end
366
367     def test_destroy_deleted_node_by_private_user
368       with_unchanging(:node, :deleted) do |node|
369         with_unchanging_request([:data_public => false]) do |headers, changeset|
370           osm_xml = "<osm><node id='#{node.id}' changeset='#{changeset.id}' version='1' lat='0' lon='0'/></osm>"
371
372           delete api_node_path(node), :params => osm_xml.to_s, :headers => headers
373
374           assert_require_public_data
375         end
376       end
377     end
378
379     def test_destroy_missing_node_by_private_user
380       with_unchanging_request([:data_public => false]) do |headers|
381         delete api_node_path(0), :headers => headers
382
383         assert_require_public_data
384       end
385     end
386
387     def test_destroy_node_in_way_by_private_user
388       with_unchanging(:node) do |node|
389         create(:way_node, :node => node)
390
391         with_unchanging_request([:data_public => false]) do |headers, changeset|
392           osm_xml = xml_for_node node
393           osm_xml = update_changeset osm_xml, changeset.id
394
395           delete api_node_path(node), :params => osm_xml.to_s, :headers => headers
396
397           assert_require_public_data "shouldn't be able to delete a node used in a way (#{@response.body})"
398         end
399       end
400     end
401
402     def test_destroy_node_in_relation_by_private_user
403       with_unchanging(:node) do |node|
404         create(:relation_member, :member => node)
405
406         with_unchanging_request([:data_public => false]) do |headers, changeset|
407           osm_xml = xml_for_node node
408           osm_xml = update_changeset osm_xml, changeset.id
409
410           delete api_node_path(node), :params => osm_xml.to_s, :headers => headers
411
412           assert_require_public_data "shouldn't be able to delete a node used in a relation (#{@response.body})"
413         end
414       end
415     end
416
417     def test_destroy_in_closed_changeset
418       with_unchanging(:node) do |node|
419         with_unchanging_request([], [:closed]) do |headers, changeset|
420           osm_xml = xml_for_node node
421           osm_xml = update_changeset osm_xml, changeset.id
422
423           delete api_node_path(node), :params => osm_xml.to_s, :headers => headers
424
425           assert_response :conflict
426         end
427       end
428     end
429
430     def test_destroy_in_missing_changeset
431       with_unchanging(:node) do |node|
432         with_unchanging_request do |headers|
433           osm_xml = xml_for_node node
434           osm_xml = update_changeset osm_xml, 0
435
436           delete api_node_path(node), :params => osm_xml.to_s, :headers => headers
437
438           assert_response :conflict
439         end
440       end
441     end
442
443     def test_destroy_different_node
444       with_unchanging(:node) do |node|
445         with_unchanging(:node) do |other_node|
446           with_unchanging_request do |headers, changeset|
447             osm_xml = xml_for_node other_node
448             osm_xml = update_changeset osm_xml, changeset.id
449
450             delete api_node_path(node), :params => osm_xml.to_s, :headers => headers
451
452             assert_response :bad_request, "should not be able to delete a node with a different ID from the XML"
453           end
454         end
455       end
456     end
457
458     def test_destroy_invalid_osm_structure
459       with_unchanging(:node) do |node|
460         with_unchanging_request do |headers|
461           osm = "<delete/>"
462
463           delete api_node_path(node), :params => osm, :headers => headers
464
465           assert_response :bad_request, "should not be able to delete a node without a valid XML payload"
466         end
467       end
468     end
469
470     def test_destroy
471       with_request do |headers, changeset|
472         node = create(:node)
473         osm_xml = xml_for_node node
474         osm_xml = update_changeset osm_xml, changeset.id
475
476         delete api_node_path(node), :params => osm_xml.to_s, :headers => headers
477
478         assert_response :success
479
480         response_node_version = @response.body.to_i
481         assert_operator response_node_version, :>, node.version, "delete request should return a new version number for node"
482         node.reload
483         assert_not_predicate node, :visible?
484         assert_equal response_node_version, node.version
485
486         changeset.reload
487         assert_equal 1, changeset.num_changes
488         assert_predicate changeset, :num_type_changes_in_sync?
489         assert_equal 1, changeset.num_deleted_nodes
490       end
491     end
492
493     def test_destroy_twice
494       user = create(:user)
495       node = create(:node, :changeset => create(:changeset, :user => user))
496       osm_xml = xml_for_node node
497
498       delete api_node_path(node), :params => osm_xml.to_s, :headers => bearer_authorization_header(user)
499
500       assert_response :success
501
502       delete api_node_path(node), :params => osm_xml.to_s, :headers => bearer_authorization_header(user)
503
504       assert_response :gone
505     end
506
507     def test_destroy_deleted_node
508       with_unchanging(:node, :deleted) do |node|
509         with_unchanging_request do |headers, changeset|
510           osm = "<osm><node id='#{node.id}' changeset='#{changeset.id}' version='1' lat='0' lon='0'/></osm>"
511
512           delete api_node_path(node), :params => osm, :headers => headers
513
514           assert_response :gone
515         end
516       end
517     end
518
519     def test_destroy_missing_node
520       with_unchanging_request do |headers|
521         delete api_node_path(0), :headers => headers
522
523         assert_response :not_found
524       end
525     end
526
527     def test_destroy_node_in_ways
528       with_unchanging(:node) do |node|
529         way_node = create(:way_node, :node => node)
530         way_node2 = create(:way_node, :node => node)
531
532         with_unchanging_request do |headers, changeset|
533           osm_xml = xml_for_node node
534           osm_xml = update_changeset osm_xml, changeset.id
535
536           delete api_node_path(node), :params => osm_xml.to_s, :headers => headers
537
538           assert_response :precondition_failed, "shouldn't be able to delete a node used in a way (#{@response.body})"
539           assert_equal "Precondition failed: Node #{node.id} is still used by ways #{way_node.way.id},#{way_node2.way.id}.", @response.body
540         end
541       end
542     end
543
544     def test_destroy_node_in_relations
545       with_unchanging(:node) do |node|
546         relation_member = create(:relation_member, :member => node)
547         relation_member2 = create(:relation_member, :member => node)
548
549         with_unchanging_request do |headers, changeset|
550           osm_xml = xml_for_node node
551           osm_xml = update_changeset osm_xml, changeset.id
552
553           delete api_node_path(node), :params => osm_xml.to_s, :headers => headers
554
555           assert_response :precondition_failed, "shouldn't be able to delete a node used in a relation (#{@response.body})"
556           assert_equal "Precondition failed: Node #{node.id} is still used by relations #{relation_member.relation.id},#{relation_member2.relation.id}.", @response.body
557         end
558       end
559     end
560
561     def test_update_when_unauthorized
562       with_unchanging(:node) do |node|
563         osm_xml = xml_for_node node
564
565         put api_node_path(node), :params => osm_xml.to_s
566
567         assert_response :unauthorized
568       end
569     end
570
571     def test_update_in_changeset_of_other_user_by_private_user
572       with_unchanging(:node) do |node|
573         other_user = create(:user)
574
575         with_unchanging_request([:data_public => false], [:user => other_user]) do |headers, changeset|
576           osm_xml = xml_for_node node
577           osm_xml = update_changeset osm_xml, changeset.id
578
579           put api_node_path(node), :params => osm_xml.to_s, :headers => headers
580
581           assert_require_public_data "update with other user's changeset should be forbidden when data isn't public"
582         end
583       end
584     end
585
586     def test_update_in_closed_changeset_by_private_user
587       with_unchanging(:node) do |node|
588         with_unchanging_request([:data_public => false], [:closed]) do |headers, changeset|
589           osm_xml = xml_for_node node
590           osm_xml = update_changeset osm_xml, changeset.id
591
592           put api_node_path(node), :params => osm_xml.to_s, :headers => headers
593
594           assert_require_public_data "update with closed changeset should be forbidden, when data isn't public"
595         end
596       end
597     end
598
599     def test_update_in_missing_changeset_by_private_user
600       with_unchanging(:node) do |node|
601         with_unchanging_request([:data_public => false]) do |headers|
602           osm_xml = xml_for_node node
603           osm_xml = update_changeset osm_xml, 0
604
605           put api_node_path(node), :params => osm_xml.to_s, :headers => headers
606
607           assert_require_public_data "update with changeset=0 should be forbidden, when data isn't public"
608         end
609       end
610     end
611
612     def test_update_with_lat_too_large_by_private_user
613       check_update_with_invalid_attr_value "lat", 91.0, :data_public => false
614     end
615
616     def test_update_with_lat_too_small_by_private_user
617       check_update_with_invalid_attr_value "lat", -91.0, :data_public => false
618     end
619
620     def test_update_with_lon_too_large_by_private_user
621       check_update_with_invalid_attr_value "lon", 181.0, :data_public => false
622     end
623
624     def test_update_with_lon_too_small_by_private_user
625       check_update_with_invalid_attr_value "lon", -181.0, :data_public => false
626     end
627
628     def test_update_by_private_user
629       with_unchanging(:node) do |node|
630         with_unchanging_request([:data_public => false]) do |headers, changeset|
631           osm_xml = xml_for_node node
632           osm_xml = update_changeset osm_xml, changeset.id
633
634           put api_node_path(node), :params => osm_xml.to_s, :headers => headers
635
636           assert_require_public_data "should have failed with a forbidden when data isn't public"
637         end
638       end
639     end
640
641     def test_update_in_changeset_of_other_user
642       with_unchanging(:node) do |node|
643         other_user = create(:user)
644
645         with_unchanging_request([], [:user => other_user]) do |headers, changeset|
646           osm_xml = xml_for_node node
647           osm_xml = update_changeset osm_xml, changeset.id
648
649           put api_node_path(node), :params => osm_xml.to_s, :headers => headers
650
651           assert_response :conflict, "update with other user's changeset should be rejected"
652         end
653       end
654     end
655
656     def test_update_in_closed_changeset
657       with_unchanging(:node) do |node|
658         with_unchanging_request([], [:closed]) do |headers, changeset|
659           osm_xml = xml_for_node node
660           osm_xml = update_changeset osm_xml, changeset.id
661
662           put api_node_path(node), :params => osm_xml.to_s, :headers => headers
663
664           assert_response :conflict, "update with closed changeset should be rejected"
665         end
666       end
667     end
668
669     def test_update_in_missing_changeset
670       with_unchanging(:node) do |node|
671         with_unchanging_request do |headers|
672           osm_xml = xml_for_node node
673           osm_xml = update_changeset osm_xml, 0
674
675           put api_node_path(node), :params => osm_xml.to_s, :headers => headers
676
677           assert_response :conflict, "update with changeset=0 should be rejected"
678         end
679       end
680     end
681
682     def test_update_with_lat_too_large
683       check_update_with_invalid_attr_value "lat", 91.0
684     end
685
686     def test_update_with_lat_too_small
687       check_update_with_invalid_attr_value "lat", -91.0
688     end
689
690     def test_update_with_lon_too_large
691       check_update_with_invalid_attr_value "lon", 181.0
692     end
693
694     def test_update_with_lon_too_small
695       check_update_with_invalid_attr_value "lon", -181.0
696     end
697
698     def test_update_with_version_behind
699       with_unchanging(:node, :version => 2) do |node|
700         with_unchanging_request do |headers, changeset|
701           osm_xml = xml_for_node node
702           osm_xml = xml_attr_rewrite osm_xml, "version", node.version - 1
703           osm_xml = update_changeset osm_xml, changeset.id
704
705           put api_node_path(node), :params => osm_xml.to_s, :headers => headers
706
707           assert_response :conflict, "should have failed on old version number"
708         end
709       end
710     end
711
712     def test_update_with_version_ahead
713       with_unchanging(:node, :version => 2) do |node|
714         with_unchanging_request do |headers, changeset|
715           osm_xml = xml_for_node node
716           osm_xml = xml_attr_rewrite osm_xml, "version", node.version + 1
717           osm_xml = update_changeset osm_xml, changeset.id
718
719           put api_node_path(node), :params => osm_xml.to_s, :headers => headers
720
721           assert_response :conflict, "should have failed on skipped version number"
722         end
723       end
724     end
725
726     def test_update_with_invalid_version
727       with_unchanging(:node) do |node|
728         with_unchanging_request do |headers, changeset|
729           osm_xml = xml_for_node node
730           osm_xml = xml_attr_rewrite osm_xml, "version", "p1r4t3s!"
731           osm_xml = update_changeset osm_xml, changeset.id
732
733           put api_node_path(node), :params => osm_xml.to_s, :headers => headers
734
735           assert_response :conflict, "should not be able to put 'p1r4at3s!' in the version field"
736         end
737       end
738     end
739
740     def test_update_other_node
741       with_unchanging(:node) do |node|
742         with_unchanging(:node) do |other_node|
743           with_unchanging_request do |headers, changeset|
744             osm_xml = xml_for_node other_node
745             osm_xml = update_changeset osm_xml, changeset.id
746
747             put api_node_path(node), :params => osm_xml.to_s, :headers => headers
748
749             assert_response :bad_request, "should not be able to update a node with a different ID from the XML"
750           end
751         end
752       end
753     end
754
755     def test_update_with_invalid_osm_structure
756       with_unchanging(:node) do |node|
757         with_unchanging_request do |headers|
758           osm = "<update/>"
759
760           put api_node_path(node), :params => osm, :headers => headers
761
762           assert_response :bad_request, "should not be able to update a node with non-OSM XML doc."
763         end
764       end
765     end
766
767     def test_update
768       with_request do |headers, changeset|
769         node = create(:node)
770         osm_xml = xml_for_node node
771         osm_xml = update_changeset osm_xml, changeset.id
772
773         put api_node_path(node), :params => osm_xml.to_s, :headers => headers
774
775         assert_response :success, "a valid update request failed"
776
777         changeset.reload
778         assert_equal 1, changeset.num_changes
779         assert_predicate changeset, :num_type_changes_in_sync?
780         assert_equal 1, changeset.num_modified_nodes
781       end
782     end
783
784     def test_update_with_duplicate_tags
785       with_unchanging(:node) do |node|
786         create(:node_tag, :node => node, :k => "key_to_duplicate", :v => "value_to_duplicate")
787
788         with_unchanging_request do |headers, changeset|
789           tag_xml = XML::Node.new("tag")
790           tag_xml["k"] = "key_to_duplicate"
791           tag_xml["v"] = "value_to_duplicate"
792
793           osm_xml = xml_for_node node
794           osm_xml.find("//osm/node").first << tag_xml
795           osm_xml = update_changeset osm_xml, changeset.id
796
797           put api_node_path(node), :params => osm_xml.to_s, :headers => headers
798
799           assert_response :bad_request, "adding duplicate tags to a node should fail with 'bad request'"
800           assert_equal "Element node/#{node.id} has duplicate tags with key key_to_duplicate", @response.body
801         end
802       end
803     end
804
805     ##
806     # test initial rate limit
807     def test_initial_rate_limit
808       # create a user
809       user = create(:user)
810
811       # create a changeset that puts us near the initial rate limit
812       changeset = create(:changeset, :user => user,
813                                      :created_at => Time.now.utc - 5.minutes,
814                                      :num_changes => Settings.initial_changes_per_hour - 1)
815
816       # create authentication header
817       auth_header = bearer_authorization_header user
818
819       # try creating a node
820       xml = "<osm><node lat='0' lon='0' changeset='#{changeset.id}'/></osm>"
821       post api_nodes_path, :params => xml, :headers => auth_header
822       assert_response :success, "node create did not return success status"
823
824       # get the id of the node we created
825       nodeid = @response.body
826
827       # try updating the node, which should be rate limited
828       xml = "<osm><node id='#{nodeid}' version='1' lat='1' lon='1' changeset='#{changeset.id}'/></osm>"
829       put api_node_path(nodeid), :params => xml, :headers => auth_header
830       assert_response :too_many_requests, "node update did not hit rate limit"
831
832       # try deleting the node, which should be rate limited
833       xml = "<osm><node id='#{nodeid}' version='2' lat='1' lon='1' changeset='#{changeset.id}'/></osm>"
834       delete api_node_path(nodeid), :params => xml, :headers => auth_header
835       assert_response :too_many_requests, "node delete did not hit rate limit"
836
837       # try creating a node, which should be rate limited
838       xml = "<osm><node lat='0' lon='0' changeset='#{changeset.id}'/></osm>"
839       post api_nodes_path, :params => xml, :headers => auth_header
840       assert_response :too_many_requests, "node create did not hit rate limit"
841     end
842
843     ##
844     # test maximum rate limit
845     def test_maximum_rate_limit
846       # create a user
847       user = create(:user)
848
849       # create a changeset to establish our initial edit time
850       changeset = create(:changeset, :user => user,
851                                      :created_at => Time.now.utc - 28.days)
852
853       # create changeset to put us near the maximum rate limit
854       total_changes = Settings.max_changes_per_hour - 1
855       while total_changes.positive?
856         changes = [total_changes, Changeset::MAX_ELEMENTS].min
857         changeset = create(:changeset, :user => user,
858                                        :created_at => Time.now.utc - 5.minutes,
859                                        :num_changes => changes)
860         total_changes -= changes
861       end
862
863       # create authentication header
864       auth_header = bearer_authorization_header user
865
866       # try creating a node
867       xml = "<osm><node lat='0' lon='0' changeset='#{changeset.id}'/></osm>"
868       post api_nodes_path, :params => xml, :headers => auth_header
869       assert_response :success, "node create did not return success status"
870
871       # get the id of the node we created
872       nodeid = @response.body
873
874       # try updating the node, which should be rate limited
875       xml = "<osm><node id='#{nodeid}' version='1' lat='1' lon='1' changeset='#{changeset.id}'/></osm>"
876       put api_node_path(nodeid), :params => xml, :headers => auth_header
877       assert_response :too_many_requests, "node update did not hit rate limit"
878
879       # try deleting the node, which should be rate limited
880       xml = "<osm><node id='#{nodeid}' version='2' lat='1' lon='1' changeset='#{changeset.id}'/></osm>"
881       delete api_node_path(nodeid), :params => xml, :headers => auth_header
882       assert_response :too_many_requests, "node delete did not hit rate limit"
883
884       # try creating a node, which should be rate limited
885       xml = "<osm><node lat='0' lon='0' changeset='#{changeset.id}'/></osm>"
886       post api_nodes_path, :params => xml, :headers => auth_header
887       assert_response :too_many_requests, "node create did not hit rate limit"
888     end
889
890     private
891
892     def check_update_with_invalid_attr_value(name, value, data_public: true)
893       with_unchanging(:node) do |node|
894         with_unchanging_request([:data_public => data_public]) do |headers, changeset|
895           osm_xml = xml_for_node node
896           osm_xml = xml_attr_rewrite osm_xml, name, value
897           osm_xml = update_changeset osm_xml, changeset.id
898
899           put api_node_path(node), :params => osm_xml.to_s, :headers => headers
900
901           if data_public
902             assert_response :bad_request, "node at #{name}=#{value} should be rejected"
903           else
904             assert_require_public_data "node at #{name}=#{value} should be forbidden, when data isn't public"
905           end
906         end
907       end
908     end
909
910     def affected_models
911       [Node, NodeTag,
912        OldNode, OldNodeTag]
913     end
914
915     ##
916     # update an attribute in the node element
917     def xml_attr_rewrite(xml, name, value)
918       xml.find("//osm/node").first[name] = value.to_s
919       xml
920     end
921   end
922 end