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