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