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