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